Selaa lähdekoodia

Added resource type selector

jnfrati 4 vuotta sitten
vanhempi
sitoutus
2fd8d836fe

+ 157 - 0
dashboard/src/components/Dropdown.tsx

@@ -0,0 +1,157 @@
+import React, { useState } from "react";
+import styled from "styled-components";
+
+type Option = {
+  value: unknown;
+  label: string;
+};
+
+type DropdownProps = {
+  options: Array<Option>;
+  selectedOption: Option;
+  onSelect?: (selectedOption: Option) => unknown;
+  selectLabel?: (currentLabel: string) => void;
+  selectValue?: (currentValue: any) => void;
+};
+
+const Dropdown: React.FunctionComponent<DropdownProps> = ({
+  options,
+  selectedOption,
+  selectLabel,
+  selectValue,
+  onSelect,
+}) => {
+  const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
+
+  const handleSelectOption = (option: Option) => {
+    if (selectedOption.label === option.label) {
+      return;
+    }
+    onSelect(option);
+    typeof selectLabel === "function" && selectLabel(option.label);
+    typeof selectValue === "function" && selectValue(option.value);
+  };
+
+  const renderDropdown = () => {
+    if (isDropdownExpanded) {
+      return (
+        <>
+          <DropdownOverlay onClick={() => setIsDropdownExpanded(false)} />
+          <OptionWrapper
+            dropdownWidth="230px"
+            dropdownMaxHeight="200px"
+            onClick={() => setIsDropdownExpanded(false)}
+          >
+            {renderOptionList()}
+          </OptionWrapper>
+        </>
+      );
+    }
+  };
+
+  const renderOptionList = () => {
+    return options.map((option, i, originalArray) => {
+      return (
+        <Option
+          key={i}
+          selected={option.label === selectedOption.label}
+          onClick={() => handleSelectOption(option)}
+          lastItem={i === originalArray.length - 1}
+        >
+          {option.label}
+        </Option>
+      );
+    });
+  };
+
+  return (
+    <DropdownSelector
+      onClick={() => setIsDropdownExpanded(!isDropdownExpanded)}
+    >
+      <DropdownLabel>{selectedOption?.label}</DropdownLabel>
+      <i className="material-icons">arrow_drop_down</i>
+      {renderDropdown()}
+    </DropdownSelector>
+  );
+};
+
+export default Dropdown;
+
+const DropdownSelector = styled.div`
+  font-size: 13px;
+  font-weight: 500;
+  position: relative;
+  color: #ffffff;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  border-radius: 5px;
+  :hover {
+    > i {
+      background: #ffffff22;
+    }
+  }
+
+  > i {
+    border-radius: 20px;
+    font-size: 20px;
+    margin-left: 10px;
+  }
+`;
+
+const DropdownLabel = styled.div`
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  max-width: 200px;
+`;
+
+const DropdownOverlay = styled.div`
+  position: fixed;
+  width: 100%;
+  height: 100%;
+  z-index: 10;
+  left: 0px;
+  top: 0px;
+  cursor: default;
+`;
+
+const OptionWrapper = styled.div`
+  position: absolute;
+  left: 0;
+  top: calc(100% + 10px);
+  background: #26282f;
+  width: ${(props: { dropdownWidth: string; dropdownMaxHeight: string }) =>
+    props.dropdownWidth};
+  max-height: ${(props: { dropdownWidth: string; dropdownMaxHeight: string }) =>
+    props.dropdownMaxHeight || "300px"};
+  border-radius: 3px;
+  z-index: 999;
+  overflow-y: auto;
+  margin-bottom: 20px;
+  box-shadow: 0px 4px 10px 0px #00000088;
+`;
+
+const Option = styled.div`
+  width: 100%;
+  border-top: 1px solid #00000000;
+  border-bottom: 1px solid
+    ${(props: { selected: boolean; lastItem: boolean }) =>
+      props.lastItem ? "#ffffff00" : "#ffffff15"};
+  height: 37px;
+  font-size: 13px;
+  padding-top: 9px;
+  align-items: center;
+  padding-left: 15px;
+  cursor: pointer;
+  padding-right: 10px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  background: ${(props: { selected: boolean; lastItem: boolean }) =>
+    props.selected ? "#ffffff11" : ""};
+
+  :hover {
+    background: #ffffff22;
+  }
+`;

+ 118 - 44
dashboard/src/main/home/cluster-dashboard/dashboard/events/EventsTab.tsx

@@ -9,6 +9,7 @@ import api from "shared/api";
 import InfiniteScroll from "react-infinite-scroll-component";
 import { Event } from "../../expanded-chart/events/EventsTab";
 import { unionBy } from "lodash";
