diff --git a/app/docker/components/datatables/tasks-datatable/tasksDatatable.html b/app/docker/components/datatables/tasks-datatable/tasksDatatable.html
deleted file mode 100644
index fed57be91..000000000
--- a/app/docker/components/datatables/tasks-datatable/tasksDatatable.html
+++ /dev/null
@@ -1,133 +0,0 @@
-
diff --git a/app/docker/components/datatables/tasks-datatable/tasksDatatable.js b/app/docker/components/datatables/tasks-datatable/tasksDatatable.js
deleted file mode 100644
index 4bdbda8c6..000000000
--- a/app/docker/components/datatables/tasks-datatable/tasksDatatable.js
+++ /dev/null
@@ -1,16 +0,0 @@
-angular.module('portainer.docker').component('tasksDatatable', {
- templateUrl: './tasksDatatable.html',
- controller: 'TasksDatatableController',
- bindings: {
- titleText: '@',
- titleIcon: '@',
- dataset: '<',
- tableKey: '@',
- orderBy: '@',
- reverseOrder: '<',
- nodes: '<',
- showSlotColumn: '<',
- showLogsButton: '<',
- agentProxy: '<',
- },
-});
diff --git a/app/docker/components/datatables/tasks-datatable/tasksDatatableController.js b/app/docker/components/datatables/tasks-datatable/tasksDatatableController.js
deleted file mode 100644
index 38c3098f9..000000000
--- a/app/docker/components/datatables/tasks-datatable/tasksDatatableController.js
+++ /dev/null
@@ -1,49 +0,0 @@
-angular.module('portainer.docker').controller('TasksDatatableController', [
- '$scope',
- '$controller',
- 'DatatableService',
- function ($scope, $controller, DatatableService) {
- angular.extend(this, $controller('GenericDatatableController', { $scope: $scope }));
-
- this.state = Object.assign(this.state, {
- showQuickActionStats: true,
- showQuickActionLogs: true,
- showQuickActionExec: true,
- showQuickActionInspect: true,
- showQuickActionAttach: false,
- });
-
- this.$onInit = function () {
- this.setDefaults();
- this.prepareTableFromDataset();
-
- this.state.orderBy = this.orderBy;
- var storedOrder = DatatableService.getDataTableOrder(this.tableKey);
- if (storedOrder !== null) {
- this.state.reverseOrder = storedOrder.reverse;
- this.state.orderBy = storedOrder.orderBy;
- }
-
- var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
- if (textFilter !== null) {
- this.state.textFilter = textFilter;
- this.onTextFilterChange();
- }
-
- var storedFilters = DatatableService.getDataTableFilters(this.tableKey);
- if (storedFilters !== null) {
- this.filters = storedFilters;
- }
- if (this.filters && this.filters.state) {
- this.filters.state.open = false;
- }
-
- var storedSettings = DatatableService.getDataTableSettings(this.tableKey);
- if (storedSettings !== null) {
- this.settings = storedSettings;
- this.settings.open = false;
- }
- this.onSettingsRepeaterChange();
- };
- },
-]);
diff --git a/app/docker/react/components/index.ts b/app/docker/react/components/index.ts
index a76d7e2a8..34e2a4d25 100644
--- a/app/docker/react/components/index.ts
+++ b/app/docker/react/components/index.ts
@@ -3,7 +3,6 @@ import angular from 'angular';
import { r2a } from '@/react-tools/react2angular';
import { withControlledInput } from '@/react-tools/withControlledInput';
import { StackContainersDatatable } from '@/react/common/stacks/ItemView/StackContainersDatatable';
-import { ContainerQuickActions } from '@/react/docker/containers/components/ContainerQuickActions';
import { TemplateListDropdownAngular } from '@/react/docker/app-templates/TemplateListDropdown';
import { TemplateListSortAngular } from '@/react/docker/app-templates/TemplateListSort';
import { withCurrentUser } from '@/react-tools/withCurrentUser';
@@ -40,15 +39,6 @@ const ngModule = angular
])
.component('dockerfileDetails', r2a(DockerfileDetails, ['image']))
.component('dockerHealthStatus', r2a(HealthStatus, ['health']))
- .component(
- 'containerQuickActions',
- r2a(withUIRouter(withCurrentUser(ContainerQuickActions)), [
- 'containerId',
- 'nodeName',
- 'state',
- 'status',
- ])
- )
.component('templateListDropdown', TemplateListDropdownAngular)
.component('templateListSort', TemplateListSortAngular)
.component(
diff --git a/app/docker/react/components/services.ts b/app/docker/react/components/services.ts
index 046095038..ebac9b128 100644
--- a/app/docker/react/components/services.ts
+++ b/app/docker/react/components/services.ts
@@ -2,22 +2,18 @@ import angular from 'angular';
import { r2a } from '@/react-tools/react2angular';
import { withUIRouter } from '@/react-tools/withUIRouter';
-import { TasksDatatable } from '@/react/docker/services/ListView/ServicesDatatable/TasksDatatable';
import { withCurrentUser } from '@/react-tools/withCurrentUser';
-import { TaskTableQuickActions } from '@/react/docker/services/common/TaskTableQuickActions';
import { ServicesDatatable } from '@/react/docker/services/ListView/ServicesDatatable';
+import { TasksDatatable } from '@/react/docker/services/ItemView/TasksDatatable';
export const servicesModule = angular
.module('portainer.docker.react.components.services', [])
.component(
'dockerServiceTasksDatatable',
- r2a(withUIRouter(withCurrentUser(TasksDatatable)), ['dataset', 'search'])
- )
- .component(
- 'dockerTaskTableQuickActions',
- r2a(withUIRouter(withCurrentUser(TaskTableQuickActions)), [
- 'state',
- 'taskId',
+ r2a(withUIRouter(withCurrentUser(TasksDatatable)), [
+ 'serviceName',
+ 'dataset',
+ 'isSlotColumnVisible',
])
)
.component(
diff --git a/app/docker/views/services/edit/includes/tasks.html b/app/docker/views/services/edit/includes/tasks.html
index 18d5ab105..8a64277d4 100644
--- a/app/docker/views/services/edit/includes/tasks.html
+++ b/app/docker/views/services/edit/includes/tasks.html
@@ -1,14 +1,6 @@
-
-
-
+
diff --git a/app/docker/views/services/edit/service.html b/app/docker/views/services/edit/service.html
index dc341ca70..712c9da19 100644
--- a/app/docker/views/services/edit/service.html
+++ b/app/docker/views/services/edit/service.html
@@ -253,6 +253,7 @@
-
+
+
diff --git a/app/react/docker/services/ItemView/.keep b/app/react/docker/services/ItemView/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/app/react/docker/services/ItemView/TasksDatatable/TasksDatatable.tsx b/app/react/docker/services/ItemView/TasksDatatable/TasksDatatable.tsx
new file mode 100644
index 000000000..f2c0334f0
--- /dev/null
+++ b/app/react/docker/services/ItemView/TasksDatatable/TasksDatatable.tsx
@@ -0,0 +1,37 @@
+import { List } from 'lucide-react';
+
+import { Datatable } from '@@/datatables';
+import { createPersistedStore } from '@@/datatables/types';
+import { useTableState } from '@@/datatables/useTableState';
+import { withMeta } from '@@/datatables/extend-options/withMeta';
+
+import { useColumns } from './columns';
+import { DecoratedTask } from './types';
+
+const storageKey = 'docker-service-tasks';
+const store = createPersistedStore(storageKey);
+
+export function TasksDatatable({
+ dataset,
+ isSlotColumnVisible,
+ serviceName,
+}: {
+ dataset: DecoratedTask[];
+ isSlotColumnVisible: boolean;
+ serviceName: string;
+}) {
+ const tableState = useTableState(store, storageKey);
+ const columns = useColumns(isSlotColumnVisible);
+
+ return (
+
+ );
+}
diff --git a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/actions.tsx b/app/react/docker/services/ItemView/TasksDatatable/columns/actions.tsx
similarity index 97%
rename from app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/actions.tsx
rename to app/react/docker/services/ItemView/TasksDatatable/columns/actions.tsx
index fe672dc74..1f153c5f2 100644
--- a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/actions.tsx
+++ b/app/react/docker/services/ItemView/TasksDatatable/columns/actions.tsx
@@ -24,7 +24,7 @@ function Cell({
return null;
}
const state: QuickActionsState = {
- showQuickActionAttach: true,
+ showQuickActionAttach: false,
showQuickActionExec: true,
showQuickActionInspect: true,
showQuickActionLogs: true,
diff --git a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/helper.ts b/app/react/docker/services/ItemView/TasksDatatable/columns/helper.ts
similarity index 100%
rename from app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/helper.ts
rename to app/react/docker/services/ItemView/TasksDatatable/columns/helper.ts
diff --git a/app/react/docker/services/ItemView/TasksDatatable/columns/index.ts b/app/react/docker/services/ItemView/TasksDatatable/columns/index.ts
new file mode 100644
index 000000000..cf6b1f5e3
--- /dev/null
+++ b/app/react/docker/services/ItemView/TasksDatatable/columns/index.ts
@@ -0,0 +1,24 @@
+import { useMemo } from 'react';
+import _ from 'lodash';
+
+import { actions } from './actions';
+import { node } from './node';
+import { slot } from './slot';
+import { status } from './status';
+import { task } from './task';
+import { updated } from './updated';
+
+export function useColumns(isSlotColumnsVisible = true) {
+ return useMemo(
+ () =>
+ _.compact([
+ status,
+ task,
+ actions,
+ isSlotColumnsVisible && slot,
+ node,
+ updated,
+ ]),
+ [isSlotColumnsVisible]
+ );
+}
diff --git a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/node.tsx b/app/react/docker/services/ItemView/TasksDatatable/columns/node.tsx
similarity index 100%
rename from app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/node.tsx
rename to app/react/docker/services/ItemView/TasksDatatable/columns/node.tsx
diff --git a/app/react/docker/services/ItemView/TasksDatatable/columns/slot.tsx b/app/react/docker/services/ItemView/TasksDatatable/columns/slot.tsx
new file mode 100644
index 000000000..dd0916475
--- /dev/null
+++ b/app/react/docker/services/ItemView/TasksDatatable/columns/slot.tsx
@@ -0,0 +1,5 @@
+import { columnHelper } from './helper';
+
+export const slot = columnHelper.accessor((item) => item.Slot || '-', {
+ header: 'Slot',
+});
diff --git a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/status.tsx b/app/react/docker/services/ItemView/TasksDatatable/columns/status.tsx
similarity index 100%
rename from app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/status.tsx
rename to app/react/docker/services/ItemView/TasksDatatable/columns/status.tsx
diff --git a/app/react/docker/services/ItemView/TasksDatatable/columns/task.tsx b/app/react/docker/services/ItemView/TasksDatatable/columns/task.tsx
new file mode 100644
index 000000000..5defb3116
--- /dev/null
+++ b/app/react/docker/services/ItemView/TasksDatatable/columns/task.tsx
@@ -0,0 +1,51 @@
+import { CellContext } from '@tanstack/react-table';
+
+import { useCurrentEnvironment } from '@/react/hooks/useCurrentEnvironment';
+import { isAgentEnvironment } from '@/react/portainer/environments/utils';
+
+import { Link } from '@@/Link';
+
+import { DecoratedTask } from '../types';
+import { getTableMeta } from '../meta';
+
+import { columnHelper } from './helper';
+
+export const task = columnHelper.accessor('Id', {
+ header: 'Id',
+ cell: Cell,
+});
+
+function Cell({
+ getValue,
+ row: { original: item },
+ table: {
+ options: { meta },
+ },
+}: CellContext) {
+ const environmentQuery = useCurrentEnvironment();
+
+ if (!environmentQuery.data) {
+ return null;
+ }
+
+ const { serviceName } = getTableMeta(meta);
+
+ const value = getValue();
+ const isAgent = isAgentEnvironment(environmentQuery.data.Type);
+
+ const name = `${serviceName}${item.Slot ? `.${item.Slot}` : ''}.${value}`;
+
+ return isAgent && item.Container ? (
+
+ {name}
+
+ ) : (
+
+ {name}
+
+ );
+}
diff --git a/app/react/docker/services/ItemView/TasksDatatable/columns/updated.tsx b/app/react/docker/services/ItemView/TasksDatatable/columns/updated.tsx
new file mode 100644
index 000000000..026efc658
--- /dev/null
+++ b/app/react/docker/services/ItemView/TasksDatatable/columns/updated.tsx
@@ -0,0 +1,8 @@
+import { isoDate } from '@/portainer/filters/filters';
+
+import { columnHelper } from './helper';
+
+export const updated = columnHelper.accessor('Updated', {
+ header: 'Last Update',
+ cell: ({ getValue }) => isoDate(getValue()),
+});
diff --git a/app/react/docker/services/ItemView/TasksDatatable/index.ts b/app/react/docker/services/ItemView/TasksDatatable/index.ts
new file mode 100644
index 000000000..f95acf3ac
--- /dev/null
+++ b/app/react/docker/services/ItemView/TasksDatatable/index.ts
@@ -0,0 +1 @@
+export { TasksDatatable } from './TasksDatatable';
diff --git a/app/react/docker/services/ItemView/TasksDatatable/meta.ts b/app/react/docker/services/ItemView/TasksDatatable/meta.ts
new file mode 100644
index 000000000..08ac07861
--- /dev/null
+++ b/app/react/docker/services/ItemView/TasksDatatable/meta.ts
@@ -0,0 +1,17 @@
+type TableMeta = {
+ serviceName: string;
+ table: 'tasks';
+};
+
+export function getTableMeta(meta: unknown): TableMeta {
+ return isTableMeta(meta) ? meta : { table: 'tasks', serviceName: '' };
+}
+
+function isTableMeta(meta: unknown): meta is TableMeta {
+ return (
+ !!meta &&
+ typeof meta === 'object' &&
+ 'table' in meta &&
+ meta.table === 'tasks'
+ );
+}
diff --git a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/types.ts b/app/react/docker/services/ItemView/TasksDatatable/types.ts
similarity index 100%
rename from app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/types.ts
rename to app/react/docker/services/ItemView/TasksDatatable/types.ts
diff --git a/app/react/docker/services/ListView/ServicesDatatable/ServicesDatatable.tsx b/app/react/docker/services/ListView/ServicesDatatable/ServicesDatatable.tsx
index 843b70292..776397533 100644
--- a/app/react/docker/services/ListView/ServicesDatatable/ServicesDatatable.tsx
+++ b/app/react/docker/services/ListView/ServicesDatatable/ServicesDatatable.tsx
@@ -19,6 +19,8 @@ import { getColumnVisibilityState } from '@@/datatables/ColumnVisibilityMenu';
import { mergeOptions } from '@@/datatables/extend-options/mergeOptions';
import { withGlobalFilter } from '@@/datatables/extend-options/withGlobalFilter';
+import { DecoratedTask } from '../../ItemView/TasksDatatable/types';
+
import { useColumns } from './columns';
import { TasksDatatable } from './TasksDatatable';
import { TableActions } from './TableActions';
@@ -72,7 +74,10 @@ export function ServicesDatatable({
|
-
+ }
+ search={tableState.search}
+ />
|
)}
diff --git a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/TasksDatatable.tsx b/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/TasksDatatable.tsx
index 8f1e906fd..ce264eba2 100644
--- a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/TasksDatatable.tsx
+++ b/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/TasksDatatable.tsx
@@ -1,7 +1,15 @@
+import { DecoratedTask } from '@/react/docker/services/ItemView/TasksDatatable/types';
+import { status } from '@/react/docker/services/ItemView/TasksDatatable/columns/status';
+import { actions } from '@/react/docker/services/ItemView/TasksDatatable/columns/actions';
+import { slot } from '@/react/docker/services/ItemView/TasksDatatable/columns/slot';
+import { node } from '@/react/docker/services/ItemView/TasksDatatable/columns/node';
+import { updated } from '@/react/docker/services/ItemView/TasksDatatable/columns/updated';
+
import { NestedDatatable } from '@@/datatables/NestedDatatable';
-import { columns } from './columns';
-import { DecoratedTask } from './types';
+import { task } from './task-column';
+
+const columns = [status, task, actions, slot, node, updated];
export function TasksDatatable({
dataset,
diff --git a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/index.ts b/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/index.ts
deleted file mode 100644
index aaca2fa3f..000000000
--- a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/index.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { isoDate } from '@/portainer/filters/filters';
-
-import { actions } from './actions';
-import { columnHelper } from './helper';
-import { node } from './node';
-import { status } from './status';
-import { task } from './task';
-
-export const columns = [
- status,
- task,
- actions,
- columnHelper.accessor((item) => item.Slot || '-', { header: 'Slot' }),
- node,
- columnHelper.accessor('Updated', {
- header: 'Last Update',
- cell: ({ getValue }) => isoDate(getValue()),
- }),
-];
diff --git a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/task.tsx b/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/task-column.tsx
similarity index 84%
rename from app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/task.tsx
rename to app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/task-column.tsx
index 1a94f5947..8a3db86d7 100644
--- a/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/columns/task.tsx
+++ b/app/react/docker/services/ListView/ServicesDatatable/TasksDatatable/task-column.tsx
@@ -2,13 +2,11 @@ import { CellContext } from '@tanstack/react-table';
import { useCurrentEnvironment } from '@/react/hooks/useCurrentEnvironment';
import { isAgentEnvironment } from '@/react/portainer/environments/utils';
+import { DecoratedTask } from '@/react/docker/services/ItemView/TasksDatatable/types';
+import { columnHelper } from '@/react/docker/services/ItemView/TasksDatatable/columns/helper';
import { Link } from '@@/Link';
-import { DecoratedTask } from '../types';
-
-import { columnHelper } from './helper';
-
export const task = columnHelper.accessor('Id', {
header: 'Task',
cell: Cell,