From f22c0f9b80ef1ecd7da91b10712afd50d9b0af47 Mon Sep 17 00:00:00 2001 From: Alejandro Villegas Date: Thu, 31 Jul 2025 11:34:33 +0200 Subject: [PATCH 01/15] feat: Modified Instances overview by status (#105) * feat: Modified Instances overview by status Signed-off-by: Alejandro Villegas * Trigger Build * Trigger Workflows --------- Signed-off-by: Alejandro Villegas --- src/app/Overview/Overview.tsx | 6 ++++++ src/app/Overview/components/CardData.tsx | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/app/Overview/Overview.tsx b/src/app/Overview/Overview.tsx index 6087796..326fe5d 100644 --- a/src/app/Overview/Overview.tsx +++ b/src/app/Overview/Overview.tsx @@ -32,6 +32,12 @@ const AggregateStatusCards: React.FunctionComponent = () => { unknown: inventoryData?.clusters?.unknown || 0, terminated: inventoryData?.clusters?.archived || 0, }, + instancesByStatus: { + running: inventoryData?.instances?.running || 0, + stopped: inventoryData?.instances?.stopped || 0, + unknown: inventoryData?.instances?.unknown || 0, + terminated: inventoryData?.instances?.archived || 0, + }, clustersByProvider: { [CloudProvider.AWS]: inventoryData.providers.aws?.cluster_count || 0, [CloudProvider.GCP]: inventoryData.providers.gcp?.cluster_count || 0, diff --git a/src/app/Overview/components/CardData.tsx b/src/app/Overview/components/CardData.tsx index ba918df..9280cbb 100644 --- a/src/app/Overview/components/CardData.tsx +++ b/src/app/Overview/components/CardData.tsx @@ -16,8 +16,12 @@ export const generateCards = (state: DashboardState): Record ({ + icon: status.icon, + value: state.instancesByStatus[key] || 0, + ref: status.route, + })), layout: CardLayout.MULTI_ICON, }, { From c6aa4c209e39fd3394eb2fa8339725c6491443d9 Mon Sep 17 00:00:00 2001 From: Alejandro Villegas Date: Wed, 20 Aug 2025 03:16:20 +0200 Subject: [PATCH 02/15] fix: Fixed Last Scan tile (#109) Signed-off-by: Alejandro Villegas --- src/app/Overview/components/CardData.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/Overview/components/CardData.tsx b/src/app/Overview/components/CardData.tsx index 9280cbb..5ca2842 100644 --- a/src/app/Overview/components/CardData.tsx +++ b/src/app/Overview/components/CardData.tsx @@ -3,7 +3,7 @@ import { CLOUD_PROVIDERS, STATUSES } from '../constants'; export const generateCards = (state: DashboardState): Record => { const scannerContent = state.lastScanTimestamp - ? `Last scan: ${new Date(state.lastScanTimestamp).toLocaleString()}` + ? `${new Date(state.lastScanTimestamp).toLocaleString()}` : 'No scan data available'; const statusCards = [ { @@ -25,7 +25,7 @@ export const generateCards = (state: DashboardState): Record Date: Mon, 27 Oct 2025 15:13:23 +0200 Subject: [PATCH 03/15] Feature/overview improvement (#113) * add an Activity window for events * add events data from the DB to the recent events overview * add icons next to the Result columns * change icons location * chore: Updating the Event interface --- src/app/Overview/Overview.tsx | 69 +++++++++++++------ src/app/Overview/components/ActivityTable.tsx | 64 +++++++++++++++++ src/app/Overview/components/CardData.tsx | 15 +++- src/app/Overview/hooks/useEventsData.ts | 31 +++++++++ src/app/Overview/types.ts | 2 + src/app/services/api.js | 14 ++++ src/app/types/events.ts | 17 +++++ 7 files changed, 189 insertions(+), 23 deletions(-) create mode 100644 src/app/Overview/components/ActivityTable.tsx create mode 100644 src/app/Overview/hooks/useEventsData.ts create mode 100644 src/app/types/events.ts diff --git a/src/app/Overview/Overview.tsx b/src/app/Overview/Overview.tsx index 326fe5d..21543e2 100644 --- a/src/app/Overview/Overview.tsx +++ b/src/app/Overview/Overview.tsx @@ -17,9 +17,11 @@ import { generateCards } from './components/CardData'; import { CloudProvider } from './types'; import { renderContent } from './components/CardRenderer'; import { useDashboardData } from './hooks/useDashboardData'; +import { useEventsData } from './hooks/useEventsData'; const AggregateStatusCards: React.FunctionComponent = () => { const { inventoryData } = useDashboardData(); + const { events, loading: eventsLoading, error: eventsError } = useEventsData(); if (!inventoryData) { return ; @@ -33,10 +35,10 @@ const AggregateStatusCards: React.FunctionComponent = () => { terminated: inventoryData?.clusters?.archived || 0, }, instancesByStatus: { - running: inventoryData?.instances?.running || 0, - stopped: inventoryData?.instances?.stopped || 0, - unknown: inventoryData?.instances?.unknown || 0, - terminated: inventoryData?.instances?.archived || 0, + running: 0, // Not available in current API + stopped: 0, // Not available in current API + unknown: 0, // Not available in current API + terminated: 0, // Not available in current API }, clustersByProvider: { [CloudProvider.AWS]: inventoryData.providers.aws?.cluster_count || 0, @@ -52,7 +54,7 @@ const AggregateStatusCards: React.FunctionComponent = () => { lastScanTimestamp: inventoryData.scanner?.last_scan_timestamp, }; - const cardData = generateCards(dashboardState); + const cardData = generateCards(dashboardState, events); return ( @@ -63,23 +65,46 @@ const AggregateStatusCards: React.FunctionComponent = () => { - {Object.entries(cardData).map(([, cards], groupIndex) => ( - - - {cards.map((card, cardIndex) => ( - - {card.title} - {renderContent(card.content, card.layout)} - - ))} - + {Object.entries(cardData).map(([groupName, cards], groupIndex) => ( + + {groupName === 'activityCards' ? ( + // Full width Activity card with double height + + {cards[0].title} + + {eventsLoading ? ( + + ) : eventsError ? ( +
+ Error: {eventsError} +
+ Check console for details +
+ ) : cards[0].customComponent ? ( + cards[0].customComponent + ) : ( + renderContent(cards[0].content, cards[0].layout) + )} +
+
+ ) : ( + // Regular cards in Gallery + + {cards.map((card, cardIndex) => ( + + {card.title} + {renderContent(card.content, card.layout)} + + ))} + + )}
))}
diff --git a/src/app/Overview/components/ActivityTable.tsx b/src/app/Overview/components/ActivityTable.tsx new file mode 100644 index 0000000..aa12e2d --- /dev/null +++ b/src/app/Overview/components/ActivityTable.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; +import { Event } from '@app/types/events'; +import { CheckCircleIcon, ErrorCircleOIcon, WarningTriangleIcon } from '@patternfly/react-icons'; + +interface ActivityTableProps { + events: Event[]; +} + +const getResultIcon = (result: string) => { + const PATTERNFLY_COLORS = { + success: 'var(--pf-v5-global--success-color--100)', + danger: 'var(--pf-v5-global--danger-color--100)', + warning: 'var(--pf-v5-global--warning-color--100)', + } as const; + + switch (result.toLowerCase()) { + case 'success': + return ; + case 'failed': + case 'failure': + return ; + case 'warning': + case 'partial': + return ; + default: + return ; + } +}; + +export const ActivityTable: React.FunctionComponent = ({ events }) => { + if (events.length === 0) { + return
No recent events
; + } + + return ( + + + + + + + + + + + + + {events.map(event => ( + + + + + + + + + ))} + +
TimeActionResultResourceTriggered By
{getResultIcon(event.result)}{new Date(event.timestamp).toLocaleString()}{event.action}{event.result} + {event.resourceType} {event.resourceId} + {event.triggeredBy}
+ ); +}; diff --git a/src/app/Overview/components/CardData.tsx b/src/app/Overview/components/CardData.tsx index 5ca2842..f5e9750 100644 --- a/src/app/Overview/components/CardData.tsx +++ b/src/app/Overview/components/CardData.tsx @@ -1,7 +1,10 @@ import { CardDefinition, CardLayout, DashboardState } from '../types'; import { CLOUD_PROVIDERS, STATUSES } from '../constants'; +import { Event } from '@app/types/events'; +import { ActivityTable } from './ActivityTable'; +import React from 'react'; -export const generateCards = (state: DashboardState): Record => { +export const generateCards = (state: DashboardState, events: Event[] = []): Record => { const scannerContent = state.lastScanTimestamp ? `${new Date(state.lastScanTimestamp).toLocaleString()}` : 'No scan data available'; @@ -48,8 +51,18 @@ export const generateCards = (state: DashboardState): Record, + }, + ]; + return { statusCards, providerCards, + activityCards, }; }; diff --git a/src/app/Overview/hooks/useEventsData.ts b/src/app/Overview/hooks/useEventsData.ts new file mode 100644 index 0000000..98b8b65 --- /dev/null +++ b/src/app/Overview/hooks/useEventsData.ts @@ -0,0 +1,31 @@ +import { useState, useEffect } from 'react'; +import { getRecentEvents } from '@app/services/api'; +import { Event } from '@app/types/events'; + +export const useEventsData = () => { + const [events, setEvents] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchEvents = async () => { + try { + setLoading(true); + setError(null); + console.log('Fetching recent events...'); + const data = await getRecentEvents(); + console.log('Events data received:', data); + setEvents(data); + } catch (err) { + setError('Failed to fetch events'); + console.error('Error fetching events:', err); + } finally { + setLoading(false); + } + }; + + fetchEvents(); + }, []); + + return { events, loading, error }; +}; diff --git a/src/app/Overview/types.ts b/src/app/Overview/types.ts index 18c0b51..538b51d 100644 --- a/src/app/Overview/types.ts +++ b/src/app/Overview/types.ts @@ -22,10 +22,12 @@ export interface CardDefinition { title: string; content: CardContentItem[]; layout: CardLayout; + customComponent?: React.ReactNode; } export interface DashboardState { clustersByStatus: Record; + instancesByStatus: Record; clustersByProvider: Record; accountsByProvider: Record; instances: number; diff --git a/src/app/services/api.js b/src/app/services/api.js index 83fbf18..31095d8 100644 --- a/src/app/services/api.js +++ b/src/app/services/api.js @@ -162,6 +162,20 @@ export async function getSystemEvents() { } } +// Fetch the 10 most recent system events +export async function getRecentEvents() { + try { + console.log('Making API call to /events?page=1&page_size=10'); + const response = await apiClient.get('/events?page=1&page_size=10'); + console.log('API response:', response.data); + return response.data.items || []; + } catch (error) { + console.error('Failed to fetch recent events:', error); + console.error('Error details:', error.response?.data); + throw error; + } +} + // Create a scheduled action export async function createScheduledAction(actionData) { console.log('Sending to API:', JSON.stringify(actionData, null, 2)); diff --git a/src/app/types/events.ts b/src/app/types/events.ts new file mode 100644 index 0000000..f10e0ec --- /dev/null +++ b/src/app/types/events.ts @@ -0,0 +1,17 @@ +export interface Event { + id: number; + action: string; + resourceId: string; + resourceType: string; + timestamp: string; // ISO date string + result: string; + severity: string; + triggeredBy: string; + description?: string; + accountID: string; +} + +export interface EventsResponse { + events: Event[]; + total: number; +} From 42c55865f605aac8162a006ba3528179271c9560 Mon Sep 17 00:00:00 2001 From: Nofar Date: Wed, 10 Dec 2025 16:56:02 +0200 Subject: [PATCH 04/15] feat: restructure sidebar navigation into organized sections (#116) --- src/app/AppLayout/SidebarNavigation.tsx | 82 +++++++------------ .../components/ClusterDetailsInstances.tsx | 2 +- src/app/Observe/AuditLogs/AuditLogsTable.tsx | 2 +- src/app/Servers/components/ServersTable.tsx | 2 +- src/app/index.tsx | 8 +- 5 files changed, 34 insertions(+), 62 deletions(-) diff --git a/src/app/AppLayout/SidebarNavigation.tsx b/src/app/AppLayout/SidebarNavigation.tsx index 857b9dc..3cccf93 100644 --- a/src/app/AppLayout/SidebarNavigation.tsx +++ b/src/app/AppLayout/SidebarNavigation.tsx @@ -5,6 +5,14 @@ import { NavLink, useLocation } from 'react-router-dom'; const SidebarNavigation: React.FunctionComponent = () => { const location = useLocation(); + const isInventoryExpanded = + location.pathname.startsWith('/accounts') || + location.pathname.startsWith('/clusters') || + location.pathname.startsWith('/instances'); + + const isScanExpanded = location.pathname.startsWith('/scan'); + const isActionsExpanded = location.pathname.startsWith('/actions'); + return (