From 33ce841040d09c6894a7adc97c52d76f97e8da35 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Wed, 28 Aug 2024 13:41:15 -0600 Subject: [PATCH] refactor(docker/events): migrate list view to react [EE-2228] (#11581) --- app/docker/__module.js | 3 +- app/docker/react/views/index.ts | 2 ++ app/docker/services/systemService.js | 12 ------- app/docker/views/events/events.html | 3 -- app/docker/views/events/eventsController.js | 23 ------------- app/react/docker/events/EventsDatatables.tsx | 22 +++++++------ app/react/docker/events/ListView.tsx | 33 +++++++++++++++++++ .../event.ts => react/docker/events/model.ts} | 16 +-------- app/react/docker/events/types.ts | 5 +++ app/react/docker/proxy/queries/query-keys.ts | 2 ++ app/react/docker/proxy/queries/useEvents.ts | 19 ++++++++--- 11 files changed, 71 insertions(+), 69 deletions(-) delete mode 100644 app/docker/views/events/events.html delete mode 100644 app/docker/views/events/eventsController.js create mode 100644 app/react/docker/events/ListView.tsx rename app/{docker/models/event.ts => react/docker/events/model.ts} (93%) create mode 100644 app/react/docker/events/types.ts diff --git a/app/docker/__module.js b/app/docker/__module.js index cb9383bcf..ed9ad294a 100644 --- a/app/docker/__module.js +++ b/app/docker/__module.js @@ -188,8 +188,7 @@ angular.module('portainer.docker', ['portainer.app', reactModule]).config([ url: '/events', views: { 'content@': { - templateUrl: './views/events/events.html', - controller: 'EventsController', + component: 'eventsListView', }, }, data: { diff --git a/app/docker/react/views/index.ts b/app/docker/react/views/index.ts index 6205f0b22..9c3f8b95f 100644 --- a/app/docker/react/views/index.ts +++ b/app/docker/react/views/index.ts @@ -5,6 +5,7 @@ import { r2a } from '@/react-tools/react2angular'; import { withCurrentUser } from '@/react-tools/withCurrentUser'; import { withUIRouter } from '@/react-tools/withUIRouter'; import { DashboardView } from '@/react/docker/DashboardView/DashboardView'; +import { ListView } from '@/react/docker/events/ListView'; import { containersModule } from './containers'; @@ -14,6 +15,7 @@ export const viewsModule = angular 'dockerDashboardView', r2a(withUIRouter(withCurrentUser(DashboardView)), []) ) + .component('eventsListView', r2a(withUIRouter(withCurrentUser(ListView)), [])) .component( 'networkDetailsView', r2a(withUIRouter(withCurrentUser(NetworksItemView)), []) diff --git a/app/docker/services/systemService.js b/app/docker/services/systemService.js index ed02d2c00..9c945527d 100644 --- a/app/docker/services/systemService.js +++ b/app/docker/services/systemService.js @@ -1,8 +1,6 @@ import { ping } from '@/react/docker/proxy/queries/usePing'; import { getInfo } from '@/react/docker/proxy/queries/useInfo'; import { getVersion } from '@/react/docker/proxy/queries/useVersion'; -import { getEvents } from '@/react/docker/proxy/queries/useEvents'; -import { EventViewModel } from '../models/event'; angular.module('portainer.docker').factory('SystemService', SystemServiceFactory); @@ -14,15 +12,5 @@ function SystemServiceFactory(AngularToReact) { info: useAxios(injectEnvironmentId(getInfo)), // dashboard + docker host view + docker host browser + swarm inspect views + stateManager (update endpoint state) ping: useAxios(ping), // docker/__module onEnter abstract /docker subpath version: useAxios(injectEnvironmentId(getVersion)), // docker host view + swarm inspect view + stateManager (update endpoint state) - events: useAxios(injectEnvironmentId(eventsAngularJS)), // events list }; - - /** - * @param {EnvironmentId} environmentId Injected - * @param {{since: string; until: string;}} param1 - */ - async function eventsAngularJS(environmentId, { since, until }) { - const data = await getEvents(environmentId, { since, until }); - return data.map((e) => new EventViewModel(e)); - } } diff --git a/app/docker/views/events/events.html b/app/docker/views/events/events.html deleted file mode 100644 index b85942243..000000000 --- a/app/docker/views/events/events.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/docker/views/events/eventsController.js b/app/docker/views/events/eventsController.js deleted file mode 100644 index 3864eabc0..000000000 --- a/app/docker/views/events/eventsController.js +++ /dev/null @@ -1,23 +0,0 @@ -import moment from 'moment'; - -angular.module('portainer.docker').controller('EventsController', [ - '$scope', - 'Notifications', - 'SystemService', - function ($scope, Notifications, SystemService) { - function initView() { - const since = moment().subtract(24, 'hour').unix(); - const until = moment().unix(); - - SystemService.events({ since, until }) - .then(function success(data) { - $scope.events = data; - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to load events'); - }); - } - - initView(); - }, -]); diff --git a/app/react/docker/events/EventsDatatables.tsx b/app/react/docker/events/EventsDatatables.tsx index e79f65407..36d356fd1 100644 --- a/app/react/docker/events/EventsDatatables.tsx +++ b/app/react/docker/events/EventsDatatables.tsx @@ -1,5 +1,6 @@ import { createColumnHelper } from '@tanstack/react-table'; import { Clock } from 'lucide-react'; +import { EventMessage } from 'docker-types/generated/1.41'; import { isoDateFromTimestamp } from '@/portainer/filters/filters'; @@ -7,26 +8,22 @@ import { Datatable } from '@@/datatables'; import { createPersistedStore } from '@@/datatables/types'; import { useTableState } from '@@/datatables/useTableState'; -type DockerEvent = { - Time: number; - Type: string; - Details: string; -}; +import { createEventDetails } from './model'; -const columnHelper = createColumnHelper(); +const columnHelper = createColumnHelper(); export const columns = [ - columnHelper.accessor('Time', { + columnHelper.accessor('time', { header: 'Date', cell: ({ getValue }) => { const value = getValue(); return isoDateFromTimestamp(value); }, }), - columnHelper.accessor('Type', { + columnHelper.accessor((c) => c.Type, { header: 'Type', }), - columnHelper.accessor('Details', { + columnHelper.accessor((c) => createEventDetails(c), { header: 'Details', }), ]; @@ -37,12 +34,17 @@ const settingsStore = createPersistedStore(tableKey, { desc: true, }); -export function EventsDatatable({ dataset }: { dataset: Array }) { +export function EventsDatatable({ + dataset, +}: { + dataset?: Array; +}) { const tableState = useTableState(settingsStore, tableKey); return ( + + + + + ); +} + +function useDateRange() { + return useState(() => { + const since = moment().subtract(24, 'hour').unix(); + const until = moment().unix(); + + return { since, until }; + })[0]; +} diff --git a/app/docker/models/event.ts b/app/react/docker/events/model.ts similarity index 93% rename from app/docker/models/event.ts rename to app/react/docker/events/model.ts index 95b171140..437ed3ebb 100644 --- a/app/docker/models/event.ts +++ b/app/react/docker/events/model.ts @@ -87,7 +87,7 @@ const templates: EventToTemplateMap = { }, }; -function createEventDetails(event: EventMessage) { +export function createEventDetails(event: EventMessage) { const eventType = event.Type ?? ''; // An action can be `action:extra` @@ -118,17 +118,3 @@ function createEventDetails(event: EventMessage) { return details + extra; } - -export class EventViewModel { - Time: EventMessage['time']; - - Type: EventMessage['Type']; - - Details: string; - - constructor(data: EventMessage) { - this.Time = data.time; - this.Type = data.Type; - this.Details = createEventDetails(data); - } -} diff --git a/app/react/docker/events/types.ts b/app/react/docker/events/types.ts new file mode 100644 index 000000000..9d9cd92de --- /dev/null +++ b/app/react/docker/events/types.ts @@ -0,0 +1,5 @@ +export type DockerEvent = { + Time: number; + Type: string; + Details: string; +}; diff --git a/app/react/docker/proxy/queries/query-keys.ts b/app/react/docker/proxy/queries/query-keys.ts index 2439c835e..d11da4230 100644 --- a/app/react/docker/proxy/queries/query-keys.ts +++ b/app/react/docker/proxy/queries/query-keys.ts @@ -3,4 +3,6 @@ import { EnvironmentId } from '@/react/portainer/environments/types'; export const queryKeys = { base: (environmentId: EnvironmentId) => [environmentId, 'docker', 'proxy'] as const, + events: (environmentId: EnvironmentId, params?: object) => + [...queryKeys.base(environmentId), 'events', params] as const, }; diff --git a/app/react/docker/proxy/queries/useEvents.ts b/app/react/docker/proxy/queries/useEvents.ts index 35e8d6cc9..88e637886 100644 --- a/app/react/docker/proxy/queries/useEvents.ts +++ b/app/react/docker/proxy/queries/useEvents.ts @@ -1,4 +1,5 @@ import { EventMessage } from 'docker-types/generated/1.41'; +import { useQuery } from '@tanstack/react-query'; import axios, { jsonObjectsToArrayHandler, @@ -7,16 +8,26 @@ import axios, { import { EnvironmentId } from '@/react/portainer/environments/types'; import { buildDockerProxyUrl } from './buildDockerProxyUrl'; +import { queryKeys } from './query-keys'; + +type Params = { since?: number; until?: number }; + +export function useEvents( + environmentId: EnvironmentId, + { params }: { params?: Params } = {} +) { + return useQuery({ + queryKey: [...queryKeys.events(environmentId, params)], + queryFn: () => getEvents(environmentId, params), + }); +} /** * Raw docker API proxy - * @param environmentId - * @param param1 - * @returns */ export async function getEvents( environmentId: EnvironmentId, - { since, until }: { since: string; until: string } + { since, until }: Params = {} ) { try { const { data } = await axios.get(