From 946166319f3fdcf4512e9cfe8ddf952720925609 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Tue, 2 Apr 2024 22:37:47 +0300 Subject: [PATCH] refactor(kube/apps): migrate integrated apps table to react [EE-4690] (#11025) --- .../integratedApplicationsDatatable.html | 131 ------------------ .../integratedApplicationsDatatable.js | 13 -- .../models/application/models/Application.ts | 123 ++++++++++++++++ .../application/models/ConfigurationVolume.ts | 9 ++ .../application/models/PersistedFolder.ts | 7 + .../models/application/models/index.js | 78 +---------- app/kubernetes/react/components/index.ts | 11 ++ .../configmap/edit/configMap.html | 22 ++- .../configurations/secret/edit/secret.html | 22 ++- app/kubernetes/views/volumes/edit/volume.html | 22 ++- app/react/components/Svg.tsx | 2 - .../IntegratedAppsDatatable.tsx | 59 ++++++++ .../columns.helper.tsx | 5 + .../IntegratedAppsDatatable/columns.name.tsx | 22 +++ .../IntegratedAppsDatatable/columns.tsx | 22 +++ .../IntegratedAppsDatatable/types.ts | 3 + 16 files changed, 291 insertions(+), 260 deletions(-) delete mode 100644 app/kubernetes/components/datatables/integrated-applications-datatable/integratedApplicationsDatatable.html delete mode 100644 app/kubernetes/components/datatables/integrated-applications-datatable/integratedApplicationsDatatable.js create mode 100644 app/kubernetes/models/application/models/Application.ts create mode 100644 app/kubernetes/models/application/models/ConfigurationVolume.ts create mode 100644 app/kubernetes/models/application/models/PersistedFolder.ts create mode 100644 app/react/kubernetes/components/IntegratedAppsDatatable/IntegratedAppsDatatable.tsx create mode 100644 app/react/kubernetes/components/IntegratedAppsDatatable/columns.helper.tsx create mode 100644 app/react/kubernetes/components/IntegratedAppsDatatable/columns.name.tsx create mode 100644 app/react/kubernetes/components/IntegratedAppsDatatable/columns.tsx create mode 100644 app/react/kubernetes/components/IntegratedAppsDatatable/types.ts 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 @@ -
- - -
-
-
- -
- {{ $ctrl.titleText }} -
- -
- - - - - - -
-
-
- - - - - - - - - - - - - - - - - - - - - -
- - - - - -
{{ 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;