Explorar el Código

add generalized banner component + styled notif settings

jusrhee hace 4 años
padre
commit
b532db8f61

+ 1 - 2
cli/cmd/login/server.go

@@ -127,8 +127,7 @@ const successScreen = `
     <meta charset='UTF-8'>
     <title>Porter | Login</title>
     <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
-    <link href="https://fonts.googleapis.com/css?family=Assistant:400,700|Noto+Sans:400,600,700|Work+Sans:400,500,600|Source+Sans+Pro:400,600,700|Hind+Siliguri:500|Cabin:400,600" rel="stylesheet">
-    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
+    <link href="https://fonts.googleapis.com/css?family=Work+Sans:400,500,600" rel="stylesheet">
     <link href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0/katex.min.css" rel="stylesheet" />
     <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap" rel="stylesheet">
     <style>

+ 6 - 2
dashboard/babel.config.json

@@ -1,4 +1,8 @@
 {
   "plugins": ["lodash"],
-  "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"]
-}
+  "presets": [
+    "@babel/preset-env",
+    "@babel/preset-react",
+    "@babel/preset-typescript"
+  ]
+}

+ 4 - 0
dashboard/src/assets/Iconly/Bulk/Info Square.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path opacity="0.4" d="M16.34 1.9998H7.67C4.28 1.9998 2 4.3798 2 7.9198V16.0898C2 19.6198 4.28 21.9998 7.67 21.9998H16.34C19.73 21.9998 22 19.6198 22 16.0898V7.9198C22 4.3798 19.73 1.9998 16.34 1.9998Z" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.1247 8.1893C11.1247 8.6713 11.5157 9.0643 11.9947 9.0643C12.4877 9.0643 12.8797 8.6713 12.8797 8.1893C12.8797 7.7073 12.4877 7.3143 12.0047 7.3143C11.5197 7.3143 11.1247 7.7073 11.1247 8.1893ZM12.8697 11.3621C12.8697 10.8801 12.4767 10.4871 11.9947 10.4871C11.5127 10.4871 11.1197 10.8801 11.1197 11.3621V15.7821C11.1197 16.2641 11.5127 16.6571 11.9947 16.6571C12.4767 16.6571 12.8697 16.2641 12.8697 15.7821V11.3621Z" fill="white"/>
+</svg>

+ 56 - 0
dashboard/src/components/Banner.tsx

@@ -0,0 +1,56 @@
+import React from "react";
+import styled from "styled-components";
+
+import info from "assets/info.svg";
+import warning from "assets/warning.png";
+
+interface Props {
+  type?: string;
+  children: React.ReactNode;
+}
+
+const Banner: React.FC<Props> = ({ type, children }) => {
+  const renderIcon = () => {
+    if (type === "error" || type === "warning") {
+      return <i className="material-icons-round">warning</i>;
+    }
+    return <img src={info} />;
+  };
+
+  return (
+    <StyledBanner
+      color={type === "error" ? "#ff385d" : type === "warning" && "#f5cb42"}
+    >
+      {renderIcon()}
+      {children}
+    </StyledBanner>
+  );
+};
+
+export default Banner;
+
+const StyledBanner = styled.div<{ color?: string }>`
+  height: 40px;
+  width: 100%;
+  margin: 5px 0 10px;
+  font-size: 13px;
+  font-family: "Work Sans", sans-serif;
+  display: flex;
+  border: 1px solid ${(props) => props.color || "#ffffff00"};
+  border-radius: 8px;
+  padding-left: 14px;
+  color: ${(props) => props.color || "#ffffff"};
+  align-items: center;
+  background: #ffffff11;
+  > img {
+    margin-right: 10px;
+    width: 20px;
+  }
+  > i {
+    margin-right: 10px;
+    font-size: 18px;
+  }
+  > a {
+    color: ${(props) => props.color || "#ffffff"};
+  }
+`;

+ 25 - 0
dashboard/src/components/Placeholder.tsx

@@ -0,0 +1,25 @@
+import React from "react";
+import styled from "styled-components";
+
+interface Props {
+  height?: string;
+  children: React.ReactNode;
+}
+
+const Placeholder: React.FC<Props> = ({ height, children }) => {
+  return <StyledPlaceholder height={height}>{children}</StyledPlaceholder>;
+};
+
+export default Placeholder;
+
+const StyledPlaceholder = styled.div<{ height: string }>`
+  width: 100%;
+  height: ${(props) => props.height || "100px"};
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 13px;
+  color: #ffffff44;
+  border-radius: 5px;
+  background: #ffffff11;
+`;

+ 1 - 1
dashboard/src/components/porter-form/PorterForm.tsx

@@ -8,7 +8,7 @@ import {
   ResourceListField,
   Section,
   SelectField,
-  ServiceIPListField
+  ServiceIPListField,
 } from "./types";
 import TabRegion, { TabOption } from "../TabRegion";
 import Heading from "../form-components/Heading";

+ 5 - 1
dashboard/src/components/porter-form/field-components/ArrayInput.tsx

@@ -1,6 +1,10 @@
 import React from "react";
 import styled from "styled-components";
-import { ArrayInputField, ArrayInputFieldState, GetFinalVariablesFunction } from "../types";
+import {
+  ArrayInputField,
+  ArrayInputFieldState,
+  GetFinalVariablesFunction,
+} from "../types";
 import useFormField from "../hooks/useFormField";
 
 const ArrayInput: React.FC<ArrayInputField> = (props) => {

+ 5 - 1
dashboard/src/components/porter-form/field-components/Checkbox.tsx

@@ -1,5 +1,9 @@
 import React from "react";
-import { CheckboxField, CheckboxFieldState, GetFinalVariablesFunction } from "../types";
+import {
+  CheckboxField,
+  CheckboxFieldState,
+  GetFinalVariablesFunction,
+} from "../types";
 import CheckboxRow from "../../form-components/CheckboxRow";
 import useFormField from "../hooks/useFormField";
 

+ 5 - 1
dashboard/src/components/porter-form/field-components/Select.tsx

@@ -1,5 +1,9 @@
 import React, { useContext } from "react";
-import { GetFinalVariablesFunction, SelectField, SelectFieldState } from "../types";
+import {
+  GetFinalVariablesFunction,
+  SelectField,
+  SelectFieldState,
+} from "../types";
 import Selector from "../../Selector";
 import styled from "styled-components";
 import useFormField from "../hooks/useFormField";

+ 5 - 1
dashboard/src/components/porter-form/hooks/useFormField.tsx

@@ -1,6 +1,10 @@
 import { useContext, useEffect } from "react";
 import { PorterFormContext } from "../PorterFormContextProvider";
-import { PorterFormFieldFieldState, PorterFormFieldValidationState, PorterFormVariableList } from "../types";
+import {
+  PorterFormFieldFieldState,
+  PorterFormFieldValidationState,
+  PorterFormVariableList,
+} from "../types";
 
 interface FormFieldData<T> {
   state: T;

+ 2 - 15
dashboard/src/index.html

@@ -81,17 +81,8 @@
       content="Kubernetes powered PaaS that runs in your own cloud."
     />
     <meta property="og:url" content="https://porter.run" />
-
-    <link
-      href="https://fonts.googleapis.com/icon?family=Material+Icons"
-      rel="stylesheet"
-    />
-    <link
-      href="https://fonts.googleapis.com/css?family=Assistant:400,700|Noto+Sans:400,600,700|Work+Sans:400,500,600|Source+Sans+Pro:400,600,700|Hind+Siliguri:500|Cabin:400,600"
-      rel="stylesheet"
-    />
     <link
-      href="https://fonts.googleapis.com/icon?family=Material+Icons"
+      href="https://fonts.googleapis.com/css?family=Work+Sans:400,500,600"
       rel="stylesheet"
     />
     <link
@@ -99,11 +90,7 @@
       rel="stylesheet"
     />
     <link
-      href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined"
-      rel="stylesheet"
-    />
-    <link
-      href="https://fonts.googleapis.com/icon?family=Roboto+Mono"
+      href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Round"
       rel="stylesheet"
     />
   </head>

+ 6 - 1
dashboard/src/main/home/cluster-dashboard/ClusterDashboard.tsx

@@ -6,7 +6,12 @@ import { Route, Switch } from "react-router-dom";
 
 import { Context } from "shared/Context";
 import { ChartType, ClusterType } from "shared/types";
-import { getQueryParam, PorterUrl, pushFiltered, pushQueryParams } from "shared/routing";
+import {
+  getQueryParam,
+  PorterUrl,
+  pushFiltered,
+  pushQueryParams,
+} from "shared/routing";
 
 import DashboardHeader from "./DashboardHeader";
 import ChartList from "./chart/ChartList";

+ 79 - 56
dashboard/src/main/home/cluster-dashboard/expanded-chart/NotificationSettingsSection.tsx

@@ -1,12 +1,14 @@
 import React, { useContext, useState, useEffect } from "react";
-import Heading from "../../../../components/form-components/Heading";
-import CheckboxRow from "../../../../components/form-components/CheckboxRow";
-import Helper from "../../../../components/form-components/Helper";
-import SaveButton from "../../../../components/SaveButton";
-import api from "../../../../shared/api";
-import { Context } from "../../../../shared/Context";
-import { ChartType } from "../../../../shared/types";
-import Loading from "../../../../components/Loading";
+import Heading from "components/form-components/Heading";
+import CheckboxRow from "components/form-components/CheckboxRow";
+import Helper from "components/form-components/Helper";
+import SaveButton from "components/SaveButton";
+import api from "shared/api";
+import { Context } from "shared/Context";
+import { ChartType } from "shared/types";
+import Loading from "components/Loading";
+import Banner from "components/Banner";
+import styled from "styled-components";
 
 const NOTIF_CATEGORIES = ["success", "fail"];
 
@@ -105,62 +107,72 @@ const NotificationSettingsSection: React.FC<Props> = (props) => {
   return (
     <>
       <Heading>Notification Settings</Heading>
+      <Helper>Configure notification settings for this application.</Helper>
       {initLoading ? (
         <Loading />
       ) : !hasRelease ? (
-        <Heading>
-          This message appears when the release isn't in the database, so Porter
-          can't laod in notifications for it
-        </Heading>
+        <Banner type="error">
+          Notifications unavailable. Porter could not find this application in
+          the database.
+        </Banner>
       ) : (
         <>
-          {hasNotifications != null && !hasNotifications && (
-            <Helper>
-              This message appears when there are no notification integrations
-              for the project
-            </Helper>
-          )}
-          <CheckboxRow
-            label={"Notifications Enabled"}
-            checked={notificationsOn}
-            toggle={() => setNotificationsOn(!notificationsOn)}
-            disabled={props.disabled}
-          />
-          {notificationsOn && (
+          {hasNotifications != null && !hasNotifications ? (
+            <Banner type="warning">
+              No integration has been set up for notifications.{" "}
+              <A href="http://localhost:8080/integrations/slack">
+                Connect to Slack
+              </A>
+            </Banner>
+          ) : (
             <>
-              <Helper>Send notifications on:</Helper>
-              {Object.entries(categories).map(([k, v]: [string, boolean]) => {
-                return (
-                  <React.Fragment key={k}>
-                    <CheckboxRow
-                      label={k}
-                      checked={v}
-                      toggle={() =>
-                        setCategories((prev) => {
-                          return {
-                            ...prev,
-                            [k]: !v,
-                          };
-                        })
-                      }
-                      disabled={props.disabled}
-                    />
-                  </React.Fragment>
-                );
-              })}
+              <CheckboxRow
+                label={"Enable notifications"}
+                checked={notificationsOn}
+                toggle={() => setNotificationsOn(!notificationsOn)}
+                disabled={props.disabled}
+              />
+              {notificationsOn && (
+                <>
+                  <Helper>Send notifications on:</Helper>
+                  {Object.entries(categories).map(
+                    ([k, v]: [string, boolean]) => {
+                      return (
+                        <React.Fragment key={k}>
+                          <CheckboxRow
+                            label={`Deploy ${k}`}
+                            checked={v}
+                            toggle={() =>
+                              setCategories((prev) => {
+                                return {
+                                  ...prev,
+                                  [k]: !v,
+                                };
+                              })
+                            }
+                            disabled={props.disabled}
+                          />
+                        </React.Fragment>
+                      );
+                    }
+                  )}
+                </>
+              )}
+              <br />
+              <SaveButton
+                onClick={() => saveChanges()}
+                text={"Save"}
+                clearPosition={true}
+                statusPosition={"right"}
+                disabled={props.disabled || initLoading || saveLoading}
+                status={
+                  saveLoading ? "loading" : numSaves > 0 ? "successful" : null
+                }
+                saveText={"Saving . . ."}
+              />
+              <Br />
             </>
           )}
-          <SaveButton
-            onClick={() => saveChanges()}
-            text={"Save Changes"}
-            clearPosition={true}
-            statusPosition={"right"}
-            disabled={props.disabled || initLoading || saveLoading}
-            status={
-              saveLoading ? "loading" : numSaves > 0 ? "successful" : null
-            }
-            saveText={"Saving . . ."}
-          />
         </>
       )}
     </>
@@ -168,3 +180,14 @@ const NotificationSettingsSection: React.FC<Props> = (props) => {
 };
 
 export default NotificationSettingsSection;
+
+const A = styled.a`
+  text-decoration: underline;
+  cursor: pointer;
+  margin-left: 5px;
+`;
+
+const Br = styled.div`
+  width: 100%;
+  height: 10px;
+`;

+ 5 - 2
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/MetricNormalizer.ts

@@ -5,7 +5,7 @@ import {
   MetricsNetworkDataResponse,
   MetricsNGINXErrorsDataResponse,
   AvailableMetrics,
-  MetricsHpaReplicasDataResponse, 
+  MetricsHpaReplicasDataResponse,
   MetricsNGINXLatencyDataResponse,
   NormalizedMetricsData,
 } from "./types";
@@ -40,7 +40,10 @@ export class MetricNormalizer {
     if (this.kind.includes("nginx:errors")) {
       return this.parseNGINXErrorsMetrics(this.metric_results);
     }
-    if (this.kind.includes("nginx:latency") || this.kind.includes("nginx:latency-histogram")) {
+    if (
+      this.kind.includes("nginx:latency") ||
+      this.kind.includes("nginx:latency-histogram")
+    ) {
       return this.parseNGINXLatencyMetrics(this.metric_results);
     }
     if (this.kind.includes("hpa_replicas")) {

+ 5 - 1
dashboard/src/main/home/dashboard/ClusterList.tsx

@@ -3,7 +3,11 @@ import styled from "styled-components";
 
 import { Context } from "shared/Context";
 import api from "shared/api";
-import { ClusterType, DetailedClusterType, DetailedIngressError } from "shared/types";
+import {
+  ClusterType,
+  DetailedClusterType,
+  DetailedIngressError,
+} from "shared/types";
 import Helper from "components/form-components/Helper";
 import { pushFiltered } from "shared/routing";
 

+ 1 - 1
dashboard/src/main/home/modals/EditInviteOrCollaboratorModal.tsx

@@ -130,7 +130,7 @@ const ModalTitle = styled.div`
   margin: 0px 0px 13px;
   display: flex;
   flex: 1;
