Преглед изворни кода

Adding analytics to cost consent open and confirm (#3161)

Feroze Mohideen пре 2 година
родитељ
комит
ba5ee5e238

+ 17 - 1
api/server/handlers/user/update_onboarding_step.go

@@ -33,9 +33,25 @@ func (v *UpdateOnboardingStepHandler) ServeHTTP(w http.ResponseWriter, r *http.R
 		return
 	}
 
+	if request.Step == "cost-consent-opened" {
+		v.Config().AnalyticsClient.Track(analytics.CostConsentOpenedTrack(&analytics.CostConsentOpenedTrackOpts{
+			UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+			Provider:            request.Provider,
+			Email:               user.Email,
+			FirstName:           user.FirstName,
+			LastName:            user.LastName,
+			CompanyName:         user.CompanyName,
+		}))
+	}
+
 	if request.Step == "cost-consent-complete" {
-		v.Config().AnalyticsClient.Track(analytics.CostConsentTrack(&analytics.CostConsentTrackOpts{
+		v.Config().AnalyticsClient.Track(analytics.CostConsentCompletedTrack(&analytics.CostConsentCompletedTrackOpts{
 			UserScopedTrackOpts: analytics.GetUserScopedTrackOpts(user.ID),
+			Provider:            request.Provider,
+			Email:               user.Email,
+			FirstName:           user.FirstName,
+			LastName:            user.LastName,
+			CompanyName:         user.CompanyName,
 		}))
 	}
 

+ 2 - 1
api/types/user.go

@@ -82,5 +82,6 @@ type UpdateUserInfoRequest struct {
 }
 
 type UpdateOnboardingStepRequest struct {
-	Step string `json:"step" form:"required,max=255"`
+	Step     string `json:"step" form:"required,max=255"`
+	Provider string `json:"provider"`
 }

+ 4 - 31
dashboard/src/components/AWSCostConsent.tsx

@@ -1,58 +1,31 @@
-import React, { useState, useContext, useMemo } from "react";
+import React, { useState, useContext } from "react";
 import styled from "styled-components";
 
-import { integrationList } from "shared/common";
 import { Context } from "shared/Context";
 import api from "shared/api";
 
-import ProvisionerForm from "components/ProvisionerForm";
-import CloudFormationForm from "components/CloudFormationForm";
-import CredentialsForm from "components/CredentialsForm";
-import Helper from "components/form-components/Helper";
 import Modal from "./porter/Modal";
 import Text from "./porter/Text";
 import Spacer from "./porter/Spacer";
 import Fieldset from "./porter/Fieldset";
-import Checkbox from "./porter/Checkbox";
 import Button from "./porter/Button";
 import ExpandableSection from "./porter/ExpandableSection";
 import Input from "./porter/Input";
 import Link from "./porter/Link";
-import AzureCredentialForm from "components/AzureCredentialForm";
 
 type Props = {
   setCurrentStep: (step: string) => void;
   setShowCostConfirmModal: (show: boolean) => void;
+  markCostConsentComplete: () => void;
 };
 
 const AWSCostConsent: React.FC<Props> = ({
   setCurrentStep,
   setShowCostConfirmModal,
+  markCostConsentComplete,
 }) => {
-  const { currentProject } = useContext(Context);
   const [confirmCost, setConfirmCost] = useState("");
 
-  const markStepCostConsent = async () => {
-    try {
-      const res = await api.updateOnboardingStep(
-        "<token>",
-        { step: "cost-consent-complete" },
-        {}
-      );
-    } catch (err) {
-      console.log(err);
-    }
-    try {
-      const res = await api.inviteAdmin(
-        "<token>",
-        {},
-        { project_id: currentProject.id }
-      );
-    } catch (err) {
-      console.log(err);
-    }
-  };
-
   return (
     <>
       <Modal
@@ -137,7 +110,7 @@ const AWSCostConsent: React.FC<Props> = ({
           onClick={() => {
             setShowCostConfirmModal(false);
             setConfirmCost("");
-            markStepCostConsent();
+            markCostConsentComplete();
             setCurrentStep("credentials");
           }}
         >

+ 3 - 23
dashboard/src/components/AzureCostConsent.tsx

@@ -23,36 +23,16 @@ import AzureCredentialForm from "components/AzureCredentialForm";
 type Props = {
   setCurrentStep: (step: string) => void;
   setShowCostConfirmModal: (show: boolean) => void;
+  markCostConsentComplete: () => void;
 };
 
 const AzureCostConsent: React.FC<Props> = ({
   setCurrentStep,
   setShowCostConfirmModal,
+  markCostConsentComplete,
 }) => {
-  const { currentProject } = useContext(Context);
   const [confirmCost, setConfirmCost] = useState("");
 
-  const markStepCostConsent = async () => {
-    try {
-      const res = await api.updateOnboardingStep(
-        "<token>",
-        { step: "cost-consent-complete" },
-        {}
-      );
-    } catch (err) {
-      console.log(err);
-    }
-    try {
-      const res = await api.inviteAdmin(
-        "<token>",
-        {},
-        { project_id: currentProject.id }
-      );
-    } catch (err) {
-      console.log(err);
-    }
-  };
-
   return (
     <>
       <Modal
@@ -140,7 +120,7 @@ const AzureCostConsent: React.FC<Props> = ({
           onClick={() => {
             setShowCostConfirmModal(false);
             setConfirmCost("");
-            markStepCostConsent();
+            markCostConsentComplete();
             setCurrentStep("credentials");
           }}
         >

+ 48 - 25
dashboard/src/components/ProvisionerFlow.tsx

@@ -9,15 +9,6 @@ import ProvisionerForm from "components/ProvisionerForm";
 import CloudFormationForm from "components/CloudFormationForm";
 import CredentialsForm from "components/CredentialsForm";
 import Helper from "components/form-components/Helper";
-import Modal from "./porter/Modal";
-import Text from "./porter/Text";
-import Spacer from "./porter/Spacer";
-import Fieldset from "./porter/Fieldset";
-import Checkbox from "./porter/Checkbox";
-import Button from "./porter/Button";
-import ExpandableSection from "./porter/ExpandableSection";
-import Input from "./porter/Input";
-import Link from "./porter/Link";
 import AzureCredentialForm from "components/AzureCredentialForm";
 import AWSCostConsent from "./AWSCostConsent";
 import AzureCostConsent from "./AzureCostConsent";
@@ -26,7 +17,7 @@ const providers = ["aws", "gcp", "azure"];
 
 type Props = {};
 
-const ProvisionerFlow: React.FC<Props> = ({}) => {
+const ProvisionerFlow: React.FC<Props> = ({ }) => {
   const {
     usage,
     hasBillingEnabled,
@@ -47,25 +38,22 @@ const ProvisionerFlow: React.FC<Props> = ({}) => {
     return usage?.current.clusters >= usage?.limit.clusters;
   }, [usage]);
 
-  const markStepCostConsent = async () => {
+  const markStepCostConsent = async (step: string, provider: string) => {
     try {
-      const res = await api.updateOnboardingStep(
+      await api.updateOnboardingStep(
         "<token>",
-        { step: "cost-consent-complete" },
+        { step, provider },
         {}
       );
     } catch (err) {
       console.log(err);
     }
-    try {
-      const res = await api.inviteAdmin(
-        "<token>",
-        {},
-        { project_id: currentProject.id }
-      );
-    } catch (err) {
-      console.log(err);
-    }
+  };
+
+  const openCostConsentModal = (provider: string) => {
+    setSelectedProvider(provider);
+    setShowCostConfirmModal(true);
+    markStepCostConsent("cost-consent-opened", provider);
   };
 
   if (currentStep === "cloud") {
@@ -92,8 +80,7 @@ const ProvisionerFlow: React.FC<Props> = ({}) => {
                         provider === "gcp"
                       )
                     ) {
-                      setSelectedProvider(provider);
-                      setShowCostConfirmModal(true);
+                      openCostConsentModal(provider)
                     }
                   }}
                 >
@@ -112,13 +99,49 @@ const ProvisionerFlow: React.FC<Props> = ({}) => {
             <AWSCostConsent
               setCurrentStep={setCurrentStep}
               setShowCostConfirmModal={setShowCostConfirmModal}
+              markCostConsentComplete={() => {
+                try {
+                  markStepCostConsent("cost-consent-complete", "aws")
+                } catch (err) {
+                  console.log(err);
+                }
+
+                if (currentProject != null) {
+                  try {
+                    api.inviteAdmin(
+                      "<token>",
+                      {},
+                      { project_id: currentProject.id }
+                    );
+                  } catch (err) {
+                    console.log(err);
+                  }
+                }
+              }}
             />
           )) ||
             (selectedProvider === "azure" && (
               <AzureCostConsent
                 setCurrentStep={setCurrentStep}
                 setShowCostConfirmModal={setShowCostConfirmModal}
-              />
+                markCostConsentComplete={() => {
+                  try {
+                    markStepCostConsent("cost-consent-complete", "azure")
+                  } catch (err) {
+                    console.log(err);
+                  }
+                  if (currentProject != null) {
+                    try {
+                      api.inviteAdmin(
+                        "<token>",
+                        {},
+                        { project_id: currentProject.id }
+                      );
+                    } catch (err) {
+                      console.log(err);
+                    }
+                  }
+                }} />
             )))}
       </>
     );

+ 7 - 23
dashboard/src/main/home/app-dashboard/new-app-flow/NewAppFlow.tsx

@@ -3,7 +3,6 @@ import styled from "styled-components";
 import { RouteComponentProps, withRouter } from "react-router";
 import _ from "lodash";
 import yaml from "js-yaml";
-import github from "assets/github-white.png";
 
 import { Context } from "shared/Context";
 import api from "shared/api";
@@ -21,19 +20,12 @@ import Container from "components/porter/Container";
 
 import SourceSettings from "./SourceSettings";
 import Services from "./Services";
-import EnvGroupArray, {
-  KeyValueType,
-} from "main/home/cluster-dashboard/env-groups/EnvGroupArray";
+import EnvGroupArray, { KeyValueType } from "main/home/cluster-dashboard/env-groups/EnvGroupArray";
 import GithubActionModal from "./GithubActionModal";
-import {
-  ActionConfigType,
-  GithubActionConfigType,
-  RepoType,
-} from "shared/types";
+import { ActionConfigType } from "shared/types";
 import Error from "components/porter/Error";
-import { string, z } from "zod";
 import { PorterJson, PorterYamlSchema, createFinalPorterYaml } from "./schema";
-import { ReleaseService, Service } from "./serviceTypes";
+import { Service } from "./serviceTypes";
 import GithubConnectModal from "./GithubConnectModal";
 import Link from "components/porter/Link";
 
@@ -52,7 +44,6 @@ interface FormState {
   selectedSourceType: SourceType | undefined;
   serviceList: Service[];
   envVariables: KeyValueType[];
-  releaseCommand: string;
 }
 
 const INITIAL_STATE: FormState = {
@@ -60,7 +51,6 @@ const INITIAL_STATE: FormState = {
   selectedSourceType: undefined,
   serviceList: [],
   envVariables: [],
-  releaseCommand: "",
 };
 
 const Validators: {
@@ -70,7 +60,6 @@ const Validators: {
   selectedSourceType: (value: SourceType | undefined) => value !== undefined,
   serviceList: (value: Service[]) => value.length > 0,
   envVariables: (value: KeyValueType[]) => true,
-  releaseCommand: (value: string) => true,
 };
 
 type Detected = {
@@ -93,27 +82,22 @@ type Provider =
     integration_id: number;
   };
 const NewAppFlow: React.FC<Props> = ({ ...props }) => {
-  const [templateName, setTemplateName] = useState("");
   const [porterYamlPath, setPorterYamlPath] = useState("");
 
   const [imageUrl, setImageUrl] = useState("");
   const [imageTag, setImageTag] = useState("latest");
   const { currentCluster, currentProject } = useContext(Context);
   const [deploying, setDeploying] = useState<boolean>(false);
-  const [deploymentError, setDeploymentError] = useState<string | undefined>(
-    undefined
-  );
+  const [deploymentError, setDeploymentError] = useState<string | undefined>(undefined);
   const [currentStep, setCurrentStep] = useState<number>(0);
   const [existingStep, setExistingStep] = useState<number>(0);
   const [formState, setFormState] = useState<FormState>(INITIAL_STATE);
-  const [actionConfig, setActionConfig] = useState<ActionConfigType>({
-    ...defaultActionConfig,
-  });
+  const [actionConfig, setActionConfig] = useState<ActionConfigType>(defaultActionConfig);
   const [buildView, setBuildView] = useState<string>("buildpacks");
   const [branch, setBranch] = useState("");
-  const [dockerfilePath, setDockerfilePath] = useState(null);
+  const [dockerfilePath, setDockerfilePath] = useState("./Dockerfile");
   const [procfilePath, setProcfilePath] = useState(null);
-  const [folderPath, setFolderPath] = useState(null);
+  const [folderPath, setFolderPath] = useState("./");
   const [buildConfig, setBuildConfig] = useState({});
   const [porterYaml, setPorterYaml] = useState("");
   const [showGHAModal, setShowGHAModal] = useState<boolean>(false);

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

@@ -2417,6 +2417,7 @@ const getIncidentEvents = baseApi<
 const updateOnboardingStep = baseApi<
   {
     step: string;
+    provider?: string;
   },
   {}
 >("POST", (pathParams) => {

+ 1 - 0
internal/analytics/track_events.go

@@ -8,6 +8,7 @@ const (
 	UserVerifyEmail SegmentEvent = "User Verified Email"
 	ProjectCreate   SegmentEvent = "New Project Event"
 
+	CostConsentOpened      SegmentEvent = "Cost Consent Opened"
 	CostConsentComplete    SegmentEvent = "Cost Consent Complete"
 	CredentialStepComplete SegmentEvent = "Credential Step Complete"
 	PreProvisionCheck      SegmentEvent = "Pre Provision Check Started"

+ 37 - 4
internal/analytics/tracks.go

@@ -132,14 +132,47 @@ func ProjectCreateTrack(opts *ProjectCreateTrackOpts) segmentTrack {
 	)
 }
 
-// CostConsentTrackOpts are the options for creating a track when a user completes the cost consent
-type CostConsentTrackOpts struct {
+// CostConsentOpenedTrackOpts are the options for creating a track when a user opens the cost consent
+type CostConsentOpenedTrackOpts struct {
 	*UserScopedTrackOpts
+	Provider string
+	Email       string
+	FirstName   string
+	LastName    string
+	CompanyName string
+}
+
+// CostConsentCompletedTrack returns a track for when a user completes the cost consent
+func CostConsentOpenedTrack(opts *CostConsentOpenedTrackOpts) segmentTrack {
+	additionalProps := make(map[string]interface{})
+	additionalProps["provider"] = opts.Provider
+	additionalProps["email"] = opts.Email
+	additionalProps["name"] = opts.FirstName + " " + opts.LastName
+	additionalProps["company"] = opts.CompanyName
+
+	return getSegmentUserTrack(
+		opts.UserScopedTrackOpts,
+		getDefaultSegmentTrack(additionalProps, CostConsentOpened),
+	)
+}
+
+// CostConsentCompletedTrackOpts are the options for creating a track when a user completes the cost consent
+type CostConsentCompletedTrackOpts struct {
+	*UserScopedTrackOpts
+	Provider string
+	Email       string
+	FirstName   string
+	LastName    string
+	CompanyName string
 }
 
-// CostConsentTrack returns a track for when a user completes the cost consent
-func CostConsentTrack(opts *CostConsentTrackOpts) segmentTrack {
+// CostConsentCompletedTrack returns a track for when a user completes the cost consent
+func CostConsentCompletedTrack(opts *CostConsentCompletedTrackOpts) segmentTrack {
 	additionalProps := make(map[string]interface{})
+	additionalProps["provider"] = opts.Provider
+	additionalProps["email"] = opts.Email
+	additionalProps["name"] = opts.FirstName + " " + opts.LastName
+	additionalProps["company"] = opts.CompanyName
 
 	return getSegmentUserTrack(
 		opts.UserScopedTrackOpts,