From 23fdeea2512afd92b9757c27088da3c30f1acd97 Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Wed, 17 Sep 2025 11:59:48 -0400 Subject: [PATCH 001/127] table > select & status badge --- .../resolvers/docker/docker-form.service.ts | 170 ++++++++ pnpm-lock.yaml | 3 + web/components.d.ts | 5 + web/package.json | 1 + web/public/test-pages/all-components.html | 382 ++++++++++++++++++ .../DockerContainerOverview.standalone.vue | 28 ++ .../Docker/DockerContainerOverview.vue | 66 +++ .../Docker/DockerContainersTable.vue | 271 +++++++++++++ .../Docker/docker-containers.query.ts | 144 +++++++ .../Docker/docker-create-folder.mutation.ts | 34 ++ .../Docker/docker-overview.query.ts | 12 + .../components/Wrapper/component-registry.ts | 5 + web/src/composables/gql/gql.ts | 18 + web/src/composables/gql/graphql.ts | 69 ++++ 14 files changed, 1208 insertions(+) create mode 100644 api/src/unraid-api/graph/resolvers/docker/docker-form.service.ts create mode 100644 web/public/test-pages/all-components.html create mode 100644 web/src/components/Docker/DockerContainerOverview.standalone.vue create mode 100644 web/src/components/Docker/DockerContainerOverview.vue create mode 100644 web/src/components/Docker/DockerContainersTable.vue create mode 100644 web/src/components/Docker/docker-containers.query.ts create mode 100644 web/src/components/Docker/docker-create-folder.mutation.ts create mode 100644 web/src/components/Docker/docker-overview.query.ts diff --git a/api/src/unraid-api/graph/resolvers/docker/docker-form.service.ts b/api/src/unraid-api/graph/resolvers/docker/docker-form.service.ts new file mode 100644 index 0000000000..7972449f3d --- /dev/null +++ b/api/src/unraid-api/graph/resolvers/docker/docker-form.service.ts @@ -0,0 +1,170 @@ +import { Injectable } from '@nestjs/common'; + +import { type UISchemaElement } from '@jsonforms/core'; + +import { DockerContainerOverviewForm } from '@app/unraid-api/graph/resolvers/docker/docker.model.js'; +import { DockerService } from '@app/unraid-api/graph/resolvers/docker/docker.service.js'; +import { DataSlice } from '@app/unraid-api/types/json-forms.js'; + +@Injectable() +export class DockerFormService { + constructor(private readonly dockerService: DockerService) {} + + async getContainerOverviewForm(skipCache = false): Promise { + const containers = await this.dockerService.getContainers({ skipCache }); + + // Transform containers data for table display + const tableData = containers.map((container) => ({ + id: container.id, + name: container.names[0]?.replace(/^\//, '') || 'Unknown', + state: container.state, + status: container.status, + image: container.image, + ports: container.ports + .map((p) => { + if (p.publicPort && p.privatePort) { + return `${p.publicPort}:${p.privatePort}/${p.type}`; + } else if (p.privatePort) { + return `${p.privatePort}/${p.type}`; + } + return ''; + }) + .filter(Boolean) + .join(', '), + autoStart: container.autoStart, + network: container.hostConfig?.networkMode || 'default', + })); + + const dataSchema = this.createDataSchema(); + const uiSchema = this.createUiSchema(); + + return { + id: 'docker-container-overview', + dataSchema: { + type: 'object', + properties: dataSchema, + }, + uiSchema, + data: { + containers: tableData, + }, + }; + } + + private createDataSchema(): DataSlice { + return { + containers: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + title: 'ID', + }, + name: { + type: 'string', + title: 'Name', + }, + state: { + type: 'string', + title: 'State', + enum: ['RUNNING', 'EXITED'], + }, + status: { + type: 'string', + title: 'Status', + }, + image: { + type: 'string', + title: 'Image', + }, + ports: { + type: 'string', + title: 'Ports', + }, + autoStart: { + type: 'boolean', + title: 'Auto Start', + }, + network: { + type: 'string', + title: 'Network', + }, + }, + }, + }, + }; + } + + private createUiSchema(): UISchemaElement { + return { + type: 'VerticalLayout', + elements: [ + { + type: 'Control', + scope: '#/properties/containers', + options: { + detail: { + type: 'HorizontalLayout', + elements: [ + { + type: 'Control', + scope: '#/properties/name', + options: { + trim: true, + readonly: true, + }, + }, + { + type: 'Control', + scope: '#/properties/state', + options: { + readonly: true, + format: 'badge', + }, + }, + { + type: 'Control', + scope: '#/properties/status', + options: { + readonly: true, + }, + }, + { + type: 'Control', + scope: '#/properties/image', + options: { + readonly: true, + trim: true, + }, + }, + { + type: 'Control', + scope: '#/properties/ports', + options: { + readonly: true, + }, + }, + { + type: 'Control', + scope: '#/properties/autoStart', + options: { + readonly: true, + }, + }, + { + type: 'Control', + scope: '#/properties/network', + options: { + readonly: true, + }, + }, + ], + }, + }, + }, + ], + }; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8db98eb3a4..a1dcb021b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1085,6 +1085,9 @@ importers: '@nuxt/ui': specifier: 4.0.0-alpha.0 version: 4.0.0-alpha.0(@babel/parser@7.28.4)(@netlify/blobs@9.1.2)(change-case@5.4.4)(db0@0.3.2)(embla-carousel@8.6.0)(focus-trap@7.6.5)(ioredis@5.7.0)(jwt-decode@4.0.0)(magicast@0.3.5)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.2)(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(stylus@0.57.0)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))(vue-router@4.5.1(vue@3.5.20(typescript@5.9.2)))(vue@3.5.20(typescript@5.9.2))(zod@3.25.76) + '@tanstack/vue-table': + specifier: ^8.21.3 + version: 8.21.3(vue@3.5.20(typescript@5.9.2)) '@unraid/shared-callbacks': specifier: 1.1.1 version: 1.1.1(@vueuse/core@13.8.0(vue@3.5.20(typescript@5.9.2))) diff --git a/web/components.d.ts b/web/components.d.ts index 0a64dce38a..8419775c03 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -45,6 +45,9 @@ declare module 'vue' { 'DevModalTest.standalone': typeof import('./src/components/DevModalTest.standalone.vue')['default'] DevSettings: typeof import('./src/components/DevSettings.vue')['default'] 'DevThemeSwitcher.standalone': typeof import('./src/components/DevThemeSwitcher.standalone.vue')['default'] + DockerContainerOverview: typeof import('./src/components/Docker/DockerContainerOverview.vue')['default'] + 'DockerContainerOverview.standalone': typeof import('./src/components/Docker/DockerContainerOverview.standalone.vue')['default'] + DockerContainersTable: typeof import('./src/components/Docker/DockerContainersTable.vue')['default'] Downgrade: typeof import('./src/components/UpdateOs/Downgrade.vue')['default'] 'DowngradeOs.standalone': typeof import('./src/components/DowngradeOs.standalone.vue')['default'] 'DownloadApiLogs.standalone': typeof import('./src/components/DownloadApiLogs.standalone.vue')['default'] @@ -102,6 +105,7 @@ declare module 'vue' { SsoButtons: typeof import('./src/components/sso/SsoButtons.vue')['default'] SsoProviderButton: typeof import('./src/components/sso/SsoProviderButton.vue')['default'] Status: typeof import('./src/components/UpdateOs/Status.vue')['default'] + TableRenderer: typeof import('./src/components/JsonForms/TableRenderer.vue')['default'] 'TestThemeSwitcher.standalone': typeof import('./src/components/TestThemeSwitcher.standalone.vue')['default'] 'TestUpdateModal.standalone': typeof import('./src/components/UpdateOs/TestUpdateModal.standalone.vue')['default'] 'ThemeSwitcher.standalone': typeof import('./src/components/ThemeSwitcher.standalone.vue')['default'] @@ -127,6 +131,7 @@ declare module 'vue' { USelectMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue')['default'] 'UserProfile.standalone': typeof import('./src/components/UserProfile.standalone.vue')['default'] USwitch: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Switch.vue')['default'] + UTable: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Table.vue')['default'] UTabs: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue')['default'] 'WanIpCheck.standalone': typeof import('./src/components/WanIpCheck.standalone.vue')['default'] 'WelcomeModal.standalone': typeof import('./src/components/Activation/WelcomeModal.standalone.vue')['default'] diff --git a/web/package.json b/web/package.json index ff7ef0b30e..92e8637fc7 100644 --- a/web/package.json +++ b/web/package.json @@ -109,6 +109,7 @@ "@jsonforms/vue-vanilla": "3.6.0", "@jsonforms/vue-vuetify": "3.6.0", "@nuxt/ui": "4.0.0-alpha.0", + "@tanstack/vue-table": "^8.21.3", "@unraid/shared-callbacks": "1.1.1", "@unraid/ui": "link:../unraid-ui", "@vue/apollo-composable": "4.2.2", diff --git a/web/public/test-pages/all-components.html b/web/public/test-pages/all-components.html new file mode 100644 index 0000000000..7f1da70c88 --- /dev/null +++ b/web/public/test-pages/all-components.html @@ -0,0 +1,382 @@ + + + + + + All Components - Unraid Component Test + + + + +
+ +
🐳 Docker
+
+
+

Docker Container Overview

+ <unraid-docker-container-overview> +
+ +
+
+
+ + +
👤 Authentication & User
+
+
+

Authentication

+ <unraid-auth> +
+ +
+
+ +
+

User Profile

+ <unraid-user-profile> +
+ +
+
+ +
+

SSO Button

+ <unraid-sso-button> +
+ +
+
+ +
+

Registration

+ <unraid-registration> +
+ +
+
+
+ + +
⚙️ System & Settings
+
+
+

Connect Settings

+ <unraid-connect-settings> +
+ +
+
+ +
+

Theme Switcher

+ <unraid-theme-switcher> +
+ +
+
+ +
+

Header OS Version

+ <unraid-header-os-version> +
+ +
+
+ +
+

WAN IP Check

+ <unraid-wan-ip-check> +
+ +
+
+
+ + +
💿 OS Management
+
+
+

Update OS

+ <unraid-update-os> +
+ +
+
+ +
+

Downgrade OS

+ <unraid-downgrade-os> +
+ +
+
+
+ + +
🔧 API & Developer
+
+
+

API Key Manager

+ <unraid-api-key-manager> +
+ +
+
+ +
+

API Key Authorize

+ <unraid-api-key-authorize> +
+ +
+
+ +
+

Download API Logs

+ <unraid-download-api-logs> +
+ +
+
+ +
+

Log Viewer

+ <unraid-log-viewer> +
+ +
+
+
+ + +
🎨 UI Components
+
+
+

Modals

+ <unraid-modals> +
+ +
+
+ +
+

Welcome Modal

+ <unraid-welcome-modal> +
+ +
+
+ +
+

Dev Modal Test

+ <unraid-dev-modal-test> +
+ +
+
+ +
+

Toaster

+ <unraid-toaster> +
+ +
+
+
+ + +
🎮 Test Controls
+
+

Language Selection

+
+ +
+ +

jQuery Interaction Tests

+
+ + + + + +
+ +
+

Console Output

+
+ > Ready for testing... +
+
+
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/components/Docker/DockerContainerOverview.standalone.vue b/web/src/components/Docker/DockerContainerOverview.standalone.vue new file mode 100644 index 0000000000..af60068deb --- /dev/null +++ b/web/src/components/Docker/DockerContainerOverview.standalone.vue @@ -0,0 +1,28 @@ + + diff --git a/web/src/components/Docker/DockerContainerOverview.vue b/web/src/components/Docker/DockerContainerOverview.vue new file mode 100644 index 0000000000..045ec91000 --- /dev/null +++ b/web/src/components/Docker/DockerContainerOverview.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/web/src/components/Docker/DockerContainersTable.vue b/web/src/components/Docker/DockerContainersTable.vue new file mode 100644 index 0000000000..9a6d7471ec --- /dev/null +++ b/web/src/components/Docker/DockerContainersTable.vue @@ -0,0 +1,271 @@ + + + diff --git a/web/src/components/Docker/docker-containers.query.ts b/web/src/components/Docker/docker-containers.query.ts new file mode 100644 index 0000000000..8f84ebb7e3 --- /dev/null +++ b/web/src/components/Docker/docker-containers.query.ts @@ -0,0 +1,144 @@ +import { gql } from '@apollo/client'; + +export const GET_DOCKER_CONTAINERS = gql` + query GetDockerContainers($skipCache: Boolean = false) { + docker { + id + organizer(skipCache: $skipCache) { + version + views { + id + name + root { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + children { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + children { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + children { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + } + ... on OrganizerContainerResource { + id + name + type + meta { + id + names + state + status + image + ports { + privatePort + publicPort + type + } + autoStart + hostConfig { + networkMode + } + created + isUpdateAvailable + isRebuildReady + } + } + } + } + ... on OrganizerContainerResource { + id + name + type + meta { + id + names + state + status + image + ports { + privatePort + publicPort + type + } + autoStart + hostConfig { + networkMode + } + created + isUpdateAvailable + isRebuildReady + } + } + } + } + ... on OrganizerContainerResource { + id + name + type + meta { + id + names + state + status + image + ports { + privatePort + publicPort + type + } + autoStart + hostConfig { + networkMode + } + created + isUpdateAvailable + isRebuildReady + } + } + } + } + ... on OrganizerContainerResource { + id + name + type + meta { + id + names + state + status + image + ports { + privatePort + publicPort + type + } + autoStart + hostConfig { + networkMode + } + created + isUpdateAvailable + isRebuildReady + } + } + } + } + } + } + } +`; diff --git a/web/src/components/Docker/docker-create-folder.mutation.ts b/web/src/components/Docker/docker-create-folder.mutation.ts new file mode 100644 index 0000000000..60b18db9f9 --- /dev/null +++ b/web/src/components/Docker/docker-create-folder.mutation.ts @@ -0,0 +1,34 @@ +import { gql } from '@apollo/client'; + +export const CREATE_DOCKER_FOLDER = gql` + mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) { + createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) { + version + views { + id + name + root { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + children { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + } + ... on OrganizerContainerResource { + id + name + type + } + } + } + } + } + } + } +`; diff --git a/web/src/components/Docker/docker-overview.query.ts b/web/src/components/Docker/docker-overview.query.ts new file mode 100644 index 0000000000..2a26c54a34 --- /dev/null +++ b/web/src/components/Docker/docker-overview.query.ts @@ -0,0 +1,12 @@ +import { gql } from '@apollo/client'; + +export const GET_DOCKER_CONTAINER_OVERVIEW_FORM = gql` + query GetDockerContainerOverviewForm($skipCache: Boolean = false) { + dockerContainerOverviewForm(skipCache: $skipCache) { + id + dataSchema + uiSchema + data + } + } +`; diff --git a/web/src/components/Wrapper/component-registry.ts b/web/src/components/Wrapper/component-registry.ts index 736ffd2144..15cf81cf8d 100644 --- a/web/src/components/Wrapper/component-registry.ts +++ b/web/src/components/Wrapper/component-registry.ts @@ -156,4 +156,9 @@ export const componentMappings: ComponentMapping[] = [ selector: 'unraid-api-status-manager', appId: 'api-status-manager', }, + { + component: defineAsyncComponent(() => import('../Docker/DockerContainerOverview.standalone.vue')), + selector: 'unraid-docker-container-overview', + appId: 'docker-container-overview', + }, ]; diff --git a/web/src/composables/gql/gql.ts b/web/src/composables/gql/gql.ts index f5de35cb1d..2e575ec55f 100644 --- a/web/src/composables/gql/gql.ts +++ b/web/src/composables/gql/gql.ts @@ -28,6 +28,9 @@ type Documents = { "\n query GetPermissionsForRoles($roles: [Role!]!) {\n getPermissionsForRoles(roles: $roles) {\n resource\n actions\n }\n }\n": typeof types.GetPermissionsForRolesDocument, "\n query Unified {\n settings {\n unified {\n id\n dataSchema\n uiSchema\n values\n }\n }\n }\n": typeof types.UnifiedDocument, "\n mutation UpdateConnectSettings($input: JSON!) {\n updateSettings(input: $input) {\n restartRequired\n values\n }\n }\n": typeof types.UpdateConnectSettingsDocument, + "\n query GetDockerContainers($skipCache: Boolean = false) {\n docker {\n id\n organizer(skipCache: $skipCache) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n }\n }\n }\n": typeof types.GetDockerContainersDocument, + "\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.CreateDockerFolderDocument, + "\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n": typeof types.GetDockerContainerOverviewFormDocument, "\n query LogFiles {\n logFiles {\n name\n path\n size\n modifiedAt\n }\n }\n": typeof types.LogFilesDocument, "\n query LogFileContent($path: String!, $lines: Int, $startLine: Int) {\n logFile(path: $path, lines: $lines, startLine: $startLine) {\n path\n content\n totalLines\n startLine\n }\n }\n": typeof types.LogFileContentDocument, "\n subscription LogFileSubscription($path: String!) {\n logFile(path: $path) {\n path\n content\n totalLines\n }\n }\n": typeof types.LogFileSubscriptionDocument, @@ -73,6 +76,9 @@ const documents: Documents = { "\n query GetPermissionsForRoles($roles: [Role!]!) {\n getPermissionsForRoles(roles: $roles) {\n resource\n actions\n }\n }\n": types.GetPermissionsForRolesDocument, "\n query Unified {\n settings {\n unified {\n id\n dataSchema\n uiSchema\n values\n }\n }\n }\n": types.UnifiedDocument, "\n mutation UpdateConnectSettings($input: JSON!) {\n updateSettings(input: $input) {\n restartRequired\n values\n }\n }\n": types.UpdateConnectSettingsDocument, + "\n query GetDockerContainers($skipCache: Boolean = false) {\n docker {\n id\n organizer(skipCache: $skipCache) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n }\n }\n }\n": types.GetDockerContainersDocument, + "\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.CreateDockerFolderDocument, + "\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n": types.GetDockerContainerOverviewFormDocument, "\n query LogFiles {\n logFiles {\n name\n path\n size\n modifiedAt\n }\n }\n": types.LogFilesDocument, "\n query LogFileContent($path: String!, $lines: Int, $startLine: Int) {\n logFile(path: $path, lines: $lines, startLine: $startLine) {\n path\n content\n totalLines\n startLine\n }\n }\n": types.LogFileContentDocument, "\n subscription LogFileSubscription($path: String!) {\n logFile(path: $path) {\n path\n content\n totalLines\n }\n }\n": types.LogFileSubscriptionDocument, @@ -174,6 +180,18 @@ export function graphql(source: "\n query Unified {\n settings {\n unif * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "\n mutation UpdateConnectSettings($input: JSON!) {\n updateSettings(input: $input) {\n restartRequired\n values\n }\n }\n"): (typeof documents)["\n mutation UpdateConnectSettings($input: JSON!) {\n updateSettings(input: $input) {\n restartRequired\n values\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query GetDockerContainers($skipCache: Boolean = false) {\n docker {\n id\n organizer(skipCache: $skipCache) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query GetDockerContainers($skipCache: Boolean = false) {\n docker {\n id\n organizer(skipCache: $skipCache) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n"): (typeof documents)["\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/web/src/composables/gql/graphql.ts b/web/src/composables/gql/graphql.ts index e683aa0c02..4987cc2a69 100644 --- a/web/src/composables/gql/graphql.ts +++ b/web/src/composables/gql/graphql.ts @@ -695,6 +695,11 @@ export type DockerNetworksArgs = { skipCache?: Scalars['Boolean']['input']; }; + +export type DockerOrganizerArgs = { + skipCache?: Scalars['Boolean']['input']; +}; + export type DockerContainer = Node & { __typename?: 'DockerContainer'; autoStart: Scalars['Boolean']['output']; @@ -717,6 +722,14 @@ export type DockerContainer = Node & { status: Scalars['String']['output']; }; +export type DockerContainerOverviewForm = { + __typename?: 'DockerContainerOverviewForm'; + data: Scalars['JSON']['output']; + dataSchema: Scalars['JSON']['output']; + id: Scalars['ID']['output']; + uiSchema: Scalars['JSON']['output']; +}; + export type DockerMutations = { __typename?: 'DockerMutations'; /** Start a container */ @@ -1663,6 +1676,7 @@ export type Query = { disk: Disk; disks: Array; docker: Docker; + dockerContainerOverviewForm: DockerContainerOverviewForm; flash: Flash; /** Get JSON Schema for API key creation form */ getApiKeyCreationFormSchema: ApiKeyFormSettings; @@ -1726,6 +1740,11 @@ export type QueryDiskArgs = { }; +export type QueryDockerContainerOverviewFormArgs = { + skipCache?: Scalars['Boolean']['input']; +}; + + export type QueryGetPermissionsForRolesArgs = { roles: Array; }; @@ -2662,6 +2681,53 @@ export type UpdateConnectSettingsMutationVariables = Exact<{ export type UpdateConnectSettingsMutation = { __typename?: 'Mutation', updateSettings: { __typename?: 'UpdateSettingsResponse', restartRequired: boolean, values: any } }; +export type GetDockerContainersQueryVariables = Exact<{ + skipCache?: InputMaybe; +}>; + + +export type GetDockerContainersQuery = { __typename?: 'Query', docker: { __typename?: 'Docker', id: string, organizer: { __typename?: 'ResolvedOrganizerV1', version: number, views: Array<{ __typename?: 'ResolvedOrganizerView', id: string, name: string, root: + | { __typename: 'OrganizerContainerResource', id: string, name: string, type: string, meta?: { __typename?: 'DockerContainer', id: string, names: Array, state: ContainerState, status: string, image: string, autoStart: boolean, created: number, isUpdateAvailable?: boolean | null, isRebuildReady?: boolean | null, ports: Array<{ __typename?: 'ContainerPort', privatePort?: number | null, publicPort?: number | null, type: ContainerPortType }>, hostConfig?: { __typename?: 'ContainerHostConfig', networkMode: string } | null } | null } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string, children: Array< + | { __typename: 'OrganizerContainerResource', id: string, name: string, type: string, meta?: { __typename?: 'DockerContainer', id: string, names: Array, state: ContainerState, status: string, image: string, autoStart: boolean, created: number, isUpdateAvailable?: boolean | null, isRebuildReady?: boolean | null, ports: Array<{ __typename?: 'ContainerPort', privatePort?: number | null, publicPort?: number | null, type: ContainerPortType }>, hostConfig?: { __typename?: 'ContainerHostConfig', networkMode: string } | null } | null } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string, children: Array< + | { __typename: 'OrganizerContainerResource', id: string, name: string, type: string, meta?: { __typename?: 'DockerContainer', id: string, names: Array, state: ContainerState, status: string, image: string, autoStart: boolean, created: number, isUpdateAvailable?: boolean | null, isRebuildReady?: boolean | null, ports: Array<{ __typename?: 'ContainerPort', privatePort?: number | null, publicPort?: number | null, type: ContainerPortType }>, hostConfig?: { __typename?: 'ContainerHostConfig', networkMode: string } | null } | null } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string, children: Array< + | { __typename: 'OrganizerContainerResource', id: string, name: string, type: string, meta?: { __typename?: 'DockerContainer', id: string, names: Array, state: ContainerState, status: string, image: string, autoStart: boolean, created: number, isUpdateAvailable?: boolean | null, isRebuildReady?: boolean | null, ports: Array<{ __typename?: 'ContainerPort', privatePort?: number | null, publicPort?: number | null, type: ContainerPortType }>, hostConfig?: { __typename?: 'ContainerHostConfig', networkMode: string } | null } | null } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string } + > } + > } + > } + }> } } }; + +export type CreateDockerFolderMutationVariables = Exact<{ + name: Scalars['String']['input']; + parentId?: InputMaybe; + childrenIds?: InputMaybe | Scalars['String']['input']>; +}>; + + +export type CreateDockerFolderMutation = { __typename?: 'Mutation', createDockerFolder: { __typename?: 'ResolvedOrganizerV1', version: number, views: Array<{ __typename?: 'ResolvedOrganizerView', id: string, name: string, root: + | { __typename: 'OrganizerContainerResource' } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string, children: Array< + | { __typename: 'OrganizerContainerResource', id: string, name: string, type: string } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string } + > } + }> } }; + +export type GetDockerContainerOverviewFormQueryVariables = Exact<{ + skipCache?: InputMaybe; +}>; + + +export type GetDockerContainerOverviewFormQuery = { __typename?: 'Query', dockerContainerOverviewForm: { __typename?: 'DockerContainerOverviewForm', id: string, dataSchema: any, uiSchema: any, data: any } }; + export type LogFilesQueryVariables = Exact<{ [key: string]: never; }>; @@ -2860,6 +2926,9 @@ export const PreviewEffectivePermissionsDocument = {"kind":"Document","definitio export const GetPermissionsForRolesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetPermissionsForRoles"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"roles"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Role"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getPermissionsForRoles"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"roles"},"value":{"kind":"Variable","name":{"kind":"Name","value":"roles"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"resource"}},{"kind":"Field","name":{"kind":"Name","value":"actions"}}]}}]}}]} as unknown as DocumentNode; export const UnifiedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Unified"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"settings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unified"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSchema"}},{"kind":"Field","name":{"kind":"Name","value":"uiSchema"}},{"kind":"Field","name":{"kind":"Name","value":"values"}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateConnectSettingsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateConnectSettings"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateSettings"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"restartRequired"}},{"kind":"Field","name":{"kind":"Name","value":"values"}}]}}]}}]} as unknown as DocumentNode; +export const GetDockerContainersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDockerContainers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"skipCache"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}},"defaultValue":{"kind":"BooleanValue","value":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"docker"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"organizer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"skipCache"},"value":{"kind":"Variable","name":{"kind":"Name","value":"skipCache"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"views"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"meta"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"names"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"ports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privatePort"}},{"kind":"Field","name":{"kind":"Name","value":"publicPort"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"autoStart"}},{"kind":"Field","name":{"kind":"Name","value":"hostConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"networkMode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"isUpdateAvailable"}},{"kind":"Field","name":{"kind":"Name","value":"isRebuildReady"}}]}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"meta"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"names"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"ports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privatePort"}},{"kind":"Field","name":{"kind":"Name","value":"publicPort"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"autoStart"}},{"kind":"Field","name":{"kind":"Name","value":"hostConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"networkMode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"isUpdateAvailable"}},{"kind":"Field","name":{"kind":"Name","value":"isRebuildReady"}}]}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"meta"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"names"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"ports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privatePort"}},{"kind":"Field","name":{"kind":"Name","value":"publicPort"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"autoStart"}},{"kind":"Field","name":{"kind":"Name","value":"hostConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"networkMode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"isUpdateAvailable"}},{"kind":"Field","name":{"kind":"Name","value":"isRebuildReady"}}]}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"meta"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"names"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"ports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privatePort"}},{"kind":"Field","name":{"kind":"Name","value":"publicPort"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"autoStart"}},{"kind":"Field","name":{"kind":"Name","value":"hostConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"networkMode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"isUpdateAvailable"}},{"kind":"Field","name":{"kind":"Name","value":"isRebuildReady"}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateDockerFolderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateDockerFolder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"parentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"childrenIds"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createDockerFolder"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"parentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"parentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"childrenIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"childrenIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"views"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetDockerContainerOverviewFormDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDockerContainerOverviewForm"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"skipCache"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}},"defaultValue":{"kind":"BooleanValue","value":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dockerContainerOverviewForm"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"skipCache"},"value":{"kind":"Variable","name":{"kind":"Name","value":"skipCache"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSchema"}},{"kind":"Field","name":{"kind":"Name","value":"uiSchema"}},{"kind":"Field","name":{"kind":"Name","value":"data"}}]}}]}}]} as unknown as DocumentNode; export const LogFilesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LogFiles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFiles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"modifiedAt"}}]}}]}}]} as unknown as DocumentNode; export const LogFileContentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LogFileContent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"lines"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"startLine"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFile"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"lines"},"value":{"kind":"Variable","name":{"kind":"Name","value":"lines"}}},{"kind":"Argument","name":{"kind":"Name","value":"startLine"},"value":{"kind":"Variable","name":{"kind":"Name","value":"startLine"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"totalLines"}},{"kind":"Field","name":{"kind":"Name","value":"startLine"}}]}}]}}]} as unknown as DocumentNode; export const LogFileSubscriptionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"LogFileSubscription"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFile"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"totalLines"}}]}}]}}]} as unknown as DocumentNode; From 0681a4e34cba067b4704f0c70327cc6ec7046e02 Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Thu, 18 Sep 2025 10:45:36 -0400 Subject: [PATCH 002/127] fix context menu trigger area and dropdown styles --- web/components.d.ts | 1 + .../Docker/DockerContainersTable.vue | 296 +++++++++++++++--- 2 files changed, 249 insertions(+), 48 deletions(-) diff --git a/web/components.d.ts b/web/components.d.ts index 8419775c03..6fbb68d1d6 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -115,6 +115,7 @@ declare module 'vue' { UButton: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Button.vue')['default'] UCard: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Card.vue')['default'] UCheckbox: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue')['default'] + UContextMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/ContextMenu.vue')['default'] UDrawer: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue')['default'] UDropdownMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenu.vue')['default'] UFormField: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/FormField.vue')['default'] diff --git a/web/src/components/Docker/DockerContainersTable.vue b/web/src/components/Docker/DockerContainersTable.vue index 9a6d7471ec..6a7221c145 100644 --- a/web/src/components/Docker/DockerContainersTable.vue +++ b/web/src/components/Docker/DockerContainersTable.vue @@ -13,6 +13,7 @@ import type { ResolvedOrganizerFolder, } from '@/composables/gql/graphql'; import type { TableColumn } from '@nuxt/ui'; +import type { VNode } from 'vue'; interface Props { containers: DockerContainer[]; @@ -27,7 +28,9 @@ const props = withDefaults(defineProps(), { const UButton = resolveComponent('UButton'); const UCheckbox = resolveComponent('UCheckbox'); const UBadge = resolveComponent('UBadge'); +const UInput = resolveComponent('UInput'); const UDropdownMenu = resolveComponent('UDropdownMenu'); +const UContextMenu = resolveComponent('UContextMenu'); function formatPorts(container?: DockerContainer | null): string { if (!container) return ''; @@ -107,6 +110,30 @@ const treeData = computed(() => { return props.containers.map((container) => toContainerTreeRow(container)); }); +type DropdownMenuItem = { label: string; icon: string; onSelect: (e?: Event) => void; as?: string }; +type DropdownMenuItems = DropdownMenuItem[][]; + +function wrapCell(row: { original: TreeRow }, child: VNode) { + const content = h('div', { 'data-row-id': row.original.id, class: 'block w-full h-full px-3 py-2' }, [ + child, + ]); + if ((row.original as TreeRow).type === 'container') { + return h( + UContextMenu, + { + items: getRowActionItems(row.original as TreeRow), + size: 'md', + ui: { + content: 'overflow-x-hidden z-50', + item: 'bg-transparent hover:bg-transparent focus:bg-transparent border-0 ring-0 outline-none shadow-none data-[state=checked]:bg-transparent', + }, + }, + { default: () => content } + ); + } + return content; +} + const columns = computed[]>(() => { const cols: TableColumn[] = [ { @@ -123,28 +150,34 @@ const columns = computed[]>(() => { cell: ({ row }) => { switch ((row.original as TreeRow).type) { case 'container': - return h(UCheckbox, { - modelValue: row.getIsSelected(), - 'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value), - 'aria-label': 'Select row', - }); + return wrapCell( + row, + h(UCheckbox, { + modelValue: row.getIsSelected(), + 'onUpdate:modelValue': (value: boolean | 'indeterminate') => row.toggleSelected(!!value), + 'aria-label': 'Select row', + }) + ); case 'folder': - return h(UButton, { - color: 'neutral', - size: 'md', - variant: 'ghost', - icon: 'i-lucide-chevron-down', - square: true, - 'aria-label': 'Expand', - class: 'p-0', - ui: { - leadingIcon: [ - 'transition-transform mt-0.5 -rotate-90', - row.getIsExpanded() ? 'duration-200 rotate-0' : '', - ], - }, - onClick: () => row.toggleExpanded(), - }); + return wrapCell( + row, + h(UButton, { + color: 'neutral', + size: 'md', + variant: 'ghost', + icon: 'i-lucide-chevron-down', + square: true, + 'aria-label': 'Expand', + class: 'p-0', + ui: { + leadingIcon: [ + 'transition-transform mt-0.5 -rotate-90', + row.getIsExpanded() ? 'duration-200 rotate-0' : '', + ], + }, + onClick: () => row.toggleExpanded(), + }) + ); default: return h('span'); } @@ -154,17 +187,22 @@ const columns = computed[]>(() => { meta: { class: { th: 'w-10', td: 'w-10' } }, }, { - id: 'title', + accessorKey: 'name', header: 'Name', cell: ({ row }) => { const depth = row.depth; const indent = h('span', { class: 'inline-block', style: { width: `calc(${depth} * 1rem)` } }); const isFolder = (row.original as TreeRow).type === 'folder'; - return h('div', { class: 'truncate flex items-center' }, [ - indent, - h('span', { class: 'max-w-[40ch] truncate font-medium' }, row.original.name), - isFolder ? h('span') : null, - ]); + const content = h( + 'div', + { class: 'truncate flex items-center', 'data-row-id': row.original.id }, + [ + indent, + h('span', { class: 'max-w-[40ch] truncate font-medium' }, row.original.name), + isFolder ? h('span') : null, + ] + ); + return wrapCell(row, content); }, meta: { class: { td: 'w-[40ch] truncate', th: 'w-[45ch]' } }, }, @@ -178,45 +216,90 @@ const columns = computed[]>(() => { [ContainerState.RUNNING]: 'success' as const, [ContainerState.EXITED]: 'neutral' as const, }[state]; - return h( - UBadge, - { - color, - }, - () => state + return wrapCell( + row, + h( + UBadge, + { + color, + }, + () => state + ) ); }, }, { accessorKey: 'ports', header: 'Ports', - cell: ({ row }) => (row.original.type === 'folder' ? '' : String(row.getValue('ports') || '')), + cell: ({ row }) => + row.original.type === 'folder' + ? '' + : wrapCell(row, h('span', null, String(row.getValue('ports') || ''))), }, { accessorKey: 'autoStart', header: 'Auto Start', - cell: ({ row }) => (row.original.type === 'folder' ? '' : String(row.getValue('autoStart') || '')), + cell: ({ row }) => + row.original.type === 'folder' + ? '' + : wrapCell(row, h('span', null, String(row.getValue('autoStart') || ''))), }, { accessorKey: 'updates', header: 'Updates', - cell: ({ row }) => (row.original.type === 'folder' ? '' : String(row.getValue('updates') || '')), + cell: ({ row }) => + row.original.type === 'folder' + ? '' + : wrapCell(row, h('span', null, String(row.getValue('updates') || ''))), + }, + { + id: 'actions', + header: '', + cell: ({ row }) => { + if ((row.original as TreeRow).type === 'folder') return ''; + const items = getRowActionItems(row.original as TreeRow); + return wrapCell( + row, + h( + UDropdownMenu, + { + items, + size: 'md', + ui: { + content: 'overflow-x-hidden z-10', + item: 'bg-transparent hover:bg-transparent focus:bg-transparent border-0 ring-0 outline-none shadow-none data-[state=checked]:bg-transparent', + }, + }, + { + default: () => + h(UButton, { + color: 'neutral', + variant: 'ghost', + icon: 'i-lucide-more-vertical', + square: true, + 'aria-label': 'Row actions', + }), + } + ) + ); + }, + enableSorting: false, + enableHiding: false, + meta: { class: { th: 'w-8', td: 'w-8 text-right' } }, }, ]; return cols; }); const rowSelection = ref>({}); +type NuxtUITableRef = { table?: { getSelectedRowModel: () => { rows: unknown[] } } } | null; +const tableRef = ref(null); const selectedCount = computed(() => { - const containerIds: string[] = treeData.value - .flatMap(function collect(row): TreeRow[] { - if (row.type === 'container') return [row]; - return (row.children || []).flatMap(collect); - }) - .map((r) => r.id); - return containerIds.filter((id) => !!rowSelection.value[id]).length; + return Object.values(rowSelection.value).filter(Boolean).length; }); +const globalFilter = ref(''); + const emit = defineEmits<{ (e: 'created-folder'): void; }>(); @@ -244,23 +327,140 @@ async function handleCreateFolder() { rowSelection.value = {}; emit('created-folder'); } + +function getSelectedContainerIds(): string[] { + return treeData.value + .flatMap(function collect(row): TreeRow[] { + if (row.type === 'container') return [row]; + return (row.children || []).flatMap(collect); + }) + .filter((r) => rowSelection.value[r.id]) + .map((r) => r.id); +} + +declare global { + interface Window { + toast?: { + success: (title: string, options?: { description?: string }) => void; + error?: (title: string, options?: { description?: string }) => void; + }; + } +} + +function showToast(message: string) { + window.toast?.success(message); +} + +function handleBulkAction(action: string) { + const ids = getSelectedContainerIds(); + if (ids.length === 0) return; + showToast(`${action} (${ids.length})`); +} + +// helper removed; no longer used + +// removed unused types +// ContextRef type removed; no longer used + +// no-op: replaced by row-wrapped UContextMenu + +// Removed programmatic context menu open logic in favor of wrapping row with UContextMenu + +function handleRowAction(row: TreeRow, action: string) { + if (row.type !== 'container') return; + showToast(`${action}: ${row.name}`); +} + +const bulkItems = computed(() => [ + [ + { + label: 'Move to folder', + icon: 'i-lucide-folder', + as: 'button', + onSelect: () => handleBulkAction('Move to folder'), + }, + { + label: 'Start / Stop', + icon: 'i-lucide-power', + as: 'button', + onSelect: () => handleBulkAction('Start / Stop'), + }, + { + label: 'Pause / Resume', + icon: 'i-lucide-pause', + as: 'button', + onSelect: () => handleBulkAction('Pause / Resume'), + }, + ], +]); + +function getRowActionItems(row: TreeRow): DropdownMenuItems { + return [ + [ + { + label: 'Move to folder', + icon: 'i-lucide-folder', + as: 'button', + onSelect: () => handleRowAction(row, 'Move to folder'), + }, + { + label: 'Start / Stop', + icon: 'i-lucide-power', + as: 'button', + onSelect: () => handleRowAction(row, 'Start / Stop'), + }, + { + label: 'Pause / Resume', + icon: 'i-lucide-pause', + as: 'button', + onSelect: () => handleRowAction(row, 'Pause / Resume'), + }, + ], + [ + { + label: 'Manage Settings', + icon: 'i-lucide-settings', + as: 'button', + onSelect: () => handleRowAction(row, 'Manage Settings'), + }, + ], + ]; +} diff --git a/web/src/components/Docker/docker-delete-entries.mutation.ts b/web/src/components/Docker/docker-delete-entries.mutation.ts new file mode 100644 index 0000000000..04b8400680 --- /dev/null +++ b/web/src/components/Docker/docker-delete-entries.mutation.ts @@ -0,0 +1,34 @@ +import { gql } from '@apollo/client'; + +export const DELETE_DOCKER_ENTRIES = gql` + mutation DeleteDockerEntries($entryIds: [String!]!) { + deleteDockerEntries(entryIds: $entryIds) { + version + views { + id + name + root { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + children { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + } + ... on OrganizerContainerResource { + id + name + type + } + } + } + } + } + } + } +`; diff --git a/web/src/components/Docker/docker-move-entries.mutation.ts b/web/src/components/Docker/docker-move-entries.mutation.ts new file mode 100644 index 0000000000..a40435840a --- /dev/null +++ b/web/src/components/Docker/docker-move-entries.mutation.ts @@ -0,0 +1,37 @@ +import { gql } from '@apollo/client'; + +export const MOVE_DOCKER_ENTRIES_TO_FOLDER = gql` + mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) { + moveDockerEntriesToFolder( + destinationFolderId: $destinationFolderId + sourceEntryIds: $sourceEntryIds + ) { + version + views { + id + name + root { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + children { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + } + ... on OrganizerContainerResource { + id + name + type + } + } + } + } + } + } + } +`; diff --git a/web/src/components/Docker/docker-set-folder-children.mutation.ts b/web/src/components/Docker/docker-set-folder-children.mutation.ts new file mode 100644 index 0000000000..b8de7aa857 --- /dev/null +++ b/web/src/components/Docker/docker-set-folder-children.mutation.ts @@ -0,0 +1,34 @@ +import { gql } from '@apollo/client'; + +export const SET_DOCKER_FOLDER_CHILDREN = gql` + mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) { + setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) { + version + views { + id + name + root { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + children { + __typename + ... on ResolvedOrganizerFolder { + id + name + type + } + ... on OrganizerContainerResource { + id + name + type + } + } + } + } + } + } + } +`; diff --git a/web/src/composables/gql/gql.ts b/web/src/composables/gql/gql.ts index 2e575ec55f..25328f08d8 100644 --- a/web/src/composables/gql/gql.ts +++ b/web/src/composables/gql/gql.ts @@ -30,7 +30,10 @@ type Documents = { "\n mutation UpdateConnectSettings($input: JSON!) {\n updateSettings(input: $input) {\n restartRequired\n values\n }\n }\n": typeof types.UpdateConnectSettingsDocument, "\n query GetDockerContainers($skipCache: Boolean = false) {\n docker {\n id\n organizer(skipCache: $skipCache) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n }\n }\n }\n": typeof types.GetDockerContainersDocument, "\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.CreateDockerFolderDocument, + "\n mutation DeleteDockerEntries($entryIds: [String!]!) {\n deleteDockerEntries(entryIds: $entryIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.DeleteDockerEntriesDocument, + "\n mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {\n moveDockerEntriesToFolder(\n destinationFolderId: $destinationFolderId\n sourceEntryIds: $sourceEntryIds\n ) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.MoveDockerEntriesToFolderDocument, "\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n": typeof types.GetDockerContainerOverviewFormDocument, + "\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.SetDockerFolderChildrenDocument, "\n query LogFiles {\n logFiles {\n name\n path\n size\n modifiedAt\n }\n }\n": typeof types.LogFilesDocument, "\n query LogFileContent($path: String!, $lines: Int, $startLine: Int) {\n logFile(path: $path, lines: $lines, startLine: $startLine) {\n path\n content\n totalLines\n startLine\n }\n }\n": typeof types.LogFileContentDocument, "\n subscription LogFileSubscription($path: String!) {\n logFile(path: $path) {\n path\n content\n totalLines\n }\n }\n": typeof types.LogFileSubscriptionDocument, @@ -78,7 +81,10 @@ const documents: Documents = { "\n mutation UpdateConnectSettings($input: JSON!) {\n updateSettings(input: $input) {\n restartRequired\n values\n }\n }\n": types.UpdateConnectSettingsDocument, "\n query GetDockerContainers($skipCache: Boolean = false) {\n docker {\n id\n organizer(skipCache: $skipCache) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n meta {\n id\n names\n state\n status\n image\n ports {\n privatePort\n publicPort\n type\n }\n autoStart\n hostConfig {\n networkMode\n }\n created\n isUpdateAvailable\n isRebuildReady\n }\n }\n }\n }\n }\n }\n }\n": types.GetDockerContainersDocument, "\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.CreateDockerFolderDocument, + "\n mutation DeleteDockerEntries($entryIds: [String!]!) {\n deleteDockerEntries(entryIds: $entryIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.DeleteDockerEntriesDocument, + "\n mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {\n moveDockerEntriesToFolder(\n destinationFolderId: $destinationFolderId\n sourceEntryIds: $sourceEntryIds\n ) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.MoveDockerEntriesToFolderDocument, "\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n": types.GetDockerContainerOverviewFormDocument, + "\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.SetDockerFolderChildrenDocument, "\n query LogFiles {\n logFiles {\n name\n path\n size\n modifiedAt\n }\n }\n": types.LogFilesDocument, "\n query LogFileContent($path: String!, $lines: Int, $startLine: Int) {\n logFile(path: $path, lines: $lines, startLine: $startLine) {\n path\n content\n totalLines\n startLine\n }\n }\n": types.LogFileContentDocument, "\n subscription LogFileSubscription($path: String!) {\n logFile(path: $path) {\n path\n content\n totalLines\n }\n }\n": types.LogFileSubscriptionDocument, @@ -188,10 +194,22 @@ export function graphql(source: "\n query GetDockerContainers($skipCache: Boole * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation CreateDockerFolder($name: String!, $parentId: String, $childrenIds: [String!]) {\n createDockerFolder(name: $name, parentId: $parentId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation DeleteDockerEntries($entryIds: [String!]!) {\n deleteDockerEntries(entryIds: $entryIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation DeleteDockerEntries($entryIds: [String!]!) {\n deleteDockerEntries(entryIds: $entryIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {\n moveDockerEntriesToFolder(\n destinationFolderId: $destinationFolderId\n sourceEntryIds: $sourceEntryIds\n ) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {\n moveDockerEntriesToFolder(\n destinationFolderId: $destinationFolderId\n sourceEntryIds: $sourceEntryIds\n ) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n"): (typeof documents)["\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/web/src/composables/gql/graphql.ts b/web/src/composables/gql/graphql.ts index 4987cc2a69..56740ca5b4 100644 --- a/web/src/composables/gql/graphql.ts +++ b/web/src/composables/gql/graphql.ts @@ -2721,6 +2721,37 @@ export type CreateDockerFolderMutation = { __typename?: 'Mutation', createDocker > } }> } }; +export type DeleteDockerEntriesMutationVariables = Exact<{ + entryIds: Array | Scalars['String']['input']; +}>; + + +export type DeleteDockerEntriesMutation = { __typename?: 'Mutation', deleteDockerEntries: { __typename?: 'ResolvedOrganizerV1', version: number, views: Array<{ __typename?: 'ResolvedOrganizerView', id: string, name: string, root: + | { __typename: 'OrganizerContainerResource' } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string, children: Array< + | { __typename: 'OrganizerContainerResource', id: string, name: string, type: string } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string } + > } + }> } }; + +export type MoveDockerEntriesToFolderMutationVariables = Exact<{ + destinationFolderId: Scalars['String']['input']; + sourceEntryIds: Array | Scalars['String']['input']; +}>; + + +export type MoveDockerEntriesToFolderMutation = { __typename?: 'Mutation', moveDockerEntriesToFolder: { __typename?: 'ResolvedOrganizerV1', version: number, views: Array<{ __typename?: 'ResolvedOrganizerView', id: string, name: string, root: + | { __typename: 'OrganizerContainerResource' } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string, children: Array< + | { __typename: 'OrganizerContainerResource', id: string, name: string, type: string } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string } + > } + }> } }; + export type GetDockerContainerOverviewFormQueryVariables = Exact<{ skipCache?: InputMaybe; }>; @@ -2728,6 +2759,22 @@ export type GetDockerContainerOverviewFormQueryVariables = Exact<{ export type GetDockerContainerOverviewFormQuery = { __typename?: 'Query', dockerContainerOverviewForm: { __typename?: 'DockerContainerOverviewForm', id: string, dataSchema: any, uiSchema: any, data: any } }; +export type SetDockerFolderChildrenMutationVariables = Exact<{ + folderId?: InputMaybe; + childrenIds: Array | Scalars['String']['input']; +}>; + + +export type SetDockerFolderChildrenMutation = { __typename?: 'Mutation', setDockerFolderChildren: { __typename?: 'ResolvedOrganizerV1', version: number, views: Array<{ __typename?: 'ResolvedOrganizerView', id: string, name: string, root: + | { __typename: 'OrganizerContainerResource' } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string, children: Array< + | { __typename: 'OrganizerContainerResource', id: string, name: string, type: string } + | { __typename: 'OrganizerResource' } + | { __typename: 'ResolvedOrganizerFolder', id: string, name: string, type: string } + > } + }> } }; + export type LogFilesQueryVariables = Exact<{ [key: string]: never; }>; @@ -2928,7 +2975,10 @@ export const UnifiedDocument = {"kind":"Document","definitions":[{"kind":"Operat export const UpdateConnectSettingsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateConnectSettings"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateSettings"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"restartRequired"}},{"kind":"Field","name":{"kind":"Name","value":"values"}}]}}]}}]} as unknown as DocumentNode; export const GetDockerContainersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDockerContainers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"skipCache"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}},"defaultValue":{"kind":"BooleanValue","value":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"docker"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"organizer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"skipCache"},"value":{"kind":"Variable","name":{"kind":"Name","value":"skipCache"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"views"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"meta"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"names"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"ports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privatePort"}},{"kind":"Field","name":{"kind":"Name","value":"publicPort"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"autoStart"}},{"kind":"Field","name":{"kind":"Name","value":"hostConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"networkMode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"isUpdateAvailable"}},{"kind":"Field","name":{"kind":"Name","value":"isRebuildReady"}}]}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"meta"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"names"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"ports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privatePort"}},{"kind":"Field","name":{"kind":"Name","value":"publicPort"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"autoStart"}},{"kind":"Field","name":{"kind":"Name","value":"hostConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"networkMode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"isUpdateAvailable"}},{"kind":"Field","name":{"kind":"Name","value":"isRebuildReady"}}]}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"meta"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"names"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"ports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privatePort"}},{"kind":"Field","name":{"kind":"Name","value":"publicPort"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"autoStart"}},{"kind":"Field","name":{"kind":"Name","value":"hostConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"networkMode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"isUpdateAvailable"}},{"kind":"Field","name":{"kind":"Name","value":"isRebuildReady"}}]}}]}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"meta"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"names"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"ports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"privatePort"}},{"kind":"Field","name":{"kind":"Name","value":"publicPort"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"autoStart"}},{"kind":"Field","name":{"kind":"Name","value":"hostConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"networkMode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"isUpdateAvailable"}},{"kind":"Field","name":{"kind":"Name","value":"isRebuildReady"}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateDockerFolderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateDockerFolder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"parentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"childrenIds"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createDockerFolder"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"parentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"parentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"childrenIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"childrenIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"views"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const DeleteDockerEntriesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteDockerEntries"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"entryIds"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteDockerEntries"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"entryIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"entryIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"views"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const MoveDockerEntriesToFolderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MoveDockerEntriesToFolder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"destinationFolderId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sourceEntryIds"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"moveDockerEntriesToFolder"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"destinationFolderId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"destinationFolderId"}}},{"kind":"Argument","name":{"kind":"Name","value":"sourceEntryIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sourceEntryIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"views"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetDockerContainerOverviewFormDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDockerContainerOverviewForm"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"skipCache"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}},"defaultValue":{"kind":"BooleanValue","value":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dockerContainerOverviewForm"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"skipCache"},"value":{"kind":"Variable","name":{"kind":"Name","value":"skipCache"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSchema"}},{"kind":"Field","name":{"kind":"Name","value":"uiSchema"}},{"kind":"Field","name":{"kind":"Name","value":"data"}}]}}]}}]} as unknown as DocumentNode; +export const SetDockerFolderChildrenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SetDockerFolderChildren"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"folderId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"childrenIds"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"setDockerFolderChildren"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"folderId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"folderId"}}},{"kind":"Argument","name":{"kind":"Name","value":"childrenIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"childrenIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"views"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const LogFilesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LogFiles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFiles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"modifiedAt"}}]}}]}}]} as unknown as DocumentNode; export const LogFileContentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LogFileContent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"lines"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"startLine"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFile"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"lines"},"value":{"kind":"Variable","name":{"kind":"Name","value":"lines"}}},{"kind":"Argument","name":{"kind":"Name","value":"startLine"},"value":{"kind":"Variable","name":{"kind":"Name","value":"startLine"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"totalLines"}},{"kind":"Field","name":{"kind":"Name","value":"startLine"}}]}}]}}]} as unknown as DocumentNode; export const LogFileSubscriptionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"LogFileSubscription"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFile"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"totalLines"}}]}}]}}]} as unknown as DocumentNode; From 10f07e9b193d80683401ab3f9d5657718aacd4de Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Thu, 18 Sep 2025 16:35:49 -0400 Subject: [PATCH 004/127] implement 'start / stop' bulk & row actions --- web/components.d.ts | 1 + .../Docker/DockerContainersTable.vue | 209 +++++++++++++++++- .../Docker/docker-start-container.mutation.ts | 13 ++ .../Docker/docker-stop-container.mutation.ts | 13 ++ web/src/composables/gql/gql.ts | 12 + web/src/composables/gql/graphql.ts | 16 ++ 6 files changed, 253 insertions(+), 11 deletions(-) create mode 100644 web/src/components/Docker/docker-start-container.mutation.ts create mode 100644 web/src/components/Docker/docker-stop-container.mutation.ts diff --git a/web/components.d.ts b/web/components.d.ts index fd0188e853..83c69bf9ab 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -130,6 +130,7 @@ declare module 'vue' { URadioGroup: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/RadioGroup.vue')['default'] USelectMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue')['default'] 'UserProfile.standalone': typeof import('./src/components/UserProfile.standalone.vue')['default'] + USkeleton: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Skeleton.vue')['default'] UTable: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Table.vue')['default'] 'WanIpCheck.standalone': typeof import('./src/components/WanIpCheck.standalone.vue')['default'] 'WelcomeModal.standalone': typeof import('./src/components/Activation/WelcomeModal.standalone.vue')['default'] diff --git a/web/src/components/Docker/DockerContainersTable.vue b/web/src/components/Docker/DockerContainersTable.vue index 9cd516d061..5e08f1e315 100644 --- a/web/src/components/Docker/DockerContainersTable.vue +++ b/web/src/components/Docker/DockerContainersTable.vue @@ -8,6 +8,8 @@ import { CREATE_DOCKER_FOLDER } from '@/components/Docker/docker-create-folder.m import { DELETE_DOCKER_ENTRIES } from '@/components/Docker/docker-delete-entries.mutation'; import { MOVE_DOCKER_ENTRIES_TO_FOLDER } from '@/components/Docker/docker-move-entries.mutation'; import { SET_DOCKER_FOLDER_CHILDREN } from '@/components/Docker/docker-set-folder-children.mutation'; +import { START_DOCKER_CONTAINER } from '@/components/Docker/docker-start-container.mutation'; +import { STOP_DOCKER_CONTAINER } from '@/components/Docker/docker-stop-container.mutation'; import { ContainerState } from '@/composables/gql/graphql'; import type { @@ -16,7 +18,7 @@ import type { ResolvedOrganizerFolder, } from '@/composables/gql/graphql'; import type { TableColumn } from '@nuxt/ui'; -import type { VNode } from 'vue'; +import type { Component, VNode } from 'vue'; interface Props { containers: DockerContainer[]; @@ -35,6 +37,7 @@ const UInput = resolveComponent('UInput'); const UDropdownMenu = resolveComponent('UDropdownMenu'); const UContextMenu = resolveComponent('UContextMenu'); const UModal = resolveComponent('UModal'); +const USkeleton = resolveComponent('USkeleton') as Component; function formatPorts(container?: DockerContainer | null): string { if (!container) return ''; @@ -61,6 +64,7 @@ type TreeRow = { autoStart?: string; updates?: string; children?: TreeRow[]; + containerId?: string; }; function toContainerTreeRow(meta: DockerContainer | null | undefined, fallbackName?: string): TreeRow { @@ -76,6 +80,7 @@ function toContainerTreeRow(meta: DockerContainer | null | undefined, fallbackNa ports: formatPorts(meta || undefined), autoStart: meta?.autoStart ? 'On' : 'Off', updates: updatesParts.join(' / ') || '—', + containerId: meta?.id, }; } @@ -93,6 +98,7 @@ function buildTree(entry: ResolvedOrganizerEntry): TreeRow | null { const meta = entry.meta as DockerContainer | null | undefined; const row = toContainerTreeRow(meta, entry.name || undefined); row.id = entry.id; + row.containerId = meta?.id; return row; } return { @@ -118,9 +124,15 @@ type DropdownMenuItem = { label: string; icon: string; onSelect: (e?: Event) => type DropdownMenuItems = DropdownMenuItem[][]; function wrapCell(row: { original: TreeRow }, child: VNode) { - const content = h('div', { 'data-row-id': row.original.id, class: 'block w-full h-full px-3 py-2' }, [ - child, - ]); + const isBusy = busyRowIds.value.has((row.original as TreeRow).id); + const content = h( + 'div', + { + 'data-row-id': row.original.id, + class: `block w-full h-full px-3 py-2 ${isBusy ? 'opacity-50 pointer-events-none select-none' : ''}`, + }, + [child] + ); if ((row.original as TreeRow).type === 'container') { return h( UContextMenu, @@ -216,19 +228,17 @@ const columns = computed[]>(() => { cell: ({ row }) => { if (row.original.type === 'folder') return ''; const state = row.original.state ?? ''; + const isBusy = busyRowIds.value.has(row.original.id); const color = { [ContainerState.RUNNING]: 'success' as const, [ContainerState.EXITED]: 'neutral' as const, }[state]; + if (isBusy) { + return wrapCell(row, h(USkeleton, { class: 'h-5 w-20' })); + } return wrapCell( row, - h( - UBadge, - { - color, - }, - () => state - ) + h(UBadge, { color }, () => state) ); }, }, @@ -312,6 +322,8 @@ const { mutate: createFolderMutation, loading: creating } = useMutation(CREATE_D const { mutate: moveEntriesMutation, loading: moving } = useMutation(MOVE_DOCKER_ENTRIES_TO_FOLDER); const { mutate: deleteEntriesMutation, loading: deleting } = useMutation(DELETE_DOCKER_ENTRIES); const { mutate: setFolderChildrenMutation } = useMutation(SET_DOCKER_FOLDER_CHILDREN); +const { mutate: startContainerMutation } = useMutation(START_DOCKER_CONTAINER); +const { mutate: stopContainerMutation } = useMutation(STOP_DOCKER_CONTAINER); const moveOpen = ref(false); const selectedFolderId = ref(''); @@ -322,6 +334,144 @@ const renameValue = ref(''); const newTreeFolderName = ref(''); const rootFolderId = computed(() => props.organizerRoot?.id || ''); +// Busy/disabled rows while performing start/stop +const busyRowIds = ref>(new Set()); + +function setRowsBusy(ids: string[], busy: boolean) { + const next = new Set(busyRowIds.value); + for (const id of ids) { + if (busy) next.add(id); + else next.delete(id); + } + busyRowIds.value = next; +} + +function getRowById(targetId: string): TreeRow | undefined { + function walk(rows: TreeRow[]): TreeRow | undefined { + for (const r of rows) { + if (r.id === targetId) return r; + if (r.children?.length) { + const found = walk(r.children as TreeRow[]); + if (found) return found; + } + } + return undefined; + } + return walk(treeData.value); +} + +function classifyStartStop(ids: string[]) { + const toStart: { id: string; containerId: string; name: string }[] = []; + const toStop: { id: string; containerId: string; name: string }[] = []; + for (const id of ids) { + const row = getRowById(id); + if (!row || row.type !== 'container') continue; + const containerId = row.containerId || row.id; + const state = row.state as string | undefined; + const name = row.name; + if (state === ContainerState.RUNNING) toStop.push({ id, containerId, name }); + else toStart.push({ id, containerId, name }); + } + return { toStart, toStop }; +} + +async function runStartStopBatch( + toStart: { id: string; containerId: string; name: string }[], + toStop: { id: string; containerId: string; name: string }[] +) { + const totalOps = toStop.length + toStart.length; + let completed = 0; + // Execute sequentially; attach refetch to the final operation only + for (const item of toStop) { + completed++; + const isLast = completed === totalOps; + await stopContainerMutation( + { id: item.containerId }, + isLast + ? { + refetchQueries: [{ query: GET_DOCKER_CONTAINERS, variables: { skipCache: true } }], + awaitRefetchQueries: true, + } + : { awaitRefetchQueries: false } + ); + } + for (const item of toStart) { + completed++; + const isLast = completed === totalOps; + await startContainerMutation( + { id: item.containerId }, + isLast + ? { + refetchQueries: [{ query: GET_DOCKER_CONTAINERS, variables: { skipCache: true } }], + awaitRefetchQueries: true, + } + : { awaitRefetchQueries: false } + ); + } +} + +async function handleRowStartStop(row: TreeRow) { + if (row.type !== 'container') return; + const containerId = row.containerId || row.id; + if (!containerId) return; + setRowsBusy([row.id], true); + try { + const isRunning = row.state === ContainerState.RUNNING; + const mutate = isRunning ? stopContainerMutation : startContainerMutation; + await mutate( + { id: containerId }, + { + refetchQueries: [{ query: GET_DOCKER_CONTAINERS, variables: { skipCache: true } }], + awaitRefetchQueries: true, + } + ); + } finally { + setRowsBusy([row.id], false); + } +} + +// Bulk Start/Stop handling with mixed confirmation +const confirmStartStopOpen = ref(false); +const confirmToStart = ref<{ name: string }[]>([]); +const confirmToStop = ref<{ name: string }[]>([]); +let pendingStartStopIds: string[] = []; + +function openStartStop(ids?: string[]) { + const sources = ids ?? getSelectedContainerIds(); + if (sources.length === 0) return; + const { toStart, toStop } = classifyStartStop(sources); + const isMixed = toStart.length > 0 && toStop.length > 0; + if (isMixed) { + pendingStartStopIds = sources; + confirmToStart.value = toStart.map((i) => ({ name: i.name })); + confirmToStop.value = toStop.map((i) => ({ name: i.name })); + confirmStartStopOpen.value = true; + return; + } + // Single-type selection: run immediately with busy rows + setRowsBusy(sources, true); + runStartStopBatch(toStart, toStop) + .then(() => showToast('Action completed')) + .finally(() => { + setRowsBusy(sources, false); + rowSelection.value = {}; + }); +} + +async function confirmStartStop(close: () => void) { + const { toStart, toStop } = classifyStartStop(pendingStartStopIds); + setRowsBusy(pendingStartStopIds, true); + try { + await runStartStopBatch(toStart, toStop); + showToast('Action completed'); + rowSelection.value = {}; + } finally { + setRowsBusy(pendingStartStopIds, false); + confirmStartStopOpen.value = false; + pendingStartStopIds = []; + close(); + } +} type FolderNode = { id: string; name: string; children: FolderNode[] }; @@ -540,7 +690,12 @@ function showToast(message: string) { function handleBulkAction(action: string) { const ids = getSelectedContainerIds(); + console.log('ids', ids); if (ids.length === 0) return; + if (action === 'Start / Stop') { + openStartStop(ids); + return; + } showToast(`${action} (${ids.length})`); } @@ -555,6 +710,10 @@ function handleBulkAction(action: string) { function handleRowAction(row: TreeRow, action: string) { if (row.type !== 'container') return; + if (action === 'Start / Stop') { + handleRowStartStop(row); + return; + } showToast(`${action}: ${row.name}`); } @@ -644,6 +803,7 @@ function getRowActionItems(row: TreeRow): DropdownMenuItems { v-model:global-filter="globalFilter" :data="treeData" :columns="columns" + :get-row-id="(row: any) => row.id" :get-sub-rows="(row: any) => row.children" :column-filters-options="{ filterFromLeafRows: true }" :loading="loading" @@ -756,5 +916,32 @@ function getRowActionItems(row: TreeRow): DropdownMenuItems { + + + + + diff --git a/web/src/components/Docker/docker-start-container.mutation.ts b/web/src/components/Docker/docker-start-container.mutation.ts new file mode 100644 index 0000000000..e70db32912 --- /dev/null +++ b/web/src/components/Docker/docker-start-container.mutation.ts @@ -0,0 +1,13 @@ +import { gql } from '@apollo/client'; + +export const START_DOCKER_CONTAINER = gql` + mutation StartDockerContainer($id: PrefixedID!) { + docker { + start(id: $id) { + id + names + state + } + } + } +`; diff --git a/web/src/components/Docker/docker-stop-container.mutation.ts b/web/src/components/Docker/docker-stop-container.mutation.ts new file mode 100644 index 0000000000..8089a5491b --- /dev/null +++ b/web/src/components/Docker/docker-stop-container.mutation.ts @@ -0,0 +1,13 @@ +import { gql } from '@apollo/client'; + +export const STOP_DOCKER_CONTAINER = gql` + mutation StopDockerContainer($id: PrefixedID!) { + docker { + stop(id: $id) { + id + names + state + } + } + } +`; diff --git a/web/src/composables/gql/gql.ts b/web/src/composables/gql/gql.ts index 25328f08d8..c69ff49e4b 100644 --- a/web/src/composables/gql/gql.ts +++ b/web/src/composables/gql/gql.ts @@ -34,6 +34,8 @@ type Documents = { "\n mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {\n moveDockerEntriesToFolder(\n destinationFolderId: $destinationFolderId\n sourceEntryIds: $sourceEntryIds\n ) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.MoveDockerEntriesToFolderDocument, "\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n": typeof types.GetDockerContainerOverviewFormDocument, "\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": typeof types.SetDockerFolderChildrenDocument, + "\n mutation StartDockerContainer($id: PrefixedID!) {\n docker {\n start(id: $id) {\n id\n names\n state\n }\n }\n }\n": typeof types.StartDockerContainerDocument, + "\n mutation StopDockerContainer($id: PrefixedID!) {\n docker {\n stop(id: $id) {\n id\n names\n state\n }\n }\n }\n": typeof types.StopDockerContainerDocument, "\n query LogFiles {\n logFiles {\n name\n path\n size\n modifiedAt\n }\n }\n": typeof types.LogFilesDocument, "\n query LogFileContent($path: String!, $lines: Int, $startLine: Int) {\n logFile(path: $path, lines: $lines, startLine: $startLine) {\n path\n content\n totalLines\n startLine\n }\n }\n": typeof types.LogFileContentDocument, "\n subscription LogFileSubscription($path: String!) {\n logFile(path: $path) {\n path\n content\n totalLines\n }\n }\n": typeof types.LogFileSubscriptionDocument, @@ -85,6 +87,8 @@ const documents: Documents = { "\n mutation MoveDockerEntriesToFolder($destinationFolderId: String!, $sourceEntryIds: [String!]!) {\n moveDockerEntriesToFolder(\n destinationFolderId: $destinationFolderId\n sourceEntryIds: $sourceEntryIds\n ) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.MoveDockerEntriesToFolderDocument, "\n query GetDockerContainerOverviewForm($skipCache: Boolean = false) {\n dockerContainerOverviewForm(skipCache: $skipCache) {\n id\n dataSchema\n uiSchema\n data\n }\n }\n": types.GetDockerContainerOverviewFormDocument, "\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n": types.SetDockerFolderChildrenDocument, + "\n mutation StartDockerContainer($id: PrefixedID!) {\n docker {\n start(id: $id) {\n id\n names\n state\n }\n }\n }\n": types.StartDockerContainerDocument, + "\n mutation StopDockerContainer($id: PrefixedID!) {\n docker {\n stop(id: $id) {\n id\n names\n state\n }\n }\n }\n": types.StopDockerContainerDocument, "\n query LogFiles {\n logFiles {\n name\n path\n size\n modifiedAt\n }\n }\n": types.LogFilesDocument, "\n query LogFileContent($path: String!, $lines: Int, $startLine: Int) {\n logFile(path: $path, lines: $lines, startLine: $startLine) {\n path\n content\n totalLines\n startLine\n }\n }\n": types.LogFileContentDocument, "\n subscription LogFileSubscription($path: String!) {\n logFile(path: $path) {\n path\n content\n totalLines\n }\n }\n": types.LogFileSubscriptionDocument, @@ -210,6 +214,14 @@ export function graphql(source: "\n query GetDockerContainerOverviewForm($skipC * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n mutation SetDockerFolderChildren($folderId: String, $childrenIds: [String!]!) {\n setDockerFolderChildren(folderId: $folderId, childrenIds: $childrenIds) {\n version\n views {\n id\n name\n root {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n children {\n __typename\n ... on ResolvedOrganizerFolder {\n id\n name\n type\n }\n ... on OrganizerContainerResource {\n id\n name\n type\n }\n }\n }\n }\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation StartDockerContainer($id: PrefixedID!) {\n docker {\n start(id: $id) {\n id\n names\n state\n }\n }\n }\n"): (typeof documents)["\n mutation StartDockerContainer($id: PrefixedID!) {\n docker {\n start(id: $id) {\n id\n names\n state\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation StopDockerContainer($id: PrefixedID!) {\n docker {\n stop(id: $id) {\n id\n names\n state\n }\n }\n }\n"): (typeof documents)["\n mutation StopDockerContainer($id: PrefixedID!) {\n docker {\n stop(id: $id) {\n id\n names\n state\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/web/src/composables/gql/graphql.ts b/web/src/composables/gql/graphql.ts index 56740ca5b4..52ebe1f710 100644 --- a/web/src/composables/gql/graphql.ts +++ b/web/src/composables/gql/graphql.ts @@ -2775,6 +2775,20 @@ export type SetDockerFolderChildrenMutation = { __typename?: 'Mutation', setDock > } }> } }; +export type StartDockerContainerMutationVariables = Exact<{ + id: Scalars['PrefixedID']['input']; +}>; + + +export type StartDockerContainerMutation = { __typename?: 'Mutation', docker: { __typename?: 'DockerMutations', start: { __typename?: 'DockerContainer', id: string, names: Array, state: ContainerState } } }; + +export type StopDockerContainerMutationVariables = Exact<{ + id: Scalars['PrefixedID']['input']; +}>; + + +export type StopDockerContainerMutation = { __typename?: 'Mutation', docker: { __typename?: 'DockerMutations', stop: { __typename?: 'DockerContainer', id: string, names: Array, state: ContainerState } } }; + export type LogFilesQueryVariables = Exact<{ [key: string]: never; }>; @@ -2979,6 +2993,8 @@ export const DeleteDockerEntriesDocument = {"kind":"Document","definitions":[{"k export const MoveDockerEntriesToFolderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MoveDockerEntriesToFolder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"destinationFolderId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sourceEntryIds"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"moveDockerEntriesToFolder"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"destinationFolderId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"destinationFolderId"}}},{"kind":"Argument","name":{"kind":"Name","value":"sourceEntryIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sourceEntryIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"views"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetDockerContainerOverviewFormDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDockerContainerOverviewForm"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"skipCache"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}},"defaultValue":{"kind":"BooleanValue","value":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dockerContainerOverviewForm"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"skipCache"},"value":{"kind":"Variable","name":{"kind":"Name","value":"skipCache"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSchema"}},{"kind":"Field","name":{"kind":"Name","value":"uiSchema"}},{"kind":"Field","name":{"kind":"Name","value":"data"}}]}}]}}]} as unknown as DocumentNode; export const SetDockerFolderChildrenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SetDockerFolderChildren"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"folderId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"childrenIds"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"setDockerFolderChildren"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"folderId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"folderId"}}},{"kind":"Argument","name":{"kind":"Name","value":"childrenIds"},"value":{"kind":"Variable","name":{"kind":"Name","value":"childrenIds"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"views"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"children"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ResolvedOrganizerFolder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OrganizerContainerResource"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const StartDockerContainerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"StartDockerContainer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PrefixedID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"docker"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"start"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"names"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}}]}}]}}]} as unknown as DocumentNode; +export const StopDockerContainerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"StopDockerContainer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PrefixedID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"docker"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stop"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"names"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}}]}}]}}]} as unknown as DocumentNode; export const LogFilesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LogFiles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFiles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"modifiedAt"}}]}}]}}]} as unknown as DocumentNode; export const LogFileContentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LogFileContent"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"lines"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"startLine"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFile"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}},{"kind":"Argument","name":{"kind":"Name","value":"lines"},"value":{"kind":"Variable","name":{"kind":"Name","value":"lines"}}},{"kind":"Argument","name":{"kind":"Name","value":"startLine"},"value":{"kind":"Variable","name":{"kind":"Name","value":"startLine"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"totalLines"}},{"kind":"Field","name":{"kind":"Name","value":"startLine"}}]}}]}}]} as unknown as DocumentNode; export const LogFileSubscriptionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"LogFileSubscription"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"path"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logFile"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"path"},"value":{"kind":"Variable","name":{"kind":"Name","value":"path"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"path"}},{"kind":"Field","name":{"kind":"Name","value":"content"}},{"kind":"Field","name":{"kind":"Name","value":"totalLines"}}]}}]}}]} as unknown as DocumentNode; From 693f4913f6854595c2f475e0bd5c2fe00f1463c2 Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Fri, 19 Sep 2025 11:31:19 -0400 Subject: [PATCH 005/127] implement pause / resume --- .../graph/resolvers/docker/docker.model.ts | 19 +++ .../docker/docker.mutations.resolver.ts | 16 ++ .../graph/resolvers/docker/docker.service.ts | 56 +++++++ .../Docker/DockerContainersTable.vue | 152 ++++++++++++++++++ .../Docker/docker-pause-container.mutation.ts | 13 ++ .../docker-unpause-container.mutation.ts | 13 ++ web/src/composables/gql/graphql.ts | 15 ++ 7 files changed, 284 insertions(+) create mode 100644 web/src/components/Docker/docker-pause-container.mutation.ts create mode 100644 web/src/components/Docker/docker-unpause-container.mutation.ts diff --git a/api/src/unraid-api/graph/resolvers/docker/docker.model.ts b/api/src/unraid-api/graph/resolvers/docker/docker.model.ts index 6380cd5357..ff00c3211c 100644 --- a/api/src/unraid-api/graph/resolvers/docker/docker.model.ts +++ b/api/src/unraid-api/graph/resolvers/docker/docker.model.ts @@ -1,8 +1,11 @@ import { Field, ID, Int, ObjectType, registerEnumType } from '@nestjs/graphql'; +import { type Layout } from '@jsonforms/core'; import { Node } from '@unraid/shared/graphql.model.js'; import { GraphQLBigInt, GraphQLJSON, GraphQLPort } from 'graphql-scalars'; +import { DataSlice } from '@app/unraid-api/types/json-forms.js'; + export enum ContainerPortType { TCP = 'TCP', UDP = 'UDP', @@ -29,6 +32,7 @@ export class ContainerPort { export enum ContainerState { RUNNING = 'RUNNING', + PAUSED = 'PAUSED', EXITED = 'EXITED', } @@ -172,3 +176,18 @@ export class Docker extends Node { @Field(() => [DockerNetwork]) networks!: DockerNetwork[]; } + +@ObjectType() +export class DockerContainerOverviewForm { + @Field(() => ID) + id!: string; + + @Field(() => GraphQLJSON) + dataSchema!: { properties: DataSlice; type: 'object' }; + + @Field(() => GraphQLJSON) + uiSchema!: Layout; + + @Field(() => GraphQLJSON) + data!: Record; +} diff --git a/api/src/unraid-api/graph/resolvers/docker/docker.mutations.resolver.ts b/api/src/unraid-api/graph/resolvers/docker/docker.mutations.resolver.ts index a1de7e0dbc..863a1b3efa 100644 --- a/api/src/unraid-api/graph/resolvers/docker/docker.mutations.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/docker/docker.mutations.resolver.ts @@ -32,4 +32,20 @@ export class DockerMutationsResolver { public async stop(@Args('id', { type: () => PrefixedID }) id: string) { return this.dockerService.stop(id); } + @ResolveField(() => DockerContainer, { description: 'Pause (Suspend) a container' }) + @UsePermissions({ + action: AuthAction.UPDATE_ANY, + resource: Resource.DOCKER, + }) + public async pause(@Args('id', { type: () => PrefixedID }) id: string) { + return this.dockerService.pause(id); + } + @ResolveField(() => DockerContainer, { description: 'Unpause (Resume) a container' }) + @UsePermissions({ + action: AuthAction.UPDATE_ANY, + resource: Resource.DOCKER, + }) + public async unpause(@Args('id', { type: () => PrefixedID }) id: string) { + return this.dockerService.unpause(id); + } } diff --git a/api/src/unraid-api/graph/resolvers/docker/docker.service.ts b/api/src/unraid-api/graph/resolvers/docker/docker.service.ts index 5b244773f6..4599b3e886 100644 --- a/api/src/unraid-api/graph/resolvers/docker/docker.service.ts +++ b/api/src/unraid-api/graph/resolvers/docker/docker.service.ts @@ -243,4 +243,60 @@ export class DockerService { await pubsub.publish(PUBSUB_CHANNEL.INFO, appInfo); return updatedContainer; } + + public async pause(id: string): Promise { + const container = this.client.getContainer(id); + await container.pause(); + await this.cacheManager.del(DockerService.CONTAINER_CACHE_KEY); + this.logger.debug(`Invalidated container cache after pausing ${id}`); + + let containers = await this.getContainers({ skipCache: true }); + let updatedContainer: DockerContainer | undefined; + for (let i = 0; i < 5; i++) { + await sleep(500); + containers = await this.getContainers({ skipCache: true }); + updatedContainer = containers.find((c) => c.id === id); + this.logger.debug( + `Container ${id} state after pause attempt ${i + 1}: ${updatedContainer?.state}` + ); + if (updatedContainer?.state === ContainerState.PAUSED) { + break; + } + } + + if (!updatedContainer) { + throw new Error(`Container ${id} not found after pausing`); + } + const appInfo = await this.getAppInfo(); + await pubsub.publish(PUBSUB_CHANNEL.INFO, appInfo); + return updatedContainer; + } + + public async unpause(id: string): Promise { + const container = this.client.getContainer(id); + await container.unpause(); + await this.cacheManager.del(DockerService.CONTAINER_CACHE_KEY); + this.logger.debug(`Invalidated container cache after unpausing ${id}`); + + let containers = await this.getContainers({ skipCache: true }); + let updatedContainer: DockerContainer | undefined; + for (let i = 0; i < 5; i++) { + await sleep(500); + containers = await this.getContainers({ skipCache: true }); + updatedContainer = containers.find((c) => c.id === id); + this.logger.debug( + `Container ${id} state after unpause attempt ${i + 1}: ${updatedContainer?.state}` + ); + if (updatedContainer?.state === ContainerState.RUNNING) { + break; + } + } + + if (!updatedContainer) { + throw new Error(`Container ${id} not found after unpausing`); + } + const appInfo = await this.getAppInfo(); + await pubsub.publish(PUBSUB_CHANNEL.INFO, appInfo); + return updatedContainer; + } } diff --git a/web/src/components/Docker/DockerContainersTable.vue b/web/src/components/Docker/DockerContainersTable.vue index 5e08f1e315..a3d39045a8 100644 --- a/web/src/components/Docker/DockerContainersTable.vue +++ b/web/src/components/Docker/DockerContainersTable.vue @@ -7,9 +7,11 @@ import { GET_DOCKER_CONTAINERS } from '@/components/Docker/docker-containers.que import { CREATE_DOCKER_FOLDER } from '@/components/Docker/docker-create-folder.mutation'; import { DELETE_DOCKER_ENTRIES } from '@/components/Docker/docker-delete-entries.mutation'; import { MOVE_DOCKER_ENTRIES_TO_FOLDER } from '@/components/Docker/docker-move-entries.mutation'; +import { PAUSE_DOCKER_CONTAINER } from '@/components/Docker/docker-pause-container.mutation'; import { SET_DOCKER_FOLDER_CHILDREN } from '@/components/Docker/docker-set-folder-children.mutation'; import { START_DOCKER_CONTAINER } from '@/components/Docker/docker-start-container.mutation'; import { STOP_DOCKER_CONTAINER } from '@/components/Docker/docker-stop-container.mutation'; +import { UNPAUSE_DOCKER_CONTAINER } from '@/components/Docker/docker-unpause-container.mutation'; import { ContainerState } from '@/composables/gql/graphql'; import type { @@ -231,6 +233,7 @@ const columns = computed[]>(() => { const isBusy = busyRowIds.value.has(row.original.id); const color = { [ContainerState.RUNNING]: 'success' as const, + [ContainerState.PAUSED]: 'warning' as const, [ContainerState.EXITED]: 'neutral' as const, }[state]; if (isBusy) { @@ -324,6 +327,8 @@ const { mutate: deleteEntriesMutation, loading: deleting } = useMutation(DELETE_ const { mutate: setFolderChildrenMutation } = useMutation(SET_DOCKER_FOLDER_CHILDREN); const { mutate: startContainerMutation } = useMutation(START_DOCKER_CONTAINER); const { mutate: stopContainerMutation } = useMutation(STOP_DOCKER_CONTAINER); +const { mutate: pauseContainerMutation } = useMutation(PAUSE_DOCKER_CONTAINER); +const { mutate: unpauseContainerMutation } = useMutation(UNPAUSE_DOCKER_CONTAINER); const moveOpen = ref(false); const selectedFolderId = ref(''); @@ -430,6 +435,118 @@ async function handleRowStartStop(row: TreeRow) { } } +// Pause/Resume single row +async function handleRowPauseResume(row: TreeRow) { + if (row.type !== 'container') return; + const containerId = row.containerId || row.id; + if (!containerId) return; + setRowsBusy([row.id], true); + try { + const isPaused = row.state === ContainerState.PAUSED; + const mutate = isPaused ? unpauseContainerMutation : pauseContainerMutation; + await mutate( + { id: containerId }, + { + refetchQueries: [{ query: GET_DOCKER_CONTAINERS, variables: { skipCache: true } }], + awaitRefetchQueries: true, + } + ); + } finally { + setRowsBusy([row.id], false); + } +} + +// Bulk Pause/Resume handling with mixed confirmation +const confirmPauseResumeOpen = ref(false); +const confirmToPause = ref<{ name: string }[]>([]); +const confirmToResume = ref<{ name: string }[]>([]); +let pendingPauseResumeIds: string[] = []; + +function classifyPauseResume(ids: string[]) { + const toPause: { id: string; containerId: string; name: string }[] = []; + const toResume: { id: string; containerId: string; name: string }[] = []; + for (const id of ids) { + const row = getRowById(id); + if (!row || row.type !== 'container') continue; + const containerId = row.containerId || row.id; + const state = row.state as string | undefined; + const name = row.name; + if (state === ContainerState.PAUSED) toResume.push({ id, containerId, name }); + else if (state === ContainerState.RUNNING) toPause.push({ id, containerId, name }); + } + return { toPause, toResume }; +} + +async function runPauseResumeBatch( + toPause: { id: string; containerId: string; name: string }[], + toResume: { id: string; containerId: string; name: string }[] +) { + const totalOps = toPause.length + toResume.length; + let completed = 0; + for (const item of toPause) { + completed++; + const isLast = completed === totalOps; + await pauseContainerMutation( + { id: item.containerId }, + isLast + ? { + refetchQueries: [{ query: GET_DOCKER_CONTAINERS, variables: { skipCache: true } }], + awaitRefetchQueries: true, + } + : { awaitRefetchQueries: false } + ); + } + for (const item of toResume) { + completed++; + const isLast = completed === totalOps; + await unpauseContainerMutation( + { id: item.containerId }, + isLast + ? { + refetchQueries: [{ query: GET_DOCKER_CONTAINERS, variables: { skipCache: true } }], + awaitRefetchQueries: true, + } + : { awaitRefetchQueries: false } + ); + } +} + +function openPauseResume(ids?: string[]) { + const sources = ids ?? getSelectedContainerIds(); + if (sources.length === 0) return; + const { toPause, toResume } = classifyPauseResume(sources); + const isMixed = toPause.length > 0 && toResume.length > 0; + if (isMixed) { + pendingPauseResumeIds = sources; + confirmToPause.value = toPause.map((i) => ({ name: i.name })); + confirmToResume.value = toResume.map((i) => ({ name: i.name })); + confirmPauseResumeOpen.value = true; + return; + } + setRowsBusy(sources, true); + runPauseResumeBatch(toPause, toResume) + .then(() => showToast('Action completed')) + .finally(() => { + setRowsBusy(sources, false); + rowSelection.value = {}; + }); +} + +async function confirmPauseResume(close: () => void) { + const { toPause, toResume } = classifyPauseResume(pendingPauseResumeIds); + setRowsBusy(pendingPauseResumeIds, true); + try { + await runPauseResumeBatch(toPause, toResume); + showToast('Action completed'); + rowSelection.value = {}; + } finally { + setRowsBusy(pendingPauseResumeIds, false); + confirmPauseResumeOpen.value = false; + pendingPauseResumeIds = []; + close(); + } +} + // Bulk Start/Stop handling with mixed confirmation const confirmStartStopOpen = ref(false); const confirmToStart = ref<{ name: string }[]>([]); @@ -696,6 +813,10 @@ function handleBulkAction(action: string) { openStartStop(ids); return; } + if (action === 'Pause / Resume') { + openPauseResume(ids); + return; + } showToast(`${action} (${ids.length})`); } @@ -714,6 +835,10 @@ function handleRowAction(row: TreeRow, action: string) { handleRowStartStop(row); return; } + if (action === 'Pause / Resume') { + handleRowPauseResume(row); + return; + } showToast(`${action}: ${row.name}`); } @@ -943,5 +1068,32 @@ function getRowActionItems(row: TreeRow): DropdownMenuItems { Confirm + + + + + diff --git a/web/src/components/Docker/docker-pause-container.mutation.ts b/web/src/components/Docker/docker-pause-container.mutation.ts new file mode 100644 index 0000000000..6209372794 --- /dev/null +++ b/web/src/components/Docker/docker-pause-container.mutation.ts @@ -0,0 +1,13 @@ +import { gql } from '@apollo/client'; + +export const PAUSE_DOCKER_CONTAINER = gql` + mutation PauseDockerContainer($id: PrefixedID!) { + docker { + pause(id: $id) { + id + names + state + } + } + } +`; diff --git a/web/src/components/Docker/docker-unpause-container.mutation.ts b/web/src/components/Docker/docker-unpause-container.mutation.ts new file mode 100644 index 0000000000..1e01553cb5 --- /dev/null +++ b/web/src/components/Docker/docker-unpause-container.mutation.ts @@ -0,0 +1,13 @@ +import { gql } from '@apollo/client'; + +export const UNPAUSE_DOCKER_CONTAINER = gql` + mutation UnpauseDockerContainer($id: PrefixedID!) { + docker { + unpause(id: $id) { + id + names + state + } + } + } +`; diff --git a/web/src/composables/gql/graphql.ts b/web/src/composables/gql/graphql.ts index 52ebe1f710..4084769213 100644 --- a/web/src/composables/gql/graphql.ts +++ b/web/src/composables/gql/graphql.ts @@ -525,6 +525,7 @@ export enum ContainerPortType { export enum ContainerState { EXITED = 'EXITED', + PAUSED = 'PAUSED', RUNNING = 'RUNNING' } @@ -732,10 +733,19 @@ export type DockerContainerOverviewForm = { export type DockerMutations = { __typename?: 'DockerMutations'; + /** Pause (Suspend) a container */ + pause: DockerContainer; /** Start a container */ start: DockerContainer; /** Stop a container */ stop: DockerContainer; + /** Unpause (Resume) a container */ + unpause: DockerContainer; +}; + + +export type DockerMutationsPauseArgs = { + id: Scalars['PrefixedID']['input']; }; @@ -748,6 +758,11 @@ export type DockerMutationsStopArgs = { id: Scalars['PrefixedID']['input']; }; + +export type DockerMutationsUnpauseArgs = { + id: Scalars['PrefixedID']['input']; +}; + export type DockerNetwork = Node & { __typename?: 'DockerNetwork'; attachable: Scalars['Boolean']['output']; From 97373c14b1c9749facd707ebabe5cfc7fe4d117d Mon Sep 17 00:00:00 2001 From: Pujit Mehrotra Date: Fri, 19 Sep 2025 12:03:54 -0400 Subject: [PATCH 006/127] implement column visibility customization --- .../Docker/DockerContainersTable.vue | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/web/src/components/Docker/DockerContainersTable.vue b/web/src/components/Docker/DockerContainersTable.vue index a3d39045a8..8102e9e23f 100644 --- a/web/src/components/Docker/DockerContainersTable.vue +++ b/web/src/components/Docker/DockerContainersTable.vue @@ -1,5 +1,5 @@ + + diff --git a/web/src/components/Docker/DockerContainerOverview.standalone.vue b/web/src/components/Docker/DockerContainerOverview.standalone.vue index af60068deb..f99fa1c7bd 100644 --- a/web/src/components/Docker/DockerContainerOverview.standalone.vue +++ b/web/src/components/Docker/DockerContainerOverview.standalone.vue @@ -1,4 +1,5 @@