-  font-family: "Assistant";
+  font-family: "Work Sans", sans-serif;
   font-size: 18px;
   color: #ffffff;
   user-select: none;

+ 4 - 1
dashboard/src/main/home/modals/LoadEnvGroupModal.tsx

@@ -9,7 +9,10 @@ import { Context } from "shared/Context";
 import Loading from "components/Loading";
 import SaveButton from "components/SaveButton";
 import { KeyValue } from "components/form-components/KeyValueArray";
-import { EnvGroupData, formattedEnvironmentValue } from "../cluster-dashboard/env-groups/EnvGroup";
+import {
+  EnvGroupData,
+  formattedEnvironmentValue,
+} from "../cluster-dashboard/env-groups/EnvGroup";
 
 type PropsType = {
   namespace: string;

+ 6 - 1
dashboard/src/shared/Context.tsx

@@ -1,6 +1,11 @@
 import React, { Component } from "react";
 
-import { CapabilityType, ClusterType, ContextProps, ProjectType } from "shared/types";
+import {
+  CapabilityType,
+  ClusterType,
+  ContextProps,
+  ProjectType,
+} from "shared/types";
 
 import { pushQueryParams } from "shared/routing";
 

+ 1 - 1
dashboard/src/shared/api.tsx

@@ -1045,7 +1045,7 @@ const getPorterAgentIsInstalled = baseApi<
 
 const installPorterAgent = baseApi<
   {},
-  { project_id: number, cluster_id: number }
+  { project_id: number; cluster_id: number }
 >("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/agent/deploy?cluster_id=${pathParams.cluster_id}`;
 });