diff --git a/app/kubernetes/components/datatables/integrated-applications-datatable/integratedApplicationsDatatable.html b/app/kubernetes/components/datatables/integrated-applications-datatable/integratedApplicationsDatatable.html
deleted file mode 100644
index 14ba7f01d..000000000
--- a/app/kubernetes/components/datatables/integrated-applications-datatable/integratedApplicationsDatatable.html
+++ /dev/null
@@ -1,131 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- |
-
-
- |
-
-
- |
-
-
-
-
- {{ item.Name }} |
- {{ item.StackName || '-' }} |
- {{ item.Image | truncate: 64 }} |
-
-
- Loading... |
-
-
- No application available. |
-
-
-
-
-
-
-
-
diff --git a/app/kubernetes/components/datatables/integrated-applications-datatable/integratedApplicationsDatatable.js b/app/kubernetes/components/datatables/integrated-applications-datatable/integratedApplicationsDatatable.js
deleted file mode 100644
index 35729102f..000000000
--- a/app/kubernetes/components/datatables/integrated-applications-datatable/integratedApplicationsDatatable.js
+++ /dev/null
@@ -1,13 +0,0 @@
-angular.module('portainer.kubernetes').component('kubernetesIntegratedApplicationsDatatable', {
- templateUrl: './integratedApplicationsDatatable.html',
- controller: 'GenericDatatableController',
- bindings: {
- titleText: '@',
- titleIcon: '@',
- dataset: '<',
- tableKey: '@',
- orderBy: '@',
- reverseOrder: '<',
- refreshCallback: '<',
- },
-});
diff --git a/app/kubernetes/models/application/models/Application.ts b/app/kubernetes/models/application/models/Application.ts
new file mode 100644
index 000000000..05095675c
--- /dev/null
+++ b/app/kubernetes/models/application/models/Application.ts
@@ -0,0 +1,123 @@
+import { ServiceType } from '@/react/kubernetes/applications/CreateView/application-services/types';
+import { AppType, DeploymentType } from '@/react/kubernetes/applications/types';
+
+import { ConfigurationVolume } from './ConfigurationVolume';
+import { PersistedFolder } from './PersistedFolder';
+
+export class Application {
+ Id: string;
+
+ Name: string;
+
+ StackName: string;
+
+ StackId: string;
+
+ ApplicationKind: string;
+
+ ApplicationOwner: string;
+
+ ApplicationName: string;
+
+ Annotations: Record = {};
+
+ ResourcePool: string;
+
+ Image: string;
+
+ CreationDate: 0;
+
+ Pods: [];
+
+ Containers: [];
+
+ Metadata: {
+ labels?: Record;
+ annotations?: Record;
+ };
+
+ Limits: {
+ Cpu?: number;
+ Memory?: number;
+ };
+
+ ServiceType?: ServiceType;
+
+ ServiceId: string;
+
+ ServiceName: string;
+
+ HeadlessServiceName: undefined; // only used for StatefulSet
+
+ LoadBalancerIPAddress: undefined; // only filled when bound service is LoadBalancer and state is available
+
+ PublishedPorts: [];
+
+ Volumes: [];
+
+ Env: [];
+
+ PersistedFolders: Array;
+
+ ConfigurationVolumes: Array;
+
+ DeploymentType?: DeploymentType;
+
+ DataAccessPolicy: 'Unknown';
+
+ ApplicationType?: AppType;
+
+ RunningPodsCount: 0;
+
+ TotalPodsCount: 0;
+
+ Yaml: string;
+
+ Note: string;
+
+ Raw: undefined; // only filled when inspecting app details / create / edit view (never filled in multiple-apps views)
+
+ AutoScaler: undefined; // only filled if the application has an HorizontalPodAutoScaler bound to it
+
+ Conditions: Array<{
+ lastTransitionTime: string;
+ lastUpdateTime: string;
+ message: string;
+ reason: string;
+ status: string;
+ type: string;
+ }> = [];
+
+ constructor() {
+ this.Id = '';
+ this.Name = '';
+ this.StackName = '';
+ this.StackId = '';
+ this.ApplicationKind = '';
+ this.ApplicationOwner = '';
+ this.ApplicationName = '';
+ this.ResourcePool = '';
+ this.Image = '';
+ this.CreationDate = 0;
+ this.Pods = [];
+ this.Containers = [];
+ this.Metadata = {};
+ this.Limits = {};
+ this.ServiceId = '';
+ this.ServiceName = '';
+ this.HeadlessServiceName = undefined;
+ this.LoadBalancerIPAddress = undefined;
+ this.PublishedPorts = [];
+ this.Volumes = [];
+ this.Env = [];
+ this.PersistedFolders = [];
+ this.ConfigurationVolumes = [];
+ this.DataAccessPolicy = 'Unknown';
+ this.RunningPodsCount = 0;
+ this.TotalPodsCount = 0;
+ this.Yaml = '';
+ this.Note = '';
+ this.Raw = undefined;
+ this.AutoScaler = undefined;
+ }
+}
diff --git a/app/kubernetes/models/application/models/ConfigurationVolume.ts b/app/kubernetes/models/application/models/ConfigurationVolume.ts
new file mode 100644
index 000000000..3bc421607
--- /dev/null
+++ b/app/kubernetes/models/application/models/ConfigurationVolume.ts
@@ -0,0 +1,9 @@
+export class ConfigurationVolume {
+ fileMountPath: string = '';
+
+ rootMountPath: string = '';
+
+ configurationKey: string = '';
+
+ configurationName: string = '';
+}
diff --git a/app/kubernetes/models/application/models/PersistedFolder.ts b/app/kubernetes/models/application/models/PersistedFolder.ts
new file mode 100644
index 000000000..bf915d33a
--- /dev/null
+++ b/app/kubernetes/models/application/models/PersistedFolder.ts
@@ -0,0 +1,7 @@
+export class PersistedFolder {
+ MountPath: string = '';
+
+ persistentVolumeClaimName: string = '';
+
+ HostPath: string = '';
+}
diff --git a/app/kubernetes/models/application/models/index.js b/app/kubernetes/models/application/models/index.js
index 1e562c252..20000bd0a 100644
--- a/app/kubernetes/models/application/models/index.js
+++ b/app/kubernetes/models/application/models/index.js
@@ -1,49 +1,8 @@
export * from './constants';
-/**
- * KubernetesApplication Model (Composite)
- */
-const _KubernetesApplication = Object.freeze({
- Id: '',
- Name: '',
- StackName: '',
- StackId: '',
- ApplicationKind: '',
- ApplicationOwner: '',
- ApplicationName: '',
- ResourcePool: '',
- Image: '',
- CreationDate: 0,
- Pods: [],
- Containers: [],
- Metadata: {},
- Limits: {},
- ServiceType: '',
- ServiceId: '',
- ServiceName: '',
- HeadlessServiceName: undefined, // only used for StatefulSet
- LoadBalancerIPAddress: undefined, // only filled when bound service is LoadBalancer and state is available
- PublishedPorts: [],
- Volumes: [],
- Env: [],
- PersistedFolders: [], // KubernetesApplicationPersistedFolder list
- ConfigurationVolumes: [], // KubernetesApplicationConfigurationVolume list
- DeploymentType: 'Unknown',
- DataAccessPolicy: 'Unknown',
- ApplicationType: 'Unknown',
- RunningPodsCount: 0,
- TotalPodsCount: 0,
- Yaml: '',
- Note: '',
- Raw: undefined, // only filled when inspecting app details / create / edit view (never filled in multiple-apps views)
- AutoScaler: undefined, // only filled if the application has an HorizontalPodAutoScaler bound to it
-});
-
-export class KubernetesApplication {
- constructor() {
- Object.assign(this, JSON.parse(JSON.stringify(_KubernetesApplication)));
- }
-}
+export { Application as KubernetesApplication } from './Application';
+export { ConfigurationVolume as KubernetesApplicationConfigurationVolume } from './ConfigurationVolume';
+export { PersistedFolder as KubernetesApplicationPersistedFolder } from './PersistedFolder';
/**
* HelmApplication Model (Composite)
@@ -64,37 +23,6 @@ export class HelmApplication {
}
}
-/**
- * KubernetesApplicationPersistedFolder Model
- */
-const _KubernetesApplicationPersistedFolder = Object.freeze({
- MountPath: '',
- persistentVolumeClaimName: '',
- HostPath: '',
-});
-
-export class KubernetesApplicationPersistedFolder {
- constructor() {
- Object.assign(this, JSON.parse(JSON.stringify(_KubernetesApplicationPersistedFolder)));
- }
-}
-
-/**
- * KubernetesApplicationConfigurationVolume Model
- */
-const _KubernetesApplicationConfigurationVolume = Object.freeze({
- fileMountPath: '',
- rootMountPath: '',
- configurationKey: '',
- configurationName: '',
-});
-
-export class KubernetesApplicationConfigurationVolume {
- constructor() {
- Object.assign(this, JSON.parse(JSON.stringify(_KubernetesApplicationConfigurationVolume)));
- }
-}
-
/**
* KubernetesApplicationPort Model
*/
diff --git a/app/kubernetes/react/components/index.ts b/app/kubernetes/react/components/index.ts
index d8112d849..e2a3e0cda 100644
--- a/app/kubernetes/react/components/index.ts
+++ b/app/kubernetes/react/components/index.ts
@@ -60,6 +60,7 @@ import { AppDeploymentTypeFormSection } from '@/react/kubernetes/applications/co
import { EnvironmentVariablesFormSection } from '@/react/kubernetes/applications/components/EnvironmentVariablesFormSection/EnvironmentVariablesFormSection';
import { kubeEnvVarValidationSchema } from '@/react/kubernetes/applications/components/EnvironmentVariablesFormSection/kubeEnvVarValidationSchema';
import { HelmInsightsBox } from '@/react/kubernetes/applications/ListView/ApplicationsDatatable/HelmInsightsBox';
+import { IntegratedAppsDatatable } from '@/react/kubernetes/components/IntegratedAppsDatatable/IntegratedAppsDatatable';
import { applicationsModule } from './applications';
import { volumesModule } from './volumes';
@@ -213,6 +214,16 @@ export const ngModule = angular
'showSystem',
'setSystemResources',
])
+ )
+ .component(
+ 'kubernetesIntegratedApplicationsDatatable',
+ r2a(withUIRouter(withCurrentUser(IntegratedAppsDatatable)), [
+ 'dataset',
+ 'isLoading',
+ 'onRefresh',
+ 'tableKey',
+ 'tableTitle',
+ ])
);
export const componentsModule = ngModule.name;
diff --git a/app/kubernetes/views/configurations/configmap/edit/configMap.html b/app/kubernetes/views/configurations/configmap/edit/configMap.html
index 420b653a3..92f30e8db 100644
--- a/app/kubernetes/views/configurations/configmap/edit/configMap.html
+++ b/app/kubernetes/views/configurations/configmap/edit/configMap.html
@@ -158,17 +158,13 @@
-
+
+
diff --git a/app/kubernetes/views/configurations/secret/edit/secret.html b/app/kubernetes/views/configurations/secret/edit/secret.html
index 231ee9b5f..6052ee074 100644
--- a/app/kubernetes/views/configurations/secret/edit/secret.html
+++ b/app/kubernetes/views/configurations/secret/edit/secret.html
@@ -172,17 +172,13 @@
-
+
+
diff --git a/app/kubernetes/views/volumes/edit/volume.html b/app/kubernetes/views/volumes/edit/volume.html
index ca082006d..5120f3b2a 100644
--- a/app/kubernetes/views/volumes/edit/volume.html
+++ b/app/kubernetes/views/volumes/edit/volume.html
@@ -180,17 +180,13 @@
-
+
+
diff --git a/app/react/components/Svg.tsx b/app/react/components/Svg.tsx
index 398d89308..416a56ede 100644
--- a/app/react/components/Svg.tsx
+++ b/app/react/components/Svg.tsx
@@ -3,7 +3,6 @@
import dataflow from '@/assets/ico/dataflow-1.svg?c';
import git from '@/assets/ico/git.svg?c';
import kube from '@/assets/ico/kube.svg?c';
-import laptopcode from '@/assets/ico/laptop-code.svg?c';
import ldap from '@/assets/ico/ldap.svg?c';
import linux from '@/assets/ico/linux.svg?c';
import memory from '@/assets/ico/memory.svg?c';
@@ -40,7 +39,6 @@ export const SvgIcons = {
dataflow,
dockericon,
git,
- laptopcode,
ldap,
linux,
memory,
diff --git a/app/react/kubernetes/components/IntegratedAppsDatatable/IntegratedAppsDatatable.tsx b/app/react/kubernetes/components/IntegratedAppsDatatable/IntegratedAppsDatatable.tsx
new file mode 100644
index 000000000..92bf92346
--- /dev/null
+++ b/app/react/kubernetes/components/IntegratedAppsDatatable/IntegratedAppsDatatable.tsx
@@ -0,0 +1,59 @@
+import LaptopCode from '@/assets/ico/laptop-code.svg?c';
+
+import { Datatable, TableSettingsMenu } from '@@/datatables';
+import { useRepeater } from '@@/datatables/useRepeater';
+import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
+import { useTableStateWithStorage } from '@@/datatables/useTableState';
+import {
+ BasicTableSettings,
+ refreshableSettings,
+ RefreshableTableSettings,
+} from '@@/datatables/types';
+
+import { columns } from './columns';
+import { IntegratedApp } from './types';
+
+interface TableSettings extends BasicTableSettings, RefreshableTableSettings {}
+
+export function IntegratedAppsDatatable({
+ dataset,
+ onRefresh,
+ isLoading,
+ tableKey,
+ tableTitle,
+}: {
+ dataset: Array;
+ onRefresh: () => void;
+ isLoading: boolean;
+ tableKey: string;
+ tableTitle: string;
+}) {
+ const tableState = useTableStateWithStorage(
+ tableKey,
+ 'Name',
+ (set) => ({
+ ...refreshableSettings(set),
+ })
+ );
+ useRepeater(tableState.autoRefreshRate, onRefresh);
+
+ return (
+ (
+
+
+
+ )}
+ />
+ );
+}
diff --git a/app/react/kubernetes/components/IntegratedAppsDatatable/columns.helper.tsx b/app/react/kubernetes/components/IntegratedAppsDatatable/columns.helper.tsx
new file mode 100644
index 000000000..7a920cc6d
--- /dev/null
+++ b/app/react/kubernetes/components/IntegratedAppsDatatable/columns.helper.tsx
@@ -0,0 +1,5 @@
+import { createColumnHelper } from '@tanstack/react-table';
+
+import { IntegratedApp } from './types';
+
+export const helper = createColumnHelper();
diff --git a/app/react/kubernetes/components/IntegratedAppsDatatable/columns.name.tsx b/app/react/kubernetes/components/IntegratedAppsDatatable/columns.name.tsx
new file mode 100644
index 000000000..fec471525
--- /dev/null
+++ b/app/react/kubernetes/components/IntegratedAppsDatatable/columns.name.tsx
@@ -0,0 +1,22 @@
+import { CellContext } from '@tanstack/react-table';
+
+import { Link } from '@@/Link';
+
+import { helper } from './columns.helper';
+import { IntegratedApp } from './types';
+
+export const name = helper.accessor('Name', {
+ header: 'Name',
+ cell: Cell,
+});
+
+function Cell({ row: { original: item } }: CellContext) {
+ return (
+
+ {item.Name}
+
+ );
+}
diff --git a/app/react/kubernetes/components/IntegratedAppsDatatable/columns.tsx b/app/react/kubernetes/components/IntegratedAppsDatatable/columns.tsx
new file mode 100644
index 000000000..f2535ec7c
--- /dev/null
+++ b/app/react/kubernetes/components/IntegratedAppsDatatable/columns.tsx
@@ -0,0 +1,22 @@
+import { truncate } from '@/portainer/filters/filters';
+
+import { helper } from './columns.helper';
+import { name } from './columns.name';
+
+export const columns = [
+ name,
+ helper.accessor('StackName', {
+ header: 'Stack',
+ cell: ({ getValue }) => getValue() || '-',
+ }),
+
+ helper.accessor('Image', {
+ header: 'Image',
+ cell: ({ row: { original: item } }) => (
+ <>
+ {truncate(item.Image, 64)}
+ {item.Containers?.length > 1 && <>+ {item.Containers.length - 1}>}
+ >
+ ),
+ }),
+];
diff --git a/app/react/kubernetes/components/IntegratedAppsDatatable/types.ts b/app/react/kubernetes/components/IntegratedAppsDatatable/types.ts
new file mode 100644
index 000000000..0d858b004
--- /dev/null
+++ b/app/react/kubernetes/components/IntegratedAppsDatatable/types.ts
@@ -0,0 +1,3 @@
+import { KubernetesApplication } from '@/kubernetes/models/application/models';
+
+export type IntegratedApp = KubernetesApplication;