Przeglądaj źródła

change styling of notification tab, filter out app events and notifs from feed, scroll to top of expanded event

Feroze Mohideen 2 lat temu
rodzic
commit
d84c9a2c70

+ 1 - 1
api/server/handlers/porter_app/list_events_apply_v2.go

@@ -79,7 +79,7 @@ func (p *PorterAppV2EventListHandler) ServeHTTP(w http.ResponseWriter, r *http.R
 		return
 	}
 
-	porterAppEvents, paginatedResult, err := p.Repo().PorterAppEvent().ListEventsByPorterAppIDAndDeploymentTargetID(ctx, app.ID, uid, helpers.WithPageSize(20), helpers.WithPage(int(request.Page)))
+	porterAppEvents, paginatedResult, err := p.Repo().PorterAppEvent().ListBuildDeployEventsByPorterAppIDAndDeploymentTargetID(ctx, app.ID, uid, helpers.WithPageSize(20), helpers.WithPage(int(request.Page)))
 	if err != nil {
 		if !errors.Is(err, gorm.ErrRecordNotFound) {
 			e := telemetry.Error(ctx, span, nil, "error listing porter app events by porter app id")

+ 24 - 1
dashboard/src/components/TabSelector.tsx

@@ -1,10 +1,12 @@
 import React, { Component } from "react";
 import styled from "styled-components";
+import Spacer from "./porter/Spacer";
 
 export interface selectOption<T> {
   value: T;
   label: string;
   component?: any;
+  sibling?: JSX.Element;
 }
 
 type PropsType<T> = {
@@ -36,7 +38,21 @@ export default class TabSelector<T> extends Component<PropsType<T>, StateType> {
   renderTabList = () => {
     let color = this.props.color || "#aaaabb";
     return this.props.options.map((option: selectOption<T>, i: number) => {
-      return (
+      return option.sibling ? (
+        <TabWithSibling>
+          <Tab
+            key={i}
+            onClick={() => this.handleTabClick(option.value)}
+            lastItem={i === this.props.options.length - 1}
+            highlight={option.value === this.props.currentTab ? color : null}
+            style={{marginRight: "0px"}}
+          >
+            {option.label}
+          </Tab>
+          <Spacer inline x={0.5} />
+          {option.sibling}
+        </TabWithSibling>
+      ) : (
         <Tab
           key={i}
           onClick={() => this.handleTabClick(option.value)}
@@ -127,3 +143,10 @@ const StyledTabSelector = styled.div`
   margin-left: 1px;
   position: relative;
 `;
+
+const TabWithSibling = styled.div`
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  margin-right: 30px;
+`;

+ 2 - 1
dashboard/src/main/home/app-dashboard/app-view/AppDataContainer.tsx

@@ -49,6 +49,7 @@ import HelmLatestValuesTab from "./tabs/HelmLatestValuesTab";
 import { Context } from "shared/Context";
 import { useIntercom } from "lib/hooks/useIntercom";
 import Notifications from "./tabs/Notifications";
+import Text from "components/porter/Text";
 
 // commented out tabs are not yet implemented
 // will be included as support is available based on data from app revisions rather than helm releases
@@ -406,7 +407,7 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
     const numNotifications = latestNotifications.length;
 
     const base = [
-      { label: `Notifications (${numNotifications})`, value: "notifications"},
+      { label: `Notifications`, value: "notifications", sibling: numNotifications > 0 ? <Text color={"#FF6060"}>({numNotifications})</Text> : undefined},
       { label: "Activity", value: "activity" },
       { label: "Overview", value: "overview" },
       { label: "Logs", value: "logs" },

+ 1 - 12
dashboard/src/main/home/app-dashboard/app-view/tabs/activity-feed/ActivityFeed.tsx

@@ -37,17 +37,6 @@ const ActivityFeed: React.FC<Props> = ({ appName, deploymentTargetId, currentClu
     const [isPorterAgentInstalling, setIsPorterAgentInstalling] = useState(false);
     const [shouldAnimate, setShouldAnimate] = useState(true);
 
-    // remove this filter when https://linear.app/porter/issue/POR-1676/disable-porter-agent-code-for-cpu-alerts is resolved
-    const isNotFilteredAppEvent = (event: PorterAppEvent) => {
-        return !(event.type === "APP_EVENT" &&
-            (
-                event.metadata?.short_summary?.includes("requesting more memory than is available")
-                || event.metadata?.short_summary?.includes("requesting more CPU than is available")
-                || event.metadata?.short_summary?.includes("non-zero exit code")
-            )
-        ) && !(event.type === "NOTIFICATION");
-    }
-
     const { data: eventFetchData, isLoading: isEventFetchLoading, isRefetching } = useQuery(
         ["appEvents", deploymentTargetId, page],
         async () => {
@@ -65,7 +54,7 @@ const ActivityFeed: React.FC<Props> = ({ appName, deploymentTargetId, currentClu
             );
 
             const parsed = await z.object({ events: z.array(porterAppEventValidator).optional().default([]), num_pages: z.number() }).parseAsync(res.data);
-            return { events: parsed.events.filter(isNotFilteredAppEvent), pages: parsed.num_pages };
+            return { events: parsed.events, pages: parsed.num_pages };
         },
         {
             enabled: hasPorterAgent,

+ 20 - 1
dashboard/src/main/home/app-dashboard/app-view/tabs/notifications/NotificationExpandedView.tsx

@@ -1,4 +1,4 @@
-import React, { useEffect } from "react";
+import React, { useEffect, useRef } from "react";
 import styled from "styled-components";
 import Text from "components/porter/Text";
 import Spacer from "components/porter/Spacer";
@@ -16,6 +16,7 @@ type Props = {
   appName: string;
   deploymentTargetId: string;
   appId: number;
+  scrollToTopRef: React.MutableRefObject<HTMLDivElement | null>;
 }
 
 const NotificationExpandedView: React.FC<Props> = ({
@@ -25,10 +26,12 @@ const NotificationExpandedView: React.FC<Props> = ({
     appName,
     deploymentTargetId,
     appId,
+    scrollToTopRef,
 }) => {
   return (
     <StyledNotificationExpandedView>
       <ExpandedViewContent>
+        <div ref={scrollToTopRef} style={{marginTop: "-40px", marginBottom: "40px"}}/>
         <Text color="helper">Event ID: {notification.id}</Text>
         <Spacer y={0.5} />
         <StyledActivityFeed>
@@ -138,6 +141,22 @@ const Message = styled.div`
   font-size: 13px;
   display: flex;
   flex-direction: column;
+  opacity: 0;
+  animation: slideIn 0.5s 0s;
+  animation-fill-mode: forwards;
+  user-select: text;
+  @keyframes slideIn {
+    from {
+      margin-left: -10px;
+      opacity: 0;
+      margin-right: 10px;
+    }
+    to {
+      margin-left: 0;
+      opacity: 1;
+      margin-right: 0;
+    }
+  }
 `;
 
 const ExpandedViewFooter = styled.div`

+ 13 - 1
dashboard/src/main/home/app-dashboard/app-view/tabs/notifications/NotificationFeed.tsx

@@ -1,4 +1,4 @@
-import React, { useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
 import styled from "styled-components";
 import NotificationList from "./NotificationList";
 import NotificationExpandedView from "./NotificationExpandedView";
@@ -22,11 +22,22 @@ const NotificationFeed: React.FC<Props> = ({
     appId,
 }) => {
     const [selectedNotification, setSelectedNotification] = useState<ClientNotification | undefined>(notifications.length ? notifications[0] : undefined);
+    const scrollToTopRef = useRef<HTMLDivElement | null>(null);
 
     const handleTileClick = (notification: ClientNotification) => {
         setSelectedNotification(notification);
+        scrollToTopRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
     };
 
+    useEffect(() => {
+        // if new notifications come in, select the first one if none are selected; otherwise, keep the current selection
+        if (notifications.length && !selectedNotification) {
+            setSelectedNotification(notifications[0]);
+        } else if (selectedNotification && notifications.find((n) => n.id === selectedNotification.id)) {
+            setSelectedNotification(notifications.find((n) => n.id === selectedNotification.id));
+        }
+    }, [JSON.stringify(notifications)])
+
     return (
         <StyledNotificationFeed>
             {selectedNotification && (
@@ -43,6 +54,7 @@ const NotificationFeed: React.FC<Props> = ({
                         appName={appName}
                         deploymentTargetId={deploymentTargetId}
                         appId={appId}
+                        scrollToTopRef={scrollToTopRef}
                     />
                 </>
             )}

+ 31 - 0
internal/repository/gorm/porter_app_event.go

@@ -80,6 +80,37 @@ func (repo *PorterAppEventRepository) ListEventsByPorterAppIDAndDeploymentTarget
 	return apps, paginatedResult, nil
 }
 
+// ListEventsByPorterAppIDAndDeploymentTargetID returns a list of events for a given porter app id and deployment target id
+func (repo *PorterAppEventRepository) ListBuildDeployEventsByPorterAppIDAndDeploymentTargetID(ctx context.Context, porterAppID uint, deploymentTargetID uuid.UUID, opts ...helpers.QueryOption) ([]*models.PorterAppEvent, helpers.PaginatedResult, error) {
+	ctx, span := telemetry.NewSpan(ctx, "list-events-by-porter-app-id-and-deployment-target-id")
+	defer span.End()
+
+	telemetry.WithAttributes(span,
+		telemetry.AttributeKV{Key: "porter-app-id", Value: porterAppID},
+		telemetry.AttributeKV{Key: "deployment-target-id", Value: deploymentTargetID},
+	)
+
+	apps := []*models.PorterAppEvent{}
+	paginatedResult := helpers.PaginatedResult{}
+
+	id := strconv.Itoa(int(porterAppID))
+	if id == "" {
+		return nil, paginatedResult, telemetry.Error(ctx, span, nil, "invalid porter app id supplied")
+	}
+
+	db := repo.db.Model(&models.PorterAppEvent{})
+	resultDB := db.Where("porter_app_id = ? AND deployment_target_id = ? AND type != 'APP_EVENT' AND type != 'NOTIFICATION'", id, deploymentTargetID).Order("created_at DESC")
+	resultDB = resultDB.Scopes(helpers.Paginate(db, &paginatedResult, opts...))
+
+	if err := resultDB.Find(&apps).Error; err != nil {
+		if !errors.Is(err, gorm.ErrRecordNotFound) {
+			return nil, paginatedResult, telemetry.Error(ctx, span, err, "error finding events by porter app id and deployment target id")
+		}
+	}
+
+	return apps, paginatedResult, nil
+}
+
 func (repo *PorterAppEventRepository) CreateEvent(ctx context.Context, appEvent *models.PorterAppEvent) error {
 	if appEvent.ID == uuid.Nil {
 		appEvent.ID = uuid.New()

+ 1 - 0
internal/repository/porter_app_event.go

@@ -13,6 +13,7 @@ type PorterAppEventRepository interface {
 	ListEventsByPorterAppID(ctx context.Context, porterAppID uint, opts ...helpers.QueryOption) ([]*models.PorterAppEvent, helpers.PaginatedResult, error)
 	// ListEventsByPorterAppIDAndDeploymentTargetID returns a list of events for a given porter app id and deployment target id
 	ListEventsByPorterAppIDAndDeploymentTargetID(ctx context.Context, porterAppID uint, deploymentTargetID uuid.UUID, opts ...helpers.QueryOption) ([]*models.PorterAppEvent, helpers.PaginatedResult, error)
+	ListBuildDeployEventsByPorterAppIDAndDeploymentTargetID(ctx context.Context, porterAppID uint, deploymentTargetID uuid.UUID, opts ...helpers.QueryOption) ([]*models.PorterAppEvent, helpers.PaginatedResult, error)
 	CreateEvent(ctx context.Context, appEvent *models.PorterAppEvent) error
 	UpdateEvent(ctx context.Context, appEvent *models.PorterAppEvent) error
 	ReadEvent(ctx context.Context, id uuid.UUID) (models.PorterAppEvent, error)