Jelajahi Sumber

Implemented refresh for incidents tables and incident page

jnfrati 4 tahun lalu
induk
melakukan
833ae32e1c

+ 74 - 14
dashboard/src/components/Table.tsx

@@ -9,8 +9,13 @@ import {
 } from "react-table";
 import Loading from "components/Loading";
 import Selector from "./Selector";
+import loading from "assets/loading.gif";
 
-const GlobalFilter: React.FunctionComponent<any> = ({ setGlobalFilter }) => {
+const GlobalFilter: React.FunctionComponent<any> = ({
+  setGlobalFilter,
+  onRefresh,
+  isRefreshing,
+}) => {
   const [value, setValue] = React.useState("");
   const onChange = (value: string) => {
     setValue(value);
@@ -18,16 +23,29 @@ const GlobalFilter: React.FunctionComponent<any> = ({ setGlobalFilter }) => {
   };
 
   return (
-    <SearchRow>
-      <i className="material-icons">search</i>
-      <SearchInput
-        value={value}
-        onChange={(e: any) => {
-          onChange(e.target.value);
-        }}
-        placeholder="Search"
-      />
-    </SearchRow>
+    <SearchRowWrapper>
+      <SearchRow>
+        <i className="material-icons">search</i>
+        <SearchInput
+          value={value}
+          onChange={(e: any) => {
+            onChange(e.target.value);
+          }}
+          placeholder="Search"
+        />
+      </SearchRow>
+      {typeof onRefresh === "function" && (
+        <RefreshButton onClick={onRefresh} disabled={isRefreshing}>
+          {isRefreshing ? (
+            <>
+              <img src={loading} alt="loading icon" />
+            </>
+          ) : (
+            <i className="material-icons">refresh</i>
+          )}
+        </RefreshButton>
+      )}
+    </SearchRowWrapper>
   );
 };
 
@@ -41,6 +59,8 @@ export type TableProps = {
   enablePagination?: boolean;
   hasError?: boolean;
   errorMessage?: string;
+  onRefresh?: () => void;
+  isRefreshing?: boolean;
 };
 
 const MIN_PAGE_SIZE = 1;
@@ -55,6 +75,8 @@ const Table: React.FC<TableProps> = ({
   enablePagination,
   hasError,
   errorMessage = "An unexpected error occurred, please try again.",
+  onRefresh,
+  isRefreshing = false,
 }) => {
   const {
     getTableProps,
@@ -156,7 +178,11 @@ const Table: React.FC<TableProps> = ({
   return (
     <TableWrapper>
       {!disableGlobalFilter && (
-        <GlobalFilter setGlobalFilter={setGlobalFilter} />
+        <GlobalFilter
+          setGlobalFilter={setGlobalFilter}
+          onRefresh={onRefresh}
+          isRefreshing={isRefreshing}
+        />
       )}
       <StyledTable {...getTableProps()}>
         <StyledTHead>
@@ -354,8 +380,7 @@ const SearchRow = styled.div`
   min-width: 300px;
   max-width: min-content;
   background: #ffffff11;
-  margin-bottom: 15px;
-  margin-top: 0px;
+
   i {
     width: 18px;
     height: 18px;
@@ -364,3 +389,38 @@ const SearchRow = styled.div`
     font-size: 20px;
   }
 `;
+
+const SearchRowWrapper = styled.div`
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+  margin-top: 0px;
+`;
+
+const RefreshButton = styled.button`
+  justify-self: flex-end;
+  border: 1px solid #ffffff00;
+  border-radius: 50%;
+  background: inherit;
+  color: #ffffff;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 35px;
+  height: 35px;
+
+  > i {
+    font-size: 20px;
+  }
+  > img {
+    width: 20px;
+    height: 20px;
+  }
+
+  :hover {
+    color: #ffffff88;
+    border-color: #ffffff88;
+  }
+`;

+ 59 - 45
dashboard/src/main/home/cluster-dashboard/dashboard/incidents/IncidentPage.tsx

@@ -6,6 +6,7 @@ import TitleSection from "components/TitleSection";
 
 import backArrow from "assets/back_arrow.png";
 import nodePng from "assets/node.png";
+import loading from "assets/loading.gif";
 import { Drawer, withStyles } from "@material-ui/core";
 import EventDrawer from "./EventDrawer";
 import { useRouting } from "shared/routing";
@@ -28,6 +29,7 @@ const IncidentPage = () => {
 
   const [incident, setIncident] = useState<Incident>(null);
 
+  const [isRefreshing, setIsRefreshing] = useState(false);
   const [selectedEvent, setSelectedEvent] = useState<IncidentEvent>(null);
 
   const { getQueryParam, pushFiltered } = useRouting();
@@ -65,6 +67,29 @@ const IncidentPage = () => {
     };
   }, [incident_id]);
 
+  const refreshIncident = async () => {
+    setIsRefreshing(true);
+    try {
+      let incident = await api
+        .getIncidentById<Incident>(
+          "<token>",
+          { incident_id },
+          {
+            cluster_id: currentCluster.id,
+            project_id: currentProject.id,
+          }
+        )
+        .then((res) => res.data);
+
+      incident.events = convertEventsTimestampsToMilliseconds(incident.events);
+
+      setIncident(incident);
+    } catch (error) {
+    } finally {
+      setIsRefreshing(false);
+    }
+  };
+
   const events = useMemo(() => {
     return groupEventsByDate(incident?.events);
   }, [incident]);
@@ -146,6 +171,15 @@ const IncidentPage = () => {
       </HeaderWrapper>
       <LineBreak />
       <BodyWrapper>
+        <RefreshButton onClick={refreshIncident} disabled={isRefreshing}>
+          {isRefreshing ? (
+            <>
+              <img src={loading} alt="loading icon" />
+            </>
+          ) : (
+            <i className="material-icons">refresh</i>
+          )}
+        </RefreshButton>
         {Object.entries(events).map(([date, events_list]) => (
           <React.Fragment key={date}>
             <StyledDate>{date}</StyledDate>
@@ -278,64 +312,44 @@ export type Incident = {
   chart_name: string;
 };
 
-const LineBreak = styled.div`
-  width: calc(100% - 0px);
-  height: 2px;
-  background: #ffffff20;
-  margin: 10px 0px 35px;
-`;
-
-const IncidentMessage = styled.span`
-  display: block;
-  font-size: 16px;
-  color: #ffffff88;
-  margin-top: 10px;
-`;
-
-const IncidentStatus = styled.span`
-  display: block;
-  font-size: 16px;
-  color: #ffffff88;
-  margin-top: 10px;
-  > i {
-    margin-left: 5px;
-    color: ${(props: { status: string }) => {
-      if (props.status === "ONGOING") {
-        return "#f5cb42";
-      }
-      return "#00d12a";
-    }};
-  }
-`;
-
-const BackButton = styled.div`
+const RefreshButton = styled.button`
   position: absolute;
-  top: 0px;
   right: 0px;
-  display: flex;
-  width: 36px;
+  top: 20px;
+  border: 1px solid #ffffff00;
+  border-radius: 50%;
+  background: inherit;
+  color: #ffffff;
   cursor: pointer;
-  height: 36px;
+  display: flex;
   align-items: center;
   justify-content: center;
-  border: 1px solid #ffffff55;
-  border-radius: 100px;
-  background: #ffffff11;
+  width: 35px;
+  height: 35px;
+
+  > i {
+    font-size: 20px;
+  }
+  > img {
+    width: 20px;
+    height: 20px;
+  }
 
   :hover {
-    background: #ffffff22;
-    > img {
-      opacity: 1;
-    }
+    color: #ffffff88;
+    border-color: #ffffff88;
   }
 `;
 
-const BackButtonImg = styled.img`
-  width: 16px;
-  opacity: 0.75;
+const LineBreak = styled.div`
+  width: calc(100% - 0px);
+  height: 2px;
+  background: #ffffff20;
+  margin: 10px 0px 35px;
 `;
 
 const BodyWrapper = styled.div`
+  position: relative;
   width: 100%;
   height: 100%;
   overflow: hidden;

+ 27 - 0
dashboard/src/main/home/cluster-dashboard/dashboard/incidents/IncidentsTable.tsx

@@ -27,6 +27,8 @@ const IncidentsTable = () => {
   const [incidents, setIncidents] = useState<IncidentsWithoutEvents[]>(null);
   const [hasError, setHasError] = useState(false);
 
+  const [isRefreshing, setIsRefreshing] = useState(false);
+
   useEffect(() => {
     let isSubscribed = true;
     setIncidents(null);
@@ -58,6 +60,29 @@ const IncidentsTable = () => {
     };
   }, [currentCluster, currentProject]);
 
+  const refreshIncidents = async () => {
+    setIsRefreshing(true);
+    try {
+      const incidents = await api
+        .getIncidents<{ incidents: IncidentsWithoutEvents[] }>(
+          "<token>",
+          {},
+          {
+            project_id: currentProject.id,
+            cluster_id: currentCluster.id,
+          }
+        )
+        .then((res) => res.data?.incidents || []);
+
+      setIncidents(incidents);
+    } catch (err) {
+      setHasError(true);
+      setCurrentError(err);
+    } finally {
+      setIsRefreshing(false);
+    }
+  };
+
   const columns = useMemo(() => {
     return [
       {
@@ -136,6 +161,8 @@ const IncidentsTable = () => {
         pushFiltered(`/cluster-dashboard/incidents/${row?.original?.id}`, []);
       }}
       hasError={hasError}
+      onRefresh={refreshIncidents}
+      isRefreshing={isRefreshing}
     />
   );
 };

+ 29 - 0
dashboard/src/main/home/cluster-dashboard/expanded-chart/incidents/IncidentsTable.tsx

@@ -23,6 +23,7 @@ const IncidentsTable = ({
 
   const [incidents, setIncidents] = useState<IncidentsWithoutEvents[]>(null);
   const [hasError, setHasError] = useState(false);
+  const [isRefreshing, setIsRefreshing] = useState(false);
 
   useEffect(() => {
     let isSubscribed = true;
@@ -58,6 +59,32 @@ const IncidentsTable = ({
     };
   }, [currentCluster, currentProject]);
 
+  const refreshIncidents = async () => {
+    setIsRefreshing(true);
+    try {
+      const incidents = await api
+        .getIncidentsByReleaseName<{ incidents: IncidentsWithoutEvents[] }>(
+          "<token>",
+          {
+            namespace: namespace,
+            release_name: releaseName,
+          },
+          {
+            project_id: currentProject.id,
+            cluster_id: currentCluster.id,
+          }
+        )
+        .then((res) => res.data?.incidents || []);
+
+      setIncidents(incidents);
+    } catch (err) {
+      setHasError(true);
+      setCurrentError(err);
+    } finally {
+      setIsRefreshing(false);
+    }
+  };
+
   const columns = useMemo(() => {
     return [
       {
@@ -99,6 +126,8 @@ const IncidentsTable = ({
             );
           }}
           hasError={hasError}
+          onRefresh={refreshIncidents}
+          isRefreshing={isRefreshing}
         />
       </StyledCard>
     </TableWrapper>