Skip to content

Commit

Permalink
🪟 🔧 Refactor connection creation flow (#5859)
Browse files Browse the repository at this point in the history
Co-authored-by: Chandler Prall <chandler.prall@gmail.com>
  • Loading branch information
josephkmh and chandlerprall committed Apr 14, 2023
1 parent 17482e6 commit bec240c
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const useAppMonitoringService = (): AppMonitoringServiceProviderValue =>
export const AppMonitoringServiceProvider: React.FC<React.PropsWithChildren<unknown>> = ({ children }) => {
const trackAction = (action: string, context?: Record<string, unknown>) => {
if (!datadogRum.getInternalContext()) {
console.debug(`trackAction(${action}) failed because RUM is not initialized.`);
console.debug(`trackAction(${action}) failed because RUM is not initialized. Context:`, context);
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export enum AppActionCodes {
* LaunchDarkly did not load in time and was ignored
*/
LD_LOAD_TIMEOUT = "LD_LOAD_TIMEOUT",
UNEXPECTED_CONNECTION_FLOW_STATE = "UNEXPECTED_CONNECTION_FLOW_STATE",
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { useLocation, useNavigate } from "react-router-dom";
import { Navigate, useLocation, useNavigate } from "react-router-dom";

import { LoadingPage, MainPageWithScroll } from "components";
import { MainPageWithScroll } from "components";
import { CloudInviteUsersHint } from "components/CloudInviteUsersHint";
import { HeadTitle } from "components/common/HeadTitle";
import { ConnectionBlock } from "components/connection/ConnectionBlock";
Expand All @@ -12,9 +12,12 @@ import { PageHeader } from "components/ui/PageHeader";
import { StepsIndicator } from "components/ui/StepsIndicator";

import { useTrackPage, PageTrackingCodes } from "hooks/services/Analytics";
import { AppActionCodes, useAppMonitoringService } from "hooks/services/AppMonitoringService";
import { FeatureItem, useFeature } from "hooks/services/Feature";
import { useFormChangeTrackerService } from "hooks/services/FormChangeTracker";
import { InlineEnrollmentCallout } from "packages/cloud/components/experiments/FreeConnectorProgram/InlineEnrollmentCallout";
import { RoutePaths } from "pages/routePaths";
import { useCurrentWorkspaceId } from "services/workspaces/WorkspacesService";
import { ConnectorDocumentationWrapper } from "views/Connector/ConnectorDocumentationLayout";

import { ConnectionCreateDestinationForm } from "./ConnectionCreateDestinationForm";
Expand All @@ -23,46 +26,28 @@ import ExistingEntityForm from "./ExistingEntityForm";
import { hasDestinationId, hasSourceId, usePreloadData } from "./usePreloadData";

enum StepsTypes {
CREATE_ENTITY = "createEntity",
CREATE_CONNECTOR = "createConnector",
CREATE_CONNECTION = "createConnection",
}

enum EntityStepsTypes {
SOURCE = "source",
DESTINATION = "destination",
CONNECTION = "connection",
CREATE_SOURCE = "source",
CREATE_DESTINATION = "destination",
CREATE_CONNECTION = "connection",
}

export const CreateConnectionPage: React.FC = () => {
useTrackPage(PageTrackingCodes.CONNECTIONS_NEW);
const location = useLocation();
const { formatMessage } = useIntl();
const fcpEnabled = useFeature(FeatureItem.FreeConnectorProgram);
const workspaceId = useCurrentWorkspaceId();
const { trackAction } = useAppMonitoringService();

const navigate = useNavigate();
const { clearAllFormChanges } = useFormChangeTrackerService();

// TODO: select UI and behavior based on the route using, you know, the router
const locationType = location.pathname.split("/")[3];

const type: EntityStepsTypes =
locationType === "connections"
? EntityStepsTypes.CONNECTION
: locationType === "source"
? EntityStepsTypes.DESTINATION
: EntityStepsTypes.SOURCE;

const [currentStep, setCurrentStep] = useState(
hasSourceId(location.state) && hasDestinationId(location.state)
? StepsTypes.CREATE_CONNECTION
: hasSourceId(location.state) && !hasDestinationId(location.state)
? StepsTypes.CREATE_CONNECTOR
: StepsTypes.CREATE_ENTITY
);

const [currentEntityStep, setCurrentEntityStep] = useState(
hasSourceId(location.state) ? EntityStepsTypes.DESTINATION : EntityStepsTypes.SOURCE
? StepsTypes.CREATE_DESTINATION
: StepsTypes.CREATE_SOURCE
);

const { destinationDefinition, sourceDefinition, source, destination } = usePreloadData();
Expand All @@ -75,8 +60,11 @@ export const CreateConnectionPage: React.FC = () => {
sourceId: id,
},
});
setCurrentEntityStep(EntityStepsTypes.DESTINATION);
setCurrentStep(StepsTypes.CREATE_CONNECTOR);
if (!destination) {
setCurrentStep(StepsTypes.CREATE_DESTINATION);
} else {
setCurrentStep(StepsTypes.CREATE_CONNECTION);
}
};

const onSelectExistingDestination = (id: string) => {
Expand All @@ -87,120 +75,99 @@ export const CreateConnectionPage: React.FC = () => {
destinationId: id,
},
});
setCurrentEntityStep(EntityStepsTypes.CONNECTION);
setCurrentStep(StepsTypes.CREATE_CONNECTION);
if (!source) {
setCurrentStep(StepsTypes.CREATE_SOURCE);
} else {
setCurrentStep(StepsTypes.CREATE_CONNECTION);
}
};

const renderStep = () => {
if (currentStep === StepsTypes.CREATE_ENTITY || currentStep === StepsTypes.CREATE_CONNECTOR) {
if (currentEntityStep === EntityStepsTypes.SOURCE) {
return (
<>
{type === EntityStepsTypes.CONNECTION && (
<ExistingEntityForm type="source" onSubmit={onSelectExistingSource} />
)}
<ConnectionCreateSourceForm
afterSubmit={() => {
if (type === "connection") {
setCurrentEntityStep(EntityStepsTypes.DESTINATION);
setCurrentStep(StepsTypes.CREATE_CONNECTOR);
} else {
setCurrentEntityStep(EntityStepsTypes.CONNECTION);
setCurrentStep(StepsTypes.CREATE_CONNECTION);
}
}}
/>
<CloudInviteUsersHint connectorType="source" />
</>
);
} else if (currentEntityStep === EntityStepsTypes.DESTINATION) {
return (
<>
{type === EntityStepsTypes.CONNECTION && (
<ExistingEntityForm type="destination" onSubmit={onSelectExistingDestination} />
)}
<ConnectionCreateDestinationForm
afterSubmit={() => {
setCurrentEntityStep(EntityStepsTypes.CONNECTION);
if (currentStep === StepsTypes.CREATE_SOURCE) {
return (
<>
<ExistingEntityForm type="source" onSubmit={onSelectExistingSource} />
<ConnectionCreateSourceForm
afterSubmit={() => {
if (!destination) {
setCurrentStep(StepsTypes.CREATE_DESTINATION);
} else {
setCurrentStep(StepsTypes.CREATE_CONNECTION);
}}
/>
<CloudInviteUsersHint connectorType="destination" />
</>
);
}
}

if (!source || !destination) {
console.error("unexpected state met");
return <LoadingPage />;
}
}}
/>
<CloudInviteUsersHint connectorType="source" />
</>
);
} else if (currentStep === StepsTypes.CREATE_DESTINATION) {
return (
<>
<ExistingEntityForm type="destination" onSubmit={onSelectExistingDestination} />
<ConnectionCreateDestinationForm
afterSubmit={() => {
if (!source) {
setCurrentStep(StepsTypes.CREATE_SOURCE);
} else {
setCurrentStep(StepsTypes.CREATE_CONNECTION);
}
}}
/>
<CloudInviteUsersHint connectorType="destination" />
</>
);
} else if (currentStep === StepsTypes.CREATE_CONNECTION && source && destination) {
return <CreateConnectionForm source={source} destination={destination} />;
}

return <CreateConnectionForm source={source} destination={destination} />;
trackAction(AppActionCodes.UNEXPECTED_CONNECTION_FLOW_STATE, {
currentStep,
sourceId: source?.sourceId,
destinationId: destination?.destinationId,
workspaceId,
});
return (
<Navigate to={`/${RoutePaths.Workspaces}/${workspaceId}/${RoutePaths.Connection}/${RoutePaths.ConnectionNew}`} />
);
};

const steps =
type === "connection"
? [
{
id: StepsTypes.CREATE_ENTITY,
name: formatMessage({ id: "onboarding.createSource" }),
},
{
id: StepsTypes.CREATE_CONNECTOR,
name: formatMessage({ id: "onboarding.createDestination" }),
},
{
id: StepsTypes.CREATE_CONNECTION,
name: formatMessage({ id: "onboarding.setUpConnection" }),
},
]
: [
{
id: StepsTypes.CREATE_ENTITY,
name:
type === "destination"
? formatMessage({ id: "onboarding.createDestination" })
: formatMessage({ id: "onboarding.createSource" }),
},
{
id: StepsTypes.CREATE_CONNECTION,
name: formatMessage({ id: "onboarding.setUpConnection" }),
},
];

const titleId: string =
currentStep === "createConnection"
? "connection.newConnectionTitle"
: (
{
[EntityStepsTypes.CONNECTION]: "connection.newConnectionTitle",
[EntityStepsTypes.DESTINATION]: "destinations.newDestinationTitle",
[EntityStepsTypes.SOURCE]: "sources.newSourceTitle",
} as Record<EntityStepsTypes, string>
)[type];

const headTitle = <HeadTitle titles={[{ id: "connection.newConnectionTitle" }]} />;
const pageHeader = (
<PageHeader
title={<FormattedMessage id={titleId} />}
middleComponent={<StepsIndicator steps={steps} activeStep={currentStep} />}
/>
);
const steps = [
{
id: StepsTypes.CREATE_SOURCE,
name: formatMessage({ id: "onboarding.createSource" }),
},
{
id: StepsTypes.CREATE_DESTINATION,
name: formatMessage({ id: "onboarding.createDestination" }),
},
{
id: StepsTypes.CREATE_CONNECTION,
name: formatMessage({ id: "onboarding.setUpConnection" }),
},
];

if (currentStep === StepsTypes.CREATE_CONNECTION) {
return (
<MainPageWithScroll headTitle={headTitle} pageTitle={pageHeader}>
<MainPageWithScroll
headTitle={<HeadTitle titles={[{ id: "connection.newConnectionTitle" }]} />}
pageTitle={
<PageHeader
title={<FormattedMessage id="connection.newConnectionTitle" />}
middleComponent={<StepsIndicator steps={steps} activeStep={currentStep} />}
/>
}
>
{renderStep()}
</MainPageWithScroll>
);
}

return (
<>
{headTitle}
<HeadTitle titles={[{ id: "connection.newConnectionTitle" }]} />
<ConnectorDocumentationWrapper>
{pageHeader}
<PageHeader
title={<FormattedMessage id="connection.newConnectionTitle" />}
middleComponent={<StepsIndicator steps={steps} activeStep={currentStep} />}
/>
<FormPageContent>
{(!!source || !!destination) && (
<ConnectionBlock
Expand Down

0 comments on commit bec240c

Please sign in to comment.