import { CREATE_NEW_APPS_STEP_SUBTITLE, CREATE_NEW_APPS_STEP_TITLE, GO_BACK, SKIP_START_WITH_USE_CASE_TEMPLATES, START_FROM_SCRATCH_SUBTITLE, START_FROM_SCRATCH_TITLE, START_FROM_TEMPLATE_SUBTITLE, START_FROM_TEMPLATE_TITLE, START_WITH_DATA_CONNECT_HEADING, START_WITH_DATA_CONNECT_SUBHEADING, START_WITH_DATA_SUBTITLE, START_WITH_DATA_TITLE, createMessage, } from "@appsmith/constants/messages"; import urlBuilder from "@appsmith/entities/URLRedirect/URLAssembly"; import type { AppState } from "@appsmith/reducers"; import { getCurrentPluginIdForCreateNewApp } from "@appsmith/selectors/applicationSelectors"; import { getAssetUrl } from "@appsmith/utils/airgapHelpers"; import { firstTimeUserOnboardingInit, resetCurrentApplicationIdForCreateNewApp, resetCurrentPluginIdForCreateNewApp, } from "actions/onboardingActions"; import { fetchPlugins } from "actions/pluginActions"; import { getAllTemplates, getTemplateFilters, importTemplateIntoApplicationViaOnboardingFlow, } from "actions/templateActions"; import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants"; import { Flex, Link, Text } from "design-system"; import { isEmpty } from "lodash"; import CreateNewDatasourceTab from "pages/Editor/IntegrationEditor/CreateNewDatasourceTab"; import { getApplicationsOfWorkspace } from "@appsmith/selectors/selectedWorkspaceSelectors"; import { TemplateView } from "pages/Templates/TemplateView"; import { default as React, useCallback, useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { allTemplatesFiltersSelector, getTemplatesSelector, isImportingTemplateToAppSelector, } from "selectors/templatesSelectors"; import styled from "styled-components"; import AnalyticsUtil from "utils/AnalyticsUtil"; import history from "utils/history"; import { builderURL } from "@appsmith/RouteBuilder"; import { getDatasource, getPlugin } from "@appsmith/selectors/entitiesSelector"; import type { Plugin } from "api/PluginApi"; import { PluginPackageName, PluginType } from "entities/Action"; import DataSourceEditor from "pages/Editor/DataSourceEditor"; import { TEMP_DATASOURCE_ID } from "constants/Datasource"; import { fetchMockDatasources } from "actions/datasourceActions"; import DatasourceForm from "pages/Editor/SaaSEditor/DatasourceForm"; import type { Datasource } from "entities/Datasource"; import { fetchingEnvironmentConfigs } from "@appsmith/actions/environmentAction"; import StartWithTemplatesWrapper from "./StartWithTemplatesWrapper"; import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag"; const SectionWrapper = styled.div` display: flex; flex-direction: column; padding: 0 var(--ads-v2-spaces-7) var(--ads-v2-spaces-7); ${(props) => ` margin-top: ${props.theme.homePage.header}px; `} background: var(--ads-v2-color-gray-50); ${(props) => ` min-height: calc(100vh - ${props.theme.homePage.header}px); `} `; const BackWrapper = styled.div<{ hidden?: boolean }>` position: sticky; display: flex; justify-content: space-between; ${(props) => ` top: ${props.theme.homePage.header}px; `} background: inherit; padding: var(--ads-v2-spaces-3); z-index: 1; margin-left: -4px; ${(props) => `${props.hidden && "visibility: hidden; opacity: 0;"}`} `; const LinkWrapper = styled(Link)<{ hidden?: boolean }>` ${(props) => `${props.hidden && "visibility: hidden; opacity: 0;"}`} `; const OptionWrapper = styled.div` display: flex; align-items: center; flex-direction: column; justify-content: center; flex-grow: 1; padding: var(--ads-v2-spaces-7) 0; `; const CardsWrapper = styled.div` display: flex; gap: 16px; margin-top: 48px; flex-wrap: wrap; justify-content: center; `; const CardContainer = styled.div` display: flex; flex-direction: column; padding: 48px; border: 1px solid var(--ads-v2-color-border); width: 324px; align-items: center; text-align: center; cursor: pointer; border-radius: 4px; background: var(--ads-v2-color-bg); img { height: 160px; margin-bottom: 48px; } position: relative; `; const WithDataWrapper = styled.div` background: var(--ads-v2-color-bg); padding: var(--ads-v2-spaces-13); border: 1px solid var(--ads-v2-color-gray-300); border-radius: 5px; `; const Header = ({ subtitle, title }: { subtitle: string; title: string }) => { return ( {title} {subtitle} ); }; interface CardProps { onClick?: () => void; src: string; subTitle: string; testid: string; title: string; } const Card = ({ onClick, src, subTitle, testid, title }: CardProps) => { return ( {title} {title} {subTitle} ); }; const START_WITH_TYPE = { TEMPLATE: "TEMPLATE", DATA: "DATA", }; type TYPE_START_WITH_TYPE = keyof typeof START_WITH_TYPE; type TYPE_START_WITH_TYPE_VALUE = (typeof START_WITH_TYPE)[TYPE_START_WITH_TYPE]; const CreateNewAppsOption = ({ currentApplicationIdForCreateNewApp, onClickBack, }: { currentApplicationIdForCreateNewApp: string; onClickBack: () => void; }) => { const [useType, setUseType] = useState< TYPE_START_WITH_TYPE_VALUE | undefined >(); const [selectedTemplate, setSelectedTemplate] = useState(""); const templatesCount = useSelector( (state: AppState) => state.ui.templates.templates.length, ); const filters = useSelector(allTemplatesFiltersSelector); const isImportingTemplate = useSelector(isImportingTemplateToAppSelector); const allTemplates = useSelector(getTemplatesSelector); const createNewAppPluginId = useSelector(getCurrentPluginIdForCreateNewApp); const applications = useSelector(getApplicationsOfWorkspace); const selectedPlugin: Plugin | undefined = useSelector((state) => getPlugin(state, createNewAppPluginId || ""), ); const application = applications.find( (app) => app.id === currentApplicationIdForCreateNewApp, ); const selectedDatasource: Datasource | undefined = useSelector((state) => getDatasource(state, TEMP_DATASOURCE_ID || ""), ); const isEnabledForStartWithDataDefault = useFeatureFlag( FEATURE_FLAG.ab_start_with_data_default_enabled, ); const dispatch = useDispatch(); const onClickStartFromTemplate = () => { AnalyticsUtil.logEvent("CREATE_APP_FROM_TEMPLATE"); if (isEmpty(filters.functions)) { dispatch(getTemplateFilters()); } if (!templatesCount) { dispatch(getAllTemplates()); } setUseType(START_WITH_TYPE.TEMPLATE); }; const onClickStartFromScratch = () => { if (application) { AnalyticsUtil.logEvent("CREATE_APP_FROM_SCRATCH"); dispatch( firstTimeUserOnboardingInit( application.id, application.defaultPageId as string, ), ); } }; const onClickStartWithData = () => { AnalyticsUtil.logEvent("CREATE_APP_FROM_DATA", { [FEATURE_FLAG.ab_start_with_data_default_enabled]: isEnabledForStartWithDataDefault, }); // fetch plugins information to show list of all plugins dispatch(fetchPlugins({ workspaceId: application?.workspaceId })); dispatch(fetchMockDatasources()); if (application?.workspaceId) { dispatch( fetchingEnvironmentConfigs({ editorId: application.id, fetchDatasourceMeta: true, workspaceId: application?.workspaceId, }), ); } setUseType(START_WITH_TYPE.DATA); }; const goBackToInitialScreen = () => { setUseType(undefined); }; const getTemplateById = (id: string) => { const template = allTemplates.find((template) => template.id === id); return template; }; const resetCreateNewAppFlow = () => { dispatch(resetCurrentApplicationIdForCreateNewApp()); }; const onClickUseTemplate = (id: string) => { const template = getTemplateById(id); if (template) { AnalyticsUtil.logEvent("USE_TEMPLATE_FROM_DETAILS_PAGE_WHEN_ONBOARDING", { title: template.title, }); // When Use template is clicked on template view detail screen if (!isImportingTemplate && application) { dispatch( importTemplateIntoApplicationViaOnboardingFlow( id, template.title as string, template.pages.map((p) => p.name), application.id, application.workspaceId, ), ); } } }; const addAnalyticEventsForSkip = () => { if (useType === START_WITH_TYPE.TEMPLATE) { if (selectedTemplate) { const template = getTemplateById(selectedTemplate); if (template) { AnalyticsUtil.logEvent( "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_TEMPLATE_DETAILS_PAGE", { title: template.title }, ); } } else { AnalyticsUtil.logEvent( "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_START_FROM_TEMPLATE_PAGE", ); } } else if (useType === START_WITH_TYPE.DATA) { if (createNewAppPluginId) { AnalyticsUtil.logEvent( "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_DATASOURCE_FORM_PAGE", { pluginId: createNewAppPluginId, [FEATURE_FLAG.ab_start_with_data_default_enabled]: isEnabledForStartWithDataDefault, }, ); } else { AnalyticsUtil.logEvent( "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_START_FROM_DATA_PAGE", { [FEATURE_FLAG.ab_start_with_data_default_enabled]: isEnabledForStartWithDataDefault, }, ); } } }; const onClickSkipButton = () => { if (application) { urlBuilder.updateURLParams( { applicationSlug: application.slug, applicationVersion: application.applicationVersion, applicationId: application.id, }, application.pages.map((page) => ({ pageSlug: page.slug, customSlug: page.customSlug, pageId: page.id, })), ); history.push( builderURL({ pageId: application.pages[0].id, }), ); } addAnalyticEventsForSkip(); }; const onClickBackButton = () => { if (isImportingTemplate) return; if (useType === START_WITH_TYPE.TEMPLATE) { if (selectedTemplate) { // Going back from template details view screen const template = getTemplateById(selectedTemplate); if (template) { AnalyticsUtil.logEvent( "ONBOARDING_FLOW_CLICK_BACK_BUTTON_TEMPLATE_DETAILS_PAGE", { title: template.title }, ); } setSelectedTemplate(""); } else { // Going back from start from template screen AnalyticsUtil.logEvent( "ONBOARDING_FLOW_CLICK_BACK_BUTTON_START_FROM_TEMPLATE_PAGE", ); goBackToInitialScreen(); } } else if (useType === START_WITH_TYPE.DATA) { if (createNewAppPluginId) { AnalyticsUtil.logEvent( "ONBOARDING_FLOW_CLICK_BACK_BUTTON_DATASOURCE_FORM_PAGE", { pluginId: createNewAppPluginId }, ); dispatch(resetCurrentPluginIdForCreateNewApp()); } else { // Going back from start from data screen AnalyticsUtil.logEvent( "ONBOARDING_FLOW_CLICK_BACK_BUTTON_START_FROM_DATA_PAGE", ); goBackToInitialScreen(); } } else { // Going back from create new app flow AnalyticsUtil.logEvent( "ONBOARDING_FLOW_CLICK_BACK_BUTTON_CREATE_NEW_APP_PAGE", ); onClickBack(); } }; const renderStartWithData = useCallback(() => { return (
{createNewAppPluginId && !!selectedDatasource ? ( selectedPlugin?.type === PluginType.SAAS ? ( ) : ( ) ) : ( )} ); }, [createNewAppPluginId, selectedDatasource, selectedPlugin]); const selectionOptions: CardProps[] = [ { onClick: onClickStartWithData, src: getAssetUrl(`${ASSETS_CDN_URL}/start-with-data.svg`), subTitle: createMessage(START_WITH_DATA_SUBTITLE), testid: "t--start-from-data", title: createMessage(START_WITH_DATA_TITLE), }, { onClick: onClickStartFromScratch, src: getAssetUrl(`${ASSETS_CDN_URL}/start-from-scratch.svg`), subTitle: createMessage(START_FROM_SCRATCH_SUBTITLE), testid: "t--start-from-scratch", title: createMessage(START_FROM_SCRATCH_TITLE), }, { onClick: onClickStartFromTemplate, src: getAssetUrl(`${ASSETS_CDN_URL}/start-from-templates.svg`), subTitle: createMessage(START_FROM_TEMPLATE_SUBTITLE), testid: "t--start-from-template", title: createMessage(START_FROM_TEMPLATE_TITLE), }, ]; useEffect(() => { if (application) { urlBuilder.updateURLParams( { applicationSlug: application.slug, applicationVersion: application.applicationVersion, applicationId: application.id, }, application.pages.map((page) => ({ pageSlug: page.slug, customSlug: page.customSlug, pageId: page.id, })), ); if (isEnabledForStartWithDataDefault) { onClickStartWithData(); } } }, [application]); useEffect(() => { AnalyticsUtil.logEvent("ONBOARDING_CREATE_APP_FLOW", { totalOptions: selectionOptions.length, }); return () => { resetCreateNewAppFlow(); }; }, []); const isBackButtonHidden = isEnabledForStartWithDataDefault && (!createNewAppPluginId || !selectedDatasource); return ( {isEnabledForStartWithDataDefault ? ( renderStartWithData() ) : useType === START_WITH_TYPE.TEMPLATE ? ( selectedTemplate ? ( ) : ( ) ) : useType === START_WITH_TYPE.DATA ? ( renderStartWithData() ) : ( {createMessage(CREATE_NEW_APPS_STEP_TITLE)} {createMessage(CREATE_NEW_APPS_STEP_SUBTITLE)} {selectionOptions.map((option: CardProps) => ( ))} )} ); }; export default CreateNewAppsOption;