+import Dropdown from "components/Dropdown";
 
 export type KubeEvent = {
   cluster_id: number;
@@ -25,6 +26,11 @@ export type KubeEvent = {
   timestamp: string;
 };
 
+const availableResourceTypes = [
+  { label: "Pods", value: "pod" },
+  { label: "HPA", value: "hpa" },
+];
+
 const EventsTab = () => {
   const { currentCluster, currentProject } = useContext(Context);
 
@@ -34,6 +40,7 @@ const EventsTab = () => {
   const [kubeEvents, setKubeEvents] = useState<KubeEvent[]>([]);
   const [hasErrors, setHasErrors] = useState(false);
   const [hasMore, setHasMore] = useState(true);
+  const [resourceType, setResourceType] = useState(availableResourceTypes[0]);
 
   useEffect(() => {
     let isSubscribed = true;
@@ -57,32 +64,56 @@ const EventsTab = () => {
 
   useEffect(() => {
     fetchData(true);
-  }, [currentProject?.id, currentCluster?.id, hasPorterAgent]);
+  }, [
+    currentProject?.id,
+    currentCluster?.id,
+    hasPorterAgent,
+    resourceType?.value,
+  ]);
 
   const fetchData = async (clear?: boolean) => {
+    if (!hasPorterAgent) {
+      return;
+    }
     const project_id = currentProject?.id;
     const cluster_id = currentCluster?.id;
-    const skipBy = kubeEvents?.length;
-    console.log("PREVIOUS", kubeEvents?.length);
-    if (hasPorterAgent) {
-      api
-        .getKubeEvents("<token>", {}, { project_id, cluster_id, skipBy })
-        .then((res) => {
-          if (clear) {
-            setKubeEvents(res.data);
-          } else {
-            const newEvents = unionBy(kubeEvents, res.data, "id");
-            setKubeEvents(newEvents);
-            if (newEvents.length === kubeEvents?.length) {
-              setHasMore(false);
-            } else {
-              setHasMore(true);
-            }
-          }
+    let skipBy;
+    if (!clear) {
+      skipBy = kubeEvents?.length;
+    } else {
+      setHasMore(true);
+    }
+
+    const type = resourceType?.value;
+    try {
+      const newKubeEvents = await api
+        .getKubeEvents(
+          "<token>",
+          { skip: skipBy, type },
+          { project_id, cluster_id }
+        )
+        .then((res) => res.data);
 
-          setIsLoading(false);
-        })
-        .catch((error) => console.log(error));
+      if (!newKubeEvents?.length) {
+        setHasMore(false);
+        return;
+      }
+
+      if (clear) {
+        setKubeEvents(newKubeEvents);
+      } else {
+        const newEvents = unionBy(kubeEvents, newKubeEvents, "id");
+        setKubeEvents(newEvents);
+        if (newEvents.length === kubeEvents?.length) {
+          setHasMore(false);
+        } else {
+          setHasMore(true);
+        }
+      }
+
+      setIsLoading(false);
+    } catch (error) {
+      console.log(error);
     }
   };
 
@@ -126,36 +157,79 @@ const EventsTab = () => {
   }
 
   return (
-    <EventsGrid>
-      <InfiniteScroll
-        dataLength={kubeEvents.length}
-        next={fetchData}
-        hasMore={hasMore}
-        loader={<h4>Loading...</h4>}
-        scrollableTarget="HomeViewWrapper"
-      >
-        {/* {kubeEvents.map((_, index) => (
+    <EventsPageWrapper>
+      <ControlRow>
+        <Dropdown
+          selectedOption={resourceType}
+          options={availableResourceTypes}
+          onSelect={(o) => setResourceType({ ...o, value: o.value as string })}
+        />
+        {/* <RightFilters> */}
+        {/* <Dropdown
+            selectedOption={currentLimit}
+            options={availableLimitOptions}
+            onSelect={(o) =>
+              setCurrentLimit({ ...o, value: o.value as number })
+            }
+          />
+        </RightFilters> */}
+      </ControlRow>
+      <EventsGrid>
+        <InfiniteScroll
+          dataLength={kubeEvents.length}
+          next={fetchData}
+          hasMore={hasMore}
+          loader={<h4>Loading...</h4>}
+          scrollableTarget="HomeViewWrapper"
+          endMessage={
+            <h4>No events were found for the resource type you specified</h4>
+          }
+        >
+          {/* {kubeEvents.map((_, index) => (
           <div key={index}>div - #{index}</div>
         ))} */}
-        {kubeEvents.map(parseToEvent).map((event, i) => {
-          return (
-            <React.Fragment key={i}>
-              <EventCard
-                event={event as Event}
-                selectEvent={() => {
-                  console.log("SELECTED", event);
-                }}
-              />
-            </React.Fragment>
-          );
-        })}
-      </InfiniteScroll>
-    </EventsGrid>
+          {kubeEvents.map(parseToEvent).map((event, i) => {
+            return (
+              <React.Fragment key={i}>
+                <EventCard
+                  event={event as Event}
+                  selectEvent={() => {
+                    console.log("SELECTED", event);
+                  }}
+                />
+              </React.Fragment>
+            );
+          })}
+        </InfiniteScroll>
+      </EventsGrid>
+    </EventsPageWrapper>
   );
 };
 
 export default EventsTab;
 
+const RightFilters = styled.div`
+  display: flex;
+  > div {
+    :not(:last-child) {
+      margin-right: 15px;
+    }
+  }
+`;
+
+const ControlRow = styled.div`
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 35px;
+  padding-left: 0px;
+`;
+
+const EventsPageWrapper = styled.div`
+  margin-top: 35px;
+  padding-bottom: 80px;
+`;
+
 const EventsGrid = styled.div`
   display: grid;
   grid-row-gap: 15px;

+ 3 - 6
dashboard/src/shared/api.tsx

@@ -1151,12 +1151,9 @@ const installPorterAgent = baseApi<
 );
 
 const getKubeEvents = baseApi<
-  {},
-  { project_id: number; cluster_id: number; skipBy: number }
->("GET", ({ project_id, cluster_id, skipBy }) => {
-  if (skipBy > 0) {
-    return `/api/projects/${project_id}/clusters/${cluster_id}/kube_events?skip=${skipBy}`;
-  }
+  { skip: number; type: string },
+  { project_id: number; cluster_id: number }
+>("GET", ({ project_id, cluster_id }) => {
   return `/api/projects/${project_id}/clusters/${cluster_id}/kube_events`;
 });