diff --git a/packages/api/src/controllers/connections.js b/packages/api/src/controllers/connections.js index 7ca5c67bb..58504e87d 100644 --- a/packages/api/src/controllers/connections.js +++ b/packages/api/src/controllers/connections.js @@ -283,6 +283,14 @@ module.exports = { return res; }, + batchChangeFolder_meta: true, + async batchChangeFolder({ folder, newFolder }, req) { + // const updated = await this.datastore.find(x => x.parent == folder); + const res = await this.datastore.updateAll(x => (x.parent == folder ? { ...x, parent: newFolder } : x)); + socket.emitChanged('connection-list-changed'); + return res; + }, + updateDatabase_meta: true, async updateDatabase({ conid, database, values }, req) { if (portalConnections) return; diff --git a/packages/api/src/utility/JsonLinesDatabase.js b/packages/api/src/utility/JsonLinesDatabase.js index 9f4243bbd..12de5cef3 100644 --- a/packages/api/src/utility/JsonLinesDatabase.js +++ b/packages/api/src/utility/JsonLinesDatabase.js @@ -90,6 +90,12 @@ class JsonLinesDatabase { return obj; } + async updateAll(mapFunction) { + await this._ensureLoaded(); + this.data = this.data.map(mapFunction); + await this._save(); + } + async patch(id, values) { await this._ensureLoaded(); this.data = this.data.map(x => (x._id == id ? { ...x, ...values } : x)); diff --git a/packages/web/src/appobj/AppObjectGroup.svelte b/packages/web/src/appobj/AppObjectGroup.svelte index bf52001ca..82ddc457d 100644 --- a/packages/web/src/appobj/AppObjectGroup.svelte +++ b/packages/web/src/appobj/AppObjectGroup.svelte @@ -4,6 +4,7 @@ import { plusExpandIcon } from '../icons/expandIcons'; import FontIcon from '../icons/FontIcon.svelte'; + import contextMenu from '../utility/contextMenu'; import AppObjectListItem from './AppObjectListItem.svelte'; @@ -16,6 +17,7 @@ export let disableContextMenu = false; export let passProps; export let onDropOnGroup = undefined; + export let groupContextMenu = null; let isExpanded = true; @@ -45,7 +47,12 @@ } -
(isExpanded = !isExpanded)} on:drop={handleDrop}> +
(isExpanded = !isExpanded)} + on:drop={handleDrop} + use:contextMenu={() => groupContextMenu(group)} +> diff --git a/packages/web/src/appobj/AppObjectList.svelte b/packages/web/src/appobj/AppObjectList.svelte index d0b8d4cc6..675f3ca1a 100644 --- a/packages/web/src/appobj/AppObjectList.svelte +++ b/packages/web/src/appobj/AppObjectList.svelte @@ -19,6 +19,7 @@ export let getIsExpanded = null; export let setIsExpanded = null; export let sortGroups = false; + export let groupContextMenu = null; export let groupIconFunc = plusExpandIcon; export let groupFunc = undefined; @@ -98,6 +99,7 @@ {getIsExpanded} {setIsExpanded} {onDropOnGroup} + {groupContextMenu} /> {/each} {:else} diff --git a/packages/web/src/widgets/ConnectionList.svelte b/packages/web/src/widgets/ConnectionList.svelte index 001f89a51..c7be3892e 100644 --- a/packages/web/src/widgets/ConnectionList.svelte +++ b/packages/web/src/widgets/ConnectionList.svelte @@ -25,6 +25,9 @@ import LargeButton from '../buttons/LargeButton.svelte'; import { plusExpandIcon, chevronExpandIcon } from '../icons/expandIcons'; import { safeJsonParse } from 'dbgate-tools'; + import { showModal } from '../modals/modalTools'; + import InputTextModal from '../modals/InputTextModal.svelte'; + import ConfirmModal from '../modals/ConfirmModal.svelte'; const connections = useConnectionList(); const serverStatus = useServerStatus(); @@ -56,9 +59,9 @@ const handleDropOnGroup = (data, group) => { const json = safeJsonParse(data); if (json?._id) { - if (json.parent) { - emptyConnectionGroupNames.update(x => x.filter(y => y != json.parent)); - } + // if (json.parent) { + // emptyConnectionGroupNames.update(x => x.filter(y => y != json.parent)); + // } apiCall('connections/update', { _id: json?._id, values: { parent: group }, @@ -67,6 +70,41 @@ }; const connectionColorFactory = useConnectionColorFactory(3); + + function createGroupContextMenu(folder) { + const handleRename = () => { + showModal(InputTextModal, { + value: folder, + label: 'New folder name', + header: 'Rename folder', + onConfirm: async newFolder => { + emptyConnectionGroupNames.update(folders => _.uniq(folders.map(fld => (fld == folder ? newFolder : fld)))); + apiCall('connections/batch-change-folder', { + folder, + newFolder, + }); + }, + }); + }; + + const handleDelete = () => { + showModal(ConfirmModal, { + message: `Really delete folder ${folder}? Connections in folder will be moved into root folder.`, + onConfirm: () => { + emptyConnectionGroupNames.update(folders => folders.filter(fld => fld != folder)); + apiCall('connections/batch-change-folder', { + folder, + newFolder: '', + }); + }, + }); + }; + + return [ + { text: 'Rename', onClick: handleRename }, + { text: 'Delete', onClick: handleDelete }, + ]; + } @@ -110,6 +148,7 @@ onDropOnGroup={handleDropOnGroup} emptyGroupNames={$emptyConnectionGroupNames} sortGroups + groupContextMenu={createGroupContextMenu} /> {#if (connectionsWithParent?.length > 0 && connectionsWithoutParent?.length > 0) || ($emptyConnectionGroupNames.length > 0 && connectionsWithoutParent?.length > 0)}