fix: Remove guided tour code (#30387)

## Description

Removed the guided tour code.

#### PR fixes following issue(s)
Fixes https://github.com/appsmithorg/appsmith/issues/30332

#### Type of change

- Bug fix (non-breaking change which fixes an issue)
- Chore (housekeeping or task changes that don't impact user perception)

## Testing
>
#### How Has This Been Tested?
> Please describe the tests that you ran to verify your changes. Also
list any relevant details for your test configuration.
> Delete anything that is not relevant
- [ ] Manual
- [ ] JUnit
- [ ] Jest
- [ ] Cypress
>
>
#### Test Plan
> Add Testsmith test cases links that relate to this PR
>
>
#### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
>
>
>
## Checklist:
#### Dev activity
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag


#### QA activity:
- [ ] [Speedbreak
features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-)
have been covered
- [ ] Test plan covers all impacted features and [areas of
interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-)
- [ ] Test plan has been peer reviewed by project stakeholders and other
QA members
- [ ] Manually tested functionality on DP
- [ ] We had an implementation alignment call with stakeholders post QA
Round 2
- [ ] Cypress test cases have been added and approved by SDET/manual QA
- [ ] Added `Test Plan Approved` label after Cypress tests were reviewed
- [ ] Added `Test Plan Approved` label after JUnit tests were reviewed


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Chores**
  - Streamlined onboarding process by removing guided tour features.
- Refined action and message management for a more intuitive user
experience.
  - Enhanced property controls generation for better user interaction.

- **Refactor**
- Simplified various components by removing unnecessary guided tour
logic.
  - Improved application and page sagas for more efficient operation.
  - Refined editor components for a smoother user interface.

- **Documentation**
  - Updated message constants for clearer user guidance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
albinAppsmith 2024-01-24 16:31:21 +05:30 committed by GitHub
parent 85bd78674c
commit ced4ffa179
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 245 additions and 4996 deletions

View File

@ -1,15 +1,5 @@
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import type { SIGNPOSTING_STEP } from "pages/Editor/FirstTimeUserOnboarding/Utils";
import type { GUIDED_TOUR_STEPS } from "pages/Editor/GuidedTour/constants";
import type { GuidedTourState } from "reducers/uiReducers/guidedTourReducer";
import type { WidgetProps } from "widgets/BaseWidget";
export const enableGuidedTour = (payload: boolean) => {
return {
type: ReduxActionTypes.ENABLE_GUIDED_TOUR,
payload,
};
};
export const toggleInOnboardingWidgetSelection = (payload: boolean) => {
return {
@ -103,129 +93,12 @@ export const showAnonymousDataPopup = (payload: boolean) => {
};
};
export const markStepComplete = () => {
return {
type: ReduxActionTypes.GUIDED_TOUR_MARK_STEP_COMPLETED,
};
};
export const tableWidgetWasSelected = (payload: boolean) => {
return {
type: ReduxActionTypes.TABLE_WIDGET_WAS_SELECTED,
payload,
};
};
export const setCurrentStepInit = (payload: GUIDED_TOUR_STEPS) => {
return {
type: ReduxActionTypes.SET_CURRENT_STEP_INIT,
payload,
};
};
export const setCurrentStep = (payload: GUIDED_TOUR_STEPS) => {
return {
type: ReduxActionTypes.SET_CURRENT_STEP,
payload,
};
};
export const addOnboardingWidget = (payload: Partial<WidgetProps>) => {
return {
type: ReduxActionTypes.GUIDED_TOUR_ADD_WIDGET,
payload,
};
};
export const setUpTourApp = () => {
return {
type: ReduxActionTypes.SET_UP_TOUR_APP,
};
};
export const toggleLoader = (payload: boolean) => {
return {
type: ReduxActionTypes.GUIDED_TOUR_TOGGLE_LOADER,
payload,
};
};
export const toggleShowDeviationDialog = (payload: boolean) => {
return {
type: ReduxActionTypes.TOGGLE_DEVIATION_DIALOG,
payload,
};
};
export const toggleShowEndTourDialog = (payload: boolean) => {
return {
type: ReduxActionTypes.TOGGLE_END_GUIDED_TOUR_DIALOG,
payload,
};
};
export const showPostCompletionMessage = (payload: boolean) => {
return {
type: ReduxActionTypes.TOGGLE_END_GUIDED_TOUR_DIALOG,
payload,
};
};
export const forceShowContent = (payload: GUIDED_TOUR_STEPS) => {
return {
type: ReduxActionTypes.FORCE_SHOW_CONTENT,
payload,
};
};
export const updateButtonWidgetText = () => {
return {
type: ReduxActionTypes.UPDATE_BUTTON_WIDGET_TEXT,
};
};
export const showInfoMessage = () => {
return {
type: ReduxActionTypes.SHOW_INFO_MESSAGE,
};
};
export const focusWidget = (widgetName: string, propertyName?: string) => {
return {
type: ReduxActionTypes.GUIDED_TOUR_FOCUS_WIDGET,
payload: {
widgetName,
propertyName,
},
};
};
export const focusWidgetProperty = (widgetName: string) => {
return {
type: ReduxActionTypes.FOCUS_WIDGET_PROPERTY,
payload: widgetName,
};
};
export const onboardingCreateApplication = () => {
return {
type: ReduxActionTypes.ONBOARDING_CREATE_APPLICATION,
};
};
export const loadGuidedTourInit = () => {
return {
type: ReduxActionTypes.LOAD_GUIDED_TOUR_INIT,
};
};
export const loadGuidedTour = (guidedTourState: GuidedTourState) => {
return {
type: ReduxActionTypes.LOAD_GUIDED_TOUR,
payload: guidedTourState,
};
};
export const setCurrentApplicationIdForCreateNewApp = (
applicationId: string,
) => {
@ -241,13 +114,6 @@ export const resetCurrentApplicationIdForCreateNewApp = () => {
};
};
export const setCurrentPluginIdForCreateNewApp = (pluginId: string) => {
return {
type: ReduxActionTypes.SET_CURRENT_PLUGIN_ID_FOR_CREATE_NEW_APP,
payload: pluginId,
};
};
export const resetCurrentPluginIdForCreateNewApp = () => {
return {
type: ReduxActionTypes.RESET_CURRENT_PLUGIN_ID_FOR_CREATE_NEW_APP,

View File

@ -275,9 +275,6 @@ const ActionTypes = {
CANCEL_ACTION_MODAL: "CANCEL_ACTION_MODAL",
CONFIRM_ACTION_MODAL: "CONFIRM_ACTION_MODAL",
CREATE_QUERY_INIT: "CREATE_QUERY_INIT",
ONBOARDING_CREATE_APPLICATION: "ONBOARDING_CREATE_APPLICATION",
LOAD_GUIDED_TOUR: "LOAD_GUIDED_TOUR",
LOAD_GUIDED_TOUR_INIT: "LOAD_GUIDED_TOUR_INIT",
SET_IS_RECONNECTING_DATASOURCES_MODAL_OPEN:
"SET_IS_RECONNECTING_DATASOURCES_MODAL_OPEN",
FETCH_DATASOURCES_INIT: "FETCH_DATASOURCES_INIT",
@ -662,23 +659,9 @@ const ActionTypes = {
TOGGLE_FUNCTION_EXECUTE_ON_LOAD_SUCCESS:
"TOGGLE_FUNCTION_EXECUTE_ON_LOAD_SUCCESS",
SET_JS_ACTION_TO_EXECUTE_ON_PAGELOAD: "SET_JS_ACTION_TO_EXECUTE_ON_PAGELOAD",
ENABLE_GUIDED_TOUR: "ENABLE_GUIDED_TOUR",
GUIDED_TOUR_MARK_STEP_COMPLETED: "GUIDED_TOUR_MARK_STEP_COMPLETED",
SET_CURRENT_STEP: "SET_CURRENT_STEP",
SET_CURRENT_STEP_INIT: "SET_CURRENT_STEP_INIT",
GUIDED_TOUR_ADD_WIDGET: "GUIDED_TOUR_ADD_WIDGET",
TABLE_WIDGET_WAS_SELECTED: "TABLE_WIDGET_WAS_SELECTED",
SET_UP_TOUR_APP: "SET_UP_TOUR_APP",
GUIDED_TOUR_TOGGLE_LOADER: "GUIDED_TOUR_TOGGLE_LOADER",
TOGGLE_DEVIATION_DIALOG: "TOGGLE_DEVIATION_DIALOG",
TOGGLE_END_GUIDED_TOUR_DIALOG: "TOGGLE_END_GUIDED_TOUR_DIALOG",
SHOW_POST_COMPLETION_MESSAGE: "SHOW_POST_COMPLETION_MESSAGE",
GUIDED_TOUR_FOCUS_WIDGET: "GUIDED_TOUR_FOCUS_WIDGET",
GUIDED_TOUR_SET_DATASOURCE_ID: "GUIDED_TOUR_SET_DATASOURCE_ID",
FOCUS_WIDGET_PROPERTY: "FOCUS_WIDGET_PROPERTY",
SHOW_INFO_MESSAGE: "SHOW_INFO_MESSAGE",
FORCE_SHOW_CONTENT: "FORCE_SHOW_CONTENT",
UPDATE_BUTTON_WIDGET_TEXT: "UPDATE_BUTTON_WIDGET_TEXT",
UPDATE_REPLAY_ENTITY: "UPDATE_REPLAY_ENTITY",
DELETE_WORKSPACE_INIT: "DELETE_WORKSPACE_INIT",
DELETE_WORKSPACE_SUCCESS: "DELETE_WORKSPACE_SUCCESS",

View File

@ -1656,12 +1656,9 @@ export const CONTINUE = () => "Continue";
export const PROCEED_TO_NEXT_STEP = () => "Proceed to next step";
export const PROCEED = () => "Proceed";
export const COMPLETE = () => "Complete";
// -- Modal --
export const DEVIATION = () => "You are deviating from the tutorial";
export const END_CONFIRMATION = () => "Are you sure you want to end?";
export const CANCEL_DIALOG = () => "Cancel";
// -- End Tutorial --
export const END_TUTORIAL = () => "End tutorial";
export const CANCEL_DIALOG = () => "Cancel";
// -- Intro content --
export const TITLE = () =>
"In this tutorial well build a tool to display customer information";

View File

@ -55,7 +55,6 @@ import type { AppThemingState } from "reducers/uiReducers/appThemingReducer";
import type { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer";
import type { SettingsReduxState } from "@appsmith/reducers/settingsReducer";
import SettingsReducer from "@appsmith/reducers/settingsReducer";
import type { GuidedTourState } from "reducers/uiReducers/guidedTourReducer";
import type { TriggerValuesEvaluationState } from "reducers/evaluationReducers/triggerReducer";
import type { CanvasWidgetStructure } from "WidgetProvider/constants";
import type { AppSettingsPaneReduxState } from "reducers/uiReducers/appSettingsPaneReducer";
@ -119,7 +118,6 @@ export interface AppState {
datasourceName: DatasourceNameReduxState;
theme: ThemeState;
onBoarding: OnboardingState;
guidedTour: GuidedTourState;
globalSearch: GlobalSearchReduxState;
releases: ReleasesState;
websocket: WebsocketReducerState;

View File

@ -39,7 +39,6 @@ import appThemingReducer from "reducers/uiReducers/appThemingReducer";
import mainCanvasReducer from "reducers/uiReducers/mainCanvasReducer";
import focusHistoryReducer from "reducers/uiReducers/focusHistoryReducer";
import { editorContextReducer } from "@appsmith/reducers/uiReducers/editorContextReducer";
import guidedTourReducer from "reducers/uiReducers/guidedTourReducer";
import libraryReducer from "reducers/uiReducers/libraryReducer";
import appSettingsPaneReducer from "reducers/uiReducers/appSettingsPaneReducer";
import autoHeightUIReducer from "reducers/uiReducers/autoHeightReducer";
@ -76,7 +75,6 @@ export const uiReducerObject = {
theme: themeReducer,
modalAction: modalActionReducer,
onBoarding: onBoardingReducer,
guidedTour: guidedTourReducer,
globalSearch: globalSearchReducer,
releases: releasesReducer,
websocket: websocketReducer,

View File

@ -83,7 +83,6 @@ import {
import {
deleteRecentAppEntities,
getEnableStartSignposting,
setPostWelcomeTourState,
} from "utils/storage";
import {
reconnectAppLevelWebsocket,
@ -94,7 +93,6 @@ import {
getCurrentWorkspaceId,
} from "@appsmith/selectors/workspaceSelectors";
import { getCurrentStep, inGuidedTour } from "selectors/onboardingSelectors";
import { fetchPluginFormConfigs, fetchPlugins } from "actions/pluginActions";
import {
fetchDatasources,
@ -102,7 +100,6 @@ import {
} from "actions/datasourceActions";
import { failFastApiCalls } from "sagas/InitSagas";
import type { Datasource } from "entities/Datasource";
import { GUIDED_TOUR_STEPS } from "pages/Editor/GuidedTour/constants";
import { builderURL, viewerURL } from "@appsmith/RouteBuilder";
import { getDefaultPageId as selectDefaultPageId } from "sagas/selectors";
import PageApi from "api/PageApi";
@ -169,16 +166,10 @@ export function* publishApplicationSaga(
const applicationId: string = yield select(getCurrentApplicationId);
const currentPageId: string = yield select(getCurrentPageId);
const guidedTour: boolean = yield select(inGuidedTour);
const currentStep: number = yield select(getCurrentStep);
let appicationViewPageUrl = viewerURL({
const appicationViewPageUrl = viewerURL({
pageId: currentPageId,
});
if (guidedTour && currentStep === GUIDED_TOUR_STEPS.DEPLOY) {
appicationViewPageUrl += "?&guidedTourComplete=true";
yield call(setPostWelcomeTourState, true);
}
yield put(
fetchApplication({
@ -868,9 +859,6 @@ export function* importApplicationSaga(
});
}
history.push(pageURL);
const guidedTour: boolean = yield select(inGuidedTour);
if (guidedTour) return;
toast.show("Application imported successfully", {
kind: "success",

View File

@ -106,7 +106,6 @@ import DEFAULT_TEMPLATE from "templates/default";
import { getAppMode } from "@appsmith/selectors/applicationSelectors";
import { setCrudInfoModalData } from "actions/crudInfoModalActions";
import { selectWidgetInitAction } from "actions/widgetSelectionActions";
import { inGuidedTour } from "selectors/onboardingSelectors";
import {
fetchJSCollectionsForPage,
fetchJSCollectionsForPageError,
@ -114,7 +113,6 @@ import {
} from "actions/jsActionActions";
import WidgetFactory from "WidgetProvider/factory";
import { toggleShowDeviationDialog } from "actions/onboardingActions";
import { builderURL } from "@appsmith/RouteBuilder";
import { failFastApiCalls, waitForWidgetConfigBuild } from "sagas/InitSagas";
import { resizePublishedMainCanvasToLowestWidget } from "sagas/WidgetOperationUtils";
@ -529,7 +527,6 @@ export function* savePageSaga(action: ReduxAction<{ isRetry?: boolean }>) {
if (!editorConfigs) return;
const guidedTourEnabled: boolean = yield select(inGuidedTour);
const savePageRequest: SavePageRequest = getLayoutSavePayload(
widgets,
editorConfigs,
@ -575,7 +572,7 @@ export function* savePageSaga(action: ReduxAction<{ isRetry?: boolean }>) {
const { actionUpdates, messages } = savePageResponse.data;
// We do not want to show these toasts in guided tour
// Show toast messages from the server
if (messages && messages.length && !guidedTourEnabled) {
if (messages && messages.length) {
savePageResponse.data.messages.forEach((message) => {
toast.show(message, {
kind: "info",
@ -791,7 +788,6 @@ export function* createPageSaga(
createPageAction: ReduxAction<CreatePageActionPayload>,
) {
try {
const guidedTourEnabled: boolean = yield select(inGuidedTour);
const layoutSystemType: LayoutSystemTypes =
yield select(getLayoutSystemType);
const mainCanvasProps: MainCanvasReduxState =
@ -801,11 +797,6 @@ export function* createPageSaga(
mainCanvasProps.width,
);
// Prevent user from creating a new page during the guided tour
if (guidedTourEnabled) {
yield put(toggleShowDeviationDialog(true));
return;
}
const request: CreatePageRequest = createPageAction.payload;
const response: FetchPageResponse = yield call(PageApi.createPage, request);
const isValidResponse: boolean = yield validateResponse(response);

View File

@ -60,7 +60,6 @@ function ActionNameEditor(props: ActionNameEditorProps) {
return (
<NameEditorComponent
checkForGuidedTour
/**
* This component is used by module editor in EE which uses a different
* action to save the name of an action. The current callers of this component

View File

@ -3,11 +3,11 @@ import type { FeatureParams } from "./walkthroughContext";
import { DEFAULT_DELAY } from "./walkthroughContext";
import WalkthroughContext from "./walkthroughContext";
import { createPortal } from "react-dom";
import { hideIndicator } from "pages/Editor/GuidedTour/utils";
import { retryPromise } from "utils/AppsmithUtils";
import { useLocation } from "react-router-dom";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { isElementVisible } from "./utils";
import { hideIndicator } from "components/utils/Indicator";
const WalkthroughRenderer = lazy(async () => {
return retryPromise(

View File

@ -1,5 +1,4 @@
import { Icon, Text, Button, Divider } from "design-system";
import { showIndicator } from "pages/Editor/GuidedTour/utils";
import React, { useContext, useEffect, useState } from "react";
import styled from "styled-components";
import { PADDING_HIGHLIGHT, getPosition } from "./utils";
@ -12,6 +11,7 @@ import WalkthroughContext, {
isFeatureFooterDetails,
} from "./walkthroughContext";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { showIndicator } from "components/utils/Indicator";
const CLIPID = "clip__feature";
const Z_INDEX = 1000;

View File

@ -1,16 +1,17 @@
import type { LazyAnimationItem } from "utils/lazyLottie";
import lazyLottie from "utils/lazyLottie";
import indicatorAnimationURL from "assets/lottie/guided-tour-indicator.json.txt";
import { Classes as GuidedTourClasses } from "pages/Editor/GuidedTour/constants";
import {
setExplorerActiveAction,
setExplorerPinnedAction,
} from "actions/explorerActions";
import log from "loglevel";
// data-guided-tour-id - used for the rectangular highlight
// data-guided-tour-iid - iid(indicator id) used for the lottie animation show near an element
export const GuidedTourClasses = {
GUIDED_TOUR_BORDER: "guided-tour-border",
GUIDED_TOUR_SHOW_BORDER: "guided-tour-show-border",
GUIDED_TOUR_INDICATOR: "guided-tour-indicator",
};
class IndicatorHelper {
timerId!: ReturnType<typeof setTimeout>;
indicatorWrapper!: HTMLDivElement;
@ -262,7 +263,3 @@ export function showIndicator(
export function hideIndicator() {
indicatorHelperInstance.destroy();
}
export function closeSidebar() {
return [setExplorerPinnedAction(false), setExplorerActiveAction(false)];
}

View File

@ -3,8 +3,6 @@ import { useSelector, useDispatch, shallowEqual } from "react-redux";
import { isNameValid } from "utils/helpers";
import type { AppState } from "@appsmith/reducers";
import log from "loglevel";
import { inGuidedTour } from "selectors/onboardingSelectors";
import { toggleShowDeviationDialog } from "actions/onboardingActions";
import { getUsedActionNames } from "selectors/actionSelectors";
import {
ACTION_INVALID_NAME_ERROR,
@ -53,7 +51,6 @@ export const IconBox = styled.div`
`;
interface NameEditorProps {
checkForGuidedTour?: boolean;
children: (params: any) => JSX.Element;
id?: string;
name?: string;
@ -72,7 +69,6 @@ interface NameEditorProps {
function NameEditor(props: NameEditorProps) {
const {
checkForGuidedTour,
dispatchAction,
id: entityId,
idUndefinedErrorMessage,
@ -87,7 +83,6 @@ function NameEditor(props: NameEditorProps) {
if (!entityId) {
log.error(idUndefinedErrorMessage);
}
const guidedTourEnabled = useSelector(inGuidedTour);
const conflictingNames = useSelector(
(state: AppState) => getUsedActionNames(state, entityId || ""),
@ -114,15 +109,10 @@ function NameEditor(props: NameEditorProps) {
const handleNameChange = useCallback(
(name: string) => {
if (name !== entityName && !isInvalidNameForEntity(name)) {
if (checkForGuidedTour && guidedTourEnabled) {
dispatch(toggleShowDeviationDialog(true));
return;
}
dispatch(dispatchAction({ id: entityId, name }));
}
},
[dispatch, isInvalidNameForEntity, guidedTourEnabled, entityId, entityName],
[dispatch, isInvalidNameForEntity, entityId, entityName],
);
useEffect(() => {

View File

@ -11,7 +11,6 @@ import {
} from "actions/gitSyncActions";
import { restoreRecentEntitiesRequest } from "actions/globalSearchActions";
import { resetEditorSuccess } from "actions/initActions";
import { loadGuidedTourInit } from "actions/onboardingActions";
import { fetchAllPageEntityCompletion, setupPage } from "actions/pageActions";
import {
executePageLoadActions,
@ -241,7 +240,6 @@ export default class AppEditorEngine extends AppEngine {
currentTabs,
});
}
yield put(loadGuidedTourInit());
if (isFirstTimeUserOnboardingComplete) {
yield put({
type: ReduxActionTypes.SET_FIRST_TIME_USER_ONBOARDING_APPLICATION_IDS,

View File

@ -2,8 +2,8 @@ import { createGlobalStyle } from "styled-components";
import { Layers } from "constants/Layers";
import { Classes } from "@blueprintjs/core";
import { Classes as GitSyncClasses } from "pages/Editor/gitSync/constants";
import { Classes as GuidedTourClasses } from "pages/Editor/GuidedTour/constants";
import { Colors } from "constants/Colors";
import { GuidedTourClasses } from "components/utils/Indicator";
export const replayHighlightClass = "ur--has-border";

View File

@ -16,7 +16,6 @@ import { getThemeDetails, ThemeMode } from "selectors/themeSelectors";
import HtmlTitle from "../AppViewerHtmlTitle";
import { NAVIGATION_SETTINGS } from "constants/AppConstants";
import PageMenu from "pages/AppViewer/PageMenu";
import TourCompletionMessage from "pages/Editor/GuidedTour/TourCompletionMessage";
import { useHref } from "pages/Editor/utils";
import { builderURL } from "@appsmith/RouteBuilder";
import TopHeader from "./components/TopHeader";
@ -137,8 +136,6 @@ export function Navigation() {
setMenuOpen={setMenuOpen}
url={editorURL}
/>
<TourCompletionMessage />
</div>
</ThemeProvider>
);

View File

@ -26,7 +26,6 @@ import { getSelectedAppTheme } from "selectors/appThemingSelectors";
import { useSelector } from "react-redux";
import BrandingBadge from "./BrandingBadge";
import { setAppViewHeaderHeight } from "actions/appViewActions";
import { showPostCompletionMessage } from "selectors/onboardingSelectors";
import { CANVAS_SELECTOR } from "constants/WidgetConstants";
import { setupPublishedPage } from "actions/pageActions";
import usePrevious from "utils/hooks/usePrevious";
@ -56,7 +55,6 @@ import { getHideWatermark } from "@appsmith/selectors/tenantSelectors";
const AppViewerBody = styled.section<{
hasPages: boolean;
headerHeight: number;
showGuidedTourMessage: boolean;
}>`
display: flex;
flex-direction: row;
@ -92,7 +90,6 @@ function AppViewer(props: Props) {
const lightTheme = useSelector((state: AppState) =>
getThemeDetails(state, ThemeMode.LIGHT),
);
const showGuidedTourMessage = useSelector(showPostCompletionMessage);
const headerHeight = useSelector(getAppViewHeaderHeight);
const branch = getSearchQuery(search, GIT_BRANCH_QUERY_KEY);
const prevValues = usePrevious({ branch, location: props.location, pageId });
@ -219,7 +216,6 @@ function AppViewer(props: Props) {
hasPages={pages.length > 1}
headerHeight={headerHeight}
ref={focusRef}
showGuidedTourMessage={showGuidedTourMessage}
>
{isInitialized && <AppViewerPageContainer />}
</AppViewerBody>

View File

@ -25,7 +25,6 @@ import {
datasourcesEditorIdURL,
saasEditorDatasourceIdURL,
} from "@appsmith/RouteBuilder";
import { inGuidedTour } from "selectors/onboardingSelectors";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { useLocation } from "react-router";
import omit from "lodash/omit";
@ -57,7 +56,6 @@ const DataStructureListWrapper = styled.div<{ height: number }>`
const ExplorerDatasourceEntity = React.memo(
(props: ExplorerDatasourceEntityProps) => {
const { entityId } = props;
const guidedTourEnabled = useSelector(inGuidedTour);
const dispatch = useDispatch();
const icon = getPluginIcon(props.plugin);
const location = useLocation();
@ -152,10 +150,6 @@ const ExplorerDatasourceEntity = React.memo(
} else if (queryAction && isStoredDatasource(queryAction.datasource)) {
isDefaultExpanded = queryAction.datasource.id === props.datasource.id;
}
// In guided tour we want the datasource structure to be shown only when expanded
if (guidedTourEnabled) {
isDefaultExpanded = false;
}
return (
<Entity

View File

@ -62,9 +62,6 @@ import {
COMMUNITY_TEMPLATES,
APPLICATION_INVITE,
} from "@appsmith/constants/messages";
import Boxed from "./GuidedTour/Boxed";
import EndTour from "./GuidedTour/EndTour";
import { GUIDED_TOUR_STEPS } from "./GuidedTour/constants";
import { viewerURL } from "@appsmith/RouteBuilder";
import { useHref } from "./utils";
import { getAppsmithConfigs } from "@appsmith/configs";
@ -254,98 +251,93 @@ export function EditorHeader() {
<HelperBarInHeader />
<HeaderSection className="gap-x-1">
<Boxed
alternative={<EndTour />}
step={GUIDED_TOUR_STEPS.BUTTON_ONSUCCESS_BINDING}
<RealtimeAppEditors applicationId={applicationId} />
<ToggleModeButton />
{applicationId && <EditorShareButton setShowModal={setShowModal} />}
<Modal
onOpenChange={(isOpen) => setShowModal(isOpen)}
open={showModal}
>
<RealtimeAppEditors applicationId={applicationId} />
<ToggleModeButton />
{applicationId && <EditorShareButton setShowModal={setShowModal} />}
<Modal
onOpenChange={(isOpen) => setShowModal(isOpen)}
open={showModal}
>
<ModalContent style={{ width: "640px" }}>
<ModalHeader>
{createMessage(
APPLICATION_INVITE,
currentWorkspace.name,
!isGACEnabled,
)}
</ModalHeader>
<ModalBody>
<Tabs
onValueChange={(value) => setActiveTab(value)}
value={activeTab}
>
<TabsList>
<Tab data-testid="t--tab-INVITE" value="invite">
{createMessage(INVITE_TAB)}
</Tab>
<Tab data-testid="t--tab-EMBED" value="embed">
{createMessage(IN_APP_EMBED_SETTING.embed)}
</Tab>
{featureFlags.release_show_publish_app_to_community_enabled &&
cloudHosting && (
<Tab data-testid="t--tab-PUBLISH" value="publish">
{createMessage(COMMUNITY_TEMPLATES.tabTitle)}
</Tab>
)}
</TabsList>
<TabPanel value="invite">
<AppInviteUsersForm
applicationId={applicationId}
workspaceId={workspaceId}
<ModalContent style={{ width: "640px" }}>
<ModalHeader>
{createMessage(
APPLICATION_INVITE,
currentWorkspace.name,
!isGACEnabled,
)}
</ModalHeader>
<ModalBody>
<Tabs
onValueChange={(value) => setActiveTab(value)}
value={activeTab}
>
<TabsList>
<Tab data-testid="t--tab-INVITE" value="invite">
{createMessage(INVITE_TAB)}
</Tab>
<Tab data-testid="t--tab-EMBED" value="embed">
{createMessage(IN_APP_EMBED_SETTING.embed)}
</Tab>
{featureFlags.release_show_publish_app_to_community_enabled &&
cloudHosting && (
<Tab data-testid="t--tab-PUBLISH" value="publish">
{createMessage(COMMUNITY_TEMPLATES.tabTitle)}
</Tab>
)}
</TabsList>
<TabPanel value="invite">
<AppInviteUsersForm
applicationId={applicationId}
workspaceId={workspaceId}
/>
</TabPanel>
<TabPanel value="embed">
{getEmbedSnippetForm(isPrivateEmbedEnabled, setActiveTab)}
</TabPanel>
{cloudHosting && (
<TabPanel value="publish">
<CommunityTemplatesPublishInfo
onPublishClick={() =>
setShowPublishCommunityTemplateModal(true)
}
setShowHostModal={setShowModal}
/>
</TabPanel>
<TabPanel value="embed">
{getEmbedSnippetForm(isPrivateEmbedEnabled, setActiveTab)}
</TabPanel>
{cloudHosting && (
<TabPanel value="publish">
<CommunityTemplatesPublishInfo
onPublishClick={() =>
setShowPublishCommunityTemplateModal(true)
}
setShowHostModal={setShowModal}
/>
</TabPanel>
)}
</Tabs>
</ModalBody>
</ModalContent>
</Modal>
<PublishCommunityTemplateModal
onPublishSuccess={() => {
setShowPublishCommunityTemplateModal(false);
setShowModal(true);
}}
setShowModal={setShowPublishCommunityTemplateModal}
showModal={showPublishCommunityTemplateModal}
/>
<div className="flex items-center">
<Tooltip
content={createMessage(DEPLOY_BUTTON_TOOLTIP)}
placement="bottomRight"
)}
</Tabs>
</ModalBody>
</ModalContent>
</Modal>
<PublishCommunityTemplateModal
onPublishSuccess={() => {
setShowPublishCommunityTemplateModal(false);
setShowModal(true);
}}
setShowModal={setShowPublishCommunityTemplateModal}
showModal={showPublishCommunityTemplateModal}
/>
<div className="flex items-center">
<Tooltip
content={createMessage(DEPLOY_BUTTON_TOOLTIP)}
placement="bottomRight"
>
<Button
className="t--application-publish-btn"
data-guided-tour-iid="deploy"
id={"application-publish-btn"}
isDisabled={isProtectedMode}
isLoading={isPublishing}
kind="tertiary"
onClick={() => handleClickDeploy(true)}
size="md"
startIcon={"rocket"}
>
<Button
className="t--application-publish-btn"
data-guided-tour-iid="deploy"
id={"application-publish-btn"}
isDisabled={isProtectedMode}
isLoading={isPublishing}
kind="tertiary"
onClick={() => handleClickDeploy(true)}
size="md"
startIcon={"rocket"}
>
{DEPLOY_MENU_OPTION()}
</Button>
</Tooltip>
{DEPLOY_MENU_OPTION()}
</Button>
</Tooltip>
<DeployLinkButtonDialog link={deployLink} trigger="" />
</div>
</Boxed>
<DeployLinkButtonDialog link={deployLink} trigger="" />
</div>
</HeaderSection>
<Omnibar />
</HeaderWrapper>

View File

@ -12,8 +12,6 @@ import history from "utils/history";
import { useNewActionName } from "./helpers";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
import { inGuidedTour } from "selectors/onboardingSelectors";
import { toggleShowDeviationDialog } from "actions/onboardingActions";
import {
CONTEXT_COPY,
CONTEXT_DELETE,
@ -49,7 +47,6 @@ export function ActionEntityContextMenu(props: EntityContextMenuProps) {
const { canDeleteAction, canManageAction } = props;
const nextEntityName = useNewActionName();
const guidedTourEnabled = useSelector(inGuidedTour);
const dispatch = useDispatch();
const [confirmDelete, setConfirmDelete] = useState(false);
const copyActionToPage = useCallback(
@ -77,14 +74,9 @@ export function ActionEntityContextMenu(props: EntityContextMenuProps) {
);
const deleteActionFromPage = useCallback(
(actionId: string, actionName: string, onSuccess?: () => void) => {
if (guidedTourEnabled) {
dispatch(toggleShowDeviationDialog(true));
return;
}
dispatch(deleteAction({ id: actionId, name: actionName, onSuccess }));
},
[dispatch, guidedTourEnabled],
[dispatch],
);
const convertQueryToModuleOption = useConvertToModuleOptions({
@ -96,12 +88,8 @@ export function ActionEntityContextMenu(props: EntityContextMenuProps) {
const menuPages = useSelector(getPageListAsOptions);
const editActionName = useCallback(() => {
if (guidedTourEnabled) {
dispatch(toggleShowDeviationDialog(true));
return;
}
dispatch(initExplorerEntityNameEdit(props.id));
}, [dispatch, props.id, guidedTourEnabled]);
}, [dispatch, props.id]);
const showBinding = useCallback(
(actionId, actionName) =>

View File

@ -10,8 +10,6 @@ import {
} from "actions/pluginActionActions";
import { useNewActionName } from "./helpers";
import { inGuidedTour } from "selectors/onboardingSelectors";
import { toggleShowDeviationDialog } from "actions/onboardingActions";
import {
CONTEXT_COPY,
CONTEXT_DELETE,
@ -43,7 +41,6 @@ interface EntityContextMenuProps {
export function MoreActionsMenu(props: EntityContextMenuProps) {
const [isMenuOpen, toggleMenuOpen] = useToggle([false, true]);
const nextEntityName = useNewActionName();
const guidedTourEnabled = useSelector(inGuidedTour);
const [confirmDelete, setConfirmDelete] = useState(false);
const { isChangePermitted = false, isDeletePermitted = false } = props;
@ -73,14 +70,9 @@ export function MoreActionsMenu(props: EntityContextMenuProps) {
);
const deleteActionFromPage = useCallback(
(actionId: string, actionName: string) => {
if (guidedTourEnabled) {
dispatch(toggleShowDeviationDialog(true));
return;
}
dispatch(deleteAction({ id: actionId, name: actionName }));
},
[dispatch, guidedTourEnabled],
[dispatch],
);
const menuPages = useSelector((state: AppState) => {

View File

@ -21,10 +21,6 @@ import { noop } from "lodash";
import { useDispatch, useSelector } from "react-redux";
import useClick from "utils/hooks/useClick";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import { inGuidedTour } from "selectors/onboardingSelectors";
import { toggleShowDeviationDialog } from "actions/onboardingActions";
import Boxed from "pages/Editor/GuidedTour/Boxed";
import { GUIDED_TOUR_STEPS } from "pages/Editor/GuidedTour/constants";
import { getEntityCollapsibleState } from "selectors/editorContextSelectors";
import type { AppState } from "@appsmith/reducers";
import { setEntityCollapsibleState } from "actions/editorContextActions";
@ -265,7 +261,6 @@ export const Entity = forwardRef(
const isUpdating = useEntityUpdateState(props.entityId);
const isEditing = useEntityEditState(props.entityId);
const dispatch = useDispatch();
const guidedTourEnabled = useSelector(inGuidedTour);
const isOpen =
(isEntityOpen === undefined ? isDefaultExpanded : isEntityOpen) ||
@ -317,10 +312,6 @@ export const Entity = forwardRef(
const enterEditMode = useCallback(() => {
if (!canEditEntityName) return;
if (guidedTourEnabled) {
dispatch(toggleShowDeviationDialog(true));
return;
}
props.updateEntityName &&
dispatch({
type: ReduxActionTypes.INIT_EXPLORER_ENTITY_NAME_EDIT,
@ -328,7 +319,7 @@ export const Entity = forwardRef(
id: props.entityId,
},
});
}, [dispatch, props.entityId, props.updateEntityName, guidedTourEnabled]);
}, [dispatch, props.entityId, props.updateEntityName]);
const itemRef = useRef<HTMLDivElement | null>(null);
useClick(itemRef, handleClick, noop);
@ -349,101 +340,96 @@ export const Entity = forwardRef(
);
return (
<Boxed
show={props.name === "updateCustomerInfo"}
step={GUIDED_TOUR_STEPS.BIND_OTHER_FORM_WIDGETS}
<Wrapper
active={!!props.active}
className={`${EntityClassNames.WRAPPER} ${props.className}`}
ref={ref}
>
<Wrapper
<EntityItem
active={!!props.active}
className={`${EntityClassNames.WRAPPER} ${props.className}`}
ref={ref}
alwaysShowRightIcon={props.alwaysShowRightIcon}
className={`${props.highlight ? "highlighted" : ""} ${
props.active ? "active" : ""
} t--entity-item`}
data-guided-tour-id={`explorer-entity-${props.name}`}
data-guided-tour-iid={props.name}
data-testid={`t--entity-item-${props.name}`}
disabled={!!props.disabled}
highlight={!!props.highlight}
id={"entity-" + props.entityId}
isSticky={props.isSticky === true}
rightIconClickable={typeof props.onClickRightIcon === "function"}
spaced={!!props.children}
step={props.step}
>
<EntityItem
active={!!props.active}
alwaysShowRightIcon={props.alwaysShowRightIcon}
className={`${props.highlight ? "highlighted" : ""} ${
props.active ? "active" : ""
} t--entity-item`}
data-guided-tour-id={`explorer-entity-${props.name}`}
data-guided-tour-iid={props.name}
data-testid={`t--entity-item-${props.name}`}
<CollapseToggle
className={`${EntityClassNames.COLLAPSE_TOGGLE}`}
disabled={!!props.disabled}
highlight={!!props.highlight}
id={"entity-" + props.entityId}
isSticky={props.isSticky === true}
rightIconClickable={typeof props.onClickRightIcon === "function"}
spaced={!!props.children}
step={props.step}
>
<CollapseToggle
className={`${EntityClassNames.COLLAPSE_TOGGLE}`}
disabled={!!props.disabled}
isOpen={!!isOpen}
isVisible={!!props.children}
onClick={toggleChildren}
/>
<IconWrapper
className={`${EntityClassNames.ICON}`}
onClick={handleClick}
>
{props.icon}
</IconWrapper>
<EntityName
className={`${EntityClassNames.NAME}`}
enterEditMode={enterEditMode}
entityId={props.entityId}
exitEditMode={exitEditMode}
isBeta={props.isBeta}
isEditing={!!props.updateEntityName && isEditing}
name={props.name}
nameTransformFn={props.onNameEdit}
ref={itemRef}
searchKeyword={props.searchKeyword}
updateEntityName={updateNameCallback}
/>
{isUpdating && (
<SubItemWrapper>
<Spinner />
</SubItemWrapper>
)}
{props.isBeta && (
<SubItemWrapper>
<Tag isClosable={false}>
{createMessage(EXPLORER_BETA_ENTITY)}
</Tag>
</SubItemWrapper>
)}
{props.preRightIcon && (
<IconWrapper
className={`${EntityClassNames.PRE_RIGHT_ICON} w-full h-full`}
onClick={props.onClickPreRightIcon}
>
{props.preRightIcon}
</IconWrapper>
)}
{props.rightIcon && (
<IconWrapper
className={EntityClassNames.RIGHT_ICON}
onClick={props.onClickRightIcon}
>
{props.rightIcon}
</IconWrapper>
)}
{showAddButton && addButton}
{props.contextMenu && (
<ContextMenuWrapper>{props.contextMenu}</ContextMenuWrapper>
)}
</EntityItem>
<Collapse
active={props.active}
collapseRef={props.collapseRef}
isOpen={!!isOpen}
step={props.step}
isVisible={!!props.children}
onClick={toggleChildren}
/>
<IconWrapper
className={`${EntityClassNames.ICON}`}
onClick={handleClick}
>
{props.children}
</Collapse>
</Wrapper>
</Boxed>
{props.icon}
</IconWrapper>
<EntityName
className={`${EntityClassNames.NAME}`}
enterEditMode={enterEditMode}
entityId={props.entityId}
exitEditMode={exitEditMode}
isBeta={props.isBeta}
isEditing={!!props.updateEntityName && isEditing}
name={props.name}
nameTransformFn={props.onNameEdit}
ref={itemRef}
searchKeyword={props.searchKeyword}
updateEntityName={updateNameCallback}
/>
{isUpdating && (
<SubItemWrapper>
<Spinner />
</SubItemWrapper>
)}
{props.isBeta && (
<SubItemWrapper>
<Tag isClosable={false}>
{createMessage(EXPLORER_BETA_ENTITY)}
</Tag>
</SubItemWrapper>
)}
{props.preRightIcon && (
<IconWrapper
className={`${EntityClassNames.PRE_RIGHT_ICON} w-full h-full`}
onClick={props.onClickPreRightIcon}
>
{props.preRightIcon}
</IconWrapper>
)}
{props.rightIcon && (
<IconWrapper
className={EntityClassNames.RIGHT_ICON}
onClick={props.onClickRightIcon}
>
{props.rightIcon}
</IconWrapper>
)}
{showAddButton && addButton}
{props.contextMenu && (
<ContextMenuWrapper>{props.contextMenu}</ContextMenuWrapper>
)}
</EntityItem>
<Collapse
active={props.active}
collapseRef={props.collapseRef}
isOpen={!!isOpen}
step={props.step}
>
{props.children}
</Collapse>
</Wrapper>
);
},
);

View File

@ -8,8 +8,6 @@ import {
} from "@appsmith/constants/ReduxActionConstants";
import WidgetFactory from "WidgetProvider/factory";
import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
import { toggleShowDeviationDialog } from "actions/onboardingActions";
import { inGuidedTour } from "selectors/onboardingSelectors";
import type { TreeDropdownOption } from "pages/Editor/Explorer/ContextMenu";
import ContextMenu from "pages/Editor/Explorer/ContextMenu";
const WidgetTypes = WidgetFactory.widgetTypes;
@ -32,7 +30,6 @@ export function WidgetContextMenu(props: {
if (parentId) return state.ui.pageWidgets[props.pageId].dsl[parentId];
return {};
});
const guidedTourEnabled = useSelector(inGuidedTour);
const dispatch = useDispatch();
const dispatchDelete = useCallback(() => {
// If the widget is a tab we are updating the `tabs` of the property of the widget
@ -71,12 +68,8 @@ export function WidgetContextMenu(props: {
}, []);
const editWidgetName = useCallback(() => {
if (guidedTourEnabled) {
dispatch(toggleShowDeviationDialog(true));
return;
}
dispatch(initExplorerEntityNameEdit(widgetId));
}, [dispatch, widgetId, guidedTourEnabled]);
}, [dispatch, widgetId]);
const optionTree: TreeDropdownOption[] = [
{

View File

@ -15,7 +15,6 @@ import {
EMPTY_WIDGET_MAIN_TEXT,
} from "@appsmith/constants/messages";
import { selectWidgetsForCurrentPage } from "@appsmith/selectors/entitiesSelector";
import { inGuidedTour } from "selectors/onboardingSelectors";
import {
getExplorerStatus,
saveExplorerStatus,
@ -37,13 +36,9 @@ export const ExplorerWidgetGroup = memo((props: ExplorerWidgetGroupProps) => {
const applicationId = useSelector(getCurrentApplicationId);
const pageId = useSelector(getCurrentPageId) || "";
const widgets = useSelector(selectWidgetsForCurrentPage);
const guidedTour = useSelector(inGuidedTour);
let isWidgetsOpen = getExplorerStatus(applicationId, "widgets");
if (isWidgetsOpen === null || isWidgetsOpen === undefined) {
isWidgetsOpen = widgets?.children?.length === 0 || guidedTour;
saveExplorerStatus(applicationId, "widgets", isWidgetsOpen);
} else if (guidedTour) {
isWidgetsOpen = guidedTour;
isWidgetsOpen = widgets?.children?.length === 0;
saveExplorerStatus(applicationId, "widgets", isWidgetsOpen);
}

View File

@ -1,15 +1,3 @@
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import { APPLICATIONS_URL } from "constants/routes";
import type { Dispatch } from "react";
import history from "utils/history";
export const triggerWelcomeTour = (dispatch: Dispatch<any>) => {
history.push(APPLICATIONS_URL);
dispatch({
type: ReduxActionTypes.ONBOARDING_CREATE_APPLICATION,
});
};
export enum SIGNPOSTING_STEP {
CONNECT_A_DATASOURCE = "CONNECT_A_DATASOURCE",
CREATE_A_QUERY = "CREATE_A_QUERY",

View File

@ -1,48 +0,0 @@
import React from "react";
import type { ReactNode } from "react";
import { useSelector } from "react-redux";
import {
forceShowContentSelector,
getCurrentStep,
inGuidedTour,
} from "selectors/onboardingSelectors";
interface BoxedProps {
alternative?: JSX.Element;
children: ReactNode;
step: number;
// under which condition do you want to show an alternative or nothing(meaning hide)
show: boolean;
}
// Boxed(or hidden).
function Boxed(props: BoxedProps): JSX.Element | null {
const guidedTour = useSelector(inGuidedTour);
const currentStep = useSelector(getCurrentStep);
const forceShowContent = useSelector(forceShowContentSelector);
const hide =
guidedTour &&
props.show &&
forceShowContent < props.step &&
currentStep <= props.step;
if (hide) {
if (props.alternative) {
return props.alternative;
}
return null;
}
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{props.children}</>;
}
Boxed.defaultProps = {
show: true,
// Some out of bound value as by default the children
// of this component is to be hidden
step: 99,
};
export default Boxed;

View File

@ -1,86 +0,0 @@
import { useSelector } from "react-redux";
import {
enableGuidedTour,
toggleShowDeviationDialog,
toggleShowEndTourDialog,
} from "actions/onboardingActions";
import {
Button,
Modal,
ModalBody,
ModalContent,
ModalHeader,
ModalFooter,
} from "design-system";
import {
CANCEL_DIALOG,
createMessage,
DEVIATION,
END_CONFIRMATION,
} from "@appsmith/constants/messages";
import React from "react";
import { useDispatch } from "react-redux";
import {
showDeviatingDialogSelector,
showEndTourDialogSelector,
} from "selectors/onboardingSelectors";
import AnalyticsUtil from "utils/AnalyticsUtil";
function GuidedTourDialog() {
const showDeviatingDialog = useSelector(showDeviatingDialogSelector);
const showEndTourDialog = useSelector(showEndTourDialogSelector);
const dispatch = useDispatch();
const title = showDeviatingDialog
? createMessage(DEVIATION)
: createMessage(END_CONFIRMATION);
const onClose = () => {
if (showDeviatingDialog) {
dispatch(toggleShowDeviationDialog(false));
}
if (showEndTourDialog) {
dispatch(toggleShowEndTourDialog(false));
}
};
const endTour = () => {
onClose();
dispatch(enableGuidedTour(false));
AnalyticsUtil.logEvent("END_GUIDED_TOUR_CLICK");
};
return (
<Modal
onOpenChange={onClose}
open={showEndTourDialog || showDeviatingDialog}
>
<ModalContent
onEscapeKeyDown={(e) => e.preventDefault()}
// Don't close Modal when pressed outside
onInteractOutside={(e) => e.preventDefault()}
style={{ width: "640px" }}
>
<ModalHeader>{title}</ModalHeader>
<ModalBody>
You will be able to restart this tutorial at any time by clicking on{" "}
<b>Welcome tour</b> at the bottom left of the home page
</ModalBody>
<ModalFooter>
<Button
className="cancel"
kind="secondary"
onClick={onClose}
size="md"
>
{createMessage(CANCEL_DIALOG)}
</Button>
<Button className="end" onClick={endTour} size="md">
{createMessage(END_CONFIRMATION)}
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
export default GuidedTourDialog;

View File

@ -1,22 +0,0 @@
import { enableGuidedTour } from "actions/onboardingActions";
import { createMessage, END_TUTORIAL } from "@appsmith/constants/messages";
import React from "react";
import { useDispatch } from "react-redux";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { Button } from "design-system";
function EndTour() {
const dispatch = useDispatch();
const endTour = () => {
dispatch(enableGuidedTour(false));
AnalyticsUtil.logEvent("END_GUIDED_TOUR_CLICK");
};
return (
<Button kind="tertiary" onClick={endTour} size="md">
{createMessage(END_TUTORIAL)}
</Button>
);
}
export default EndTour;

View File

@ -1,476 +0,0 @@
import {
setUpTourApp,
showInfoMessage,
toggleLoader,
} from "actions/onboardingActions";
import { Button, Icon, Text } from "design-system";
import { isArray } from "lodash";
import React, { useEffect, useRef, useState } from "react";
import { useDispatch } from "react-redux";
import lazyLottie from "utils/lazyLottie";
import tickMarkAnimationURL from "assets/lottie/guided-tour-tick-mark.json.txt";
import {
getCurrentStep,
getQueryAction,
isExploringSelector,
loading,
showInfoMessageSelector,
showSuccessMessage,
} from "selectors/onboardingSelectors";
import { useSelector } from "react-redux";
import styled from "styled-components";
import { GUIDED_TOUR_STEPS, Steps } from "./constants";
import useComputeCurrentStep from "./useComputeCurrentStep";
import {
BUTTON_TEXT,
COMPLETE,
CONTINUE,
createMessage,
DESCRIPTION,
PROCEED,
PROCEED_TO_NEXT_STEP,
TITLE,
} from "@appsmith/constants/messages";
const GuideWrapper = styled.div`
user-select: text;
`;
const CardWrapper = styled.div`
width: 100%;
display: flex;
border-bottom: 1px solid var(--ads-v2-color-border);
flex-direction: column;
background: var(--ads-v2-color-bg-information);
`;
const TitleWrapper = styled.div`
align-items: center;
display: flex;
`;
const StepCount = styled.div`
background: var(--ads-v2-color-bg-emphasis-max);
color: var(--ads-v2-color-fg-on-emphasis-plus);
height: 24px;
width: 24px;
border-radius: var(--ads-v2-border-radius-circle);
display: inline-flex;
align-items: center;
justify-content: center;
margin-right: var(--ads-v2-spaces-3);
`;
const Description = styled.span<{ addLeftSpacing?: boolean }>`
font-size: 14px;
line-height: 16px;
padding-left: ${(props) => (props.addLeftSpacing ? `20px` : "0")};
margin-top: var(--ads-v2-spaces-2);
`;
const UpperContent = styled.div`
padding: var(--ads-v2-spaces-5);
flex-direction: column;
display: flex;
`;
const ContentWrapper = styled.div`
display: flex;
gap: 50px;
align-items: center;
.guided-title {
color: var(--ads-v2-color-fg-emphasis);
}
`;
const GuideButton = styled(Button)<{ isVisible?: boolean }>`
visibility: ${({ isVisible = true }) => (isVisible ? "visible" : "hidden")};
`;
const SubContentWrapper = styled.div`
display: flex;
flex: 1;
flex-direction: column;
.header {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
}
.count {
font-size: 12px;
font-weight: 600;
text-align: center;
.complete {
font-weight: 400;
}
}
`;
const Hint = styled.div`
background: var(--ads-v2-color-bg);
padding: var(--ads-v2-spaces-4);
margin-top: var(--ads-v2-spaces-5);
display: flex;
align-items: center;
border: 1px solid var(--ads-v2-color-border);
border-radius: var(--ads-v2-border-radius);
.align-vertical {
flex-direction: column;
}
.inner-wrapper {
flex: 1;
}
.hint-text {
font-size: 14px;
}
.hint-button {
margin-top: ${(props) => props.theme.spaces[6]}px;
}
.hint-steps {
display: flex;
margin-top: ${(props) => props.theme.spaces[5]}px;
}
.strike {
text-decoration: line-through;
opacity: 0.5;
}
.hint-steps-text {
margin-left: ${(props) => props.theme.spaces[4]}px;
}
`;
const HintTextWrapper = styled.div`
flex-direction: row;
display: flex;
justify-content: space-between;
align-items: center;
img {
height: 70px;
width: 152px;
}
`;
const SuccessMessageWrapper = styled.div`
display: flex;
background: var(--ads-v2-color-bg);
flex-direction: column;
border: 1px solid var(--ads-v2-color-border);
border-radius: var(--ads-v2-border-radius);
.wrapper {
padding: var(--ads-v2-spaces-4);
display: flex;
}
.info-wrapper {
padding: var(--ads-v2-spaces-4);
align-items: center;
}
.lottie-wrapper {
height: 40px;
width: 40px;
}
.title-wrapper {
display: flex;
flex: 1;
align-items: center;
justify-content: space-between;
}
.success-message {
color: var(--ads-v2-color-fg-emphasis);
}
`;
function InitialContent() {
const dispatch = useDispatch();
const isLoading = useSelector(loading);
const queryAction = useSelector(getQueryAction);
const setupFirstStep = () => {
dispatch(toggleLoader(true));
dispatch(setUpTourApp());
};
return (
<div>
<ContentWrapper>
<SubContentWrapper>
<Text className="guided-title" kind="heading-s" renderAs="h2">
{createMessage(TITLE)}
</Text>
<Description>{createMessage(DESCRIPTION)}</Description>
</SubContentWrapper>
<GuideButton
className="t--start-building"
isLoading={isLoading}
isVisible={!queryAction?.isLoading && !!queryAction?.data}
onClick={setupFirstStep}
size="md"
>
{createMessage(BUTTON_TEXT)}
</GuideButton>
</ContentWrapper>
<Hint>
<span className="hint-text">
The app is connected to a Postgres database with customers data called{" "}
<b>Customers DB</b>.
</span>
</Hint>
</div>
);
}
function GuideStepsContent(props: {
currentStep: number;
showInfoMessage: boolean;
}) {
const meta = useComputeCurrentStep(props.showInfoMessage);
const content = Steps[props.currentStep];
const [hintCount, setHintCount] = useState(0);
const currentHint = content.hints[hintCount]
? content.hints[hintCount]
: content.hints[0];
const dispatch = useDispatch();
useEffect(() => {
setHintCount(0);
}, [props.currentStep]);
useEffect(() => {
setHintCount(meta.hintCount);
}, [meta.hintCount]);
const hintSteps = currentHint.steps;
const hintButtonOnClick = () => {
if (currentHint.button && currentHint.button.onClick) {
currentHint.button.onClick(dispatch);
}
setHintCount((count) => count + 1);
};
return (
<div data-testid={"guided-tour-banner"}>
<ContentWrapper>
<SubContentWrapper>
<div className="header">
<TitleWrapper>
<StepCount>{props.currentStep}</StepCount>
<Text className="guided-title" kind="heading-s" renderAs="h2">
{content.title}
</Text>
</TitleWrapper>
<div className="count">
{props.currentStep - 1}/{GUIDED_TOUR_STEPS.DEPLOY}
{" "}
<span className="complete">{createMessage(COMPLETE)}</span>
</div>
</div>
{content.description && (
<Description>{content.description}</Description>
)}
</SubContentWrapper>
</ContentWrapper>
<Hint>
<div className="inner-wrapper hint-text">
<HintTextWrapper>
<div>{currentHint.text}</div>
{currentHint.image && <img src={currentHint.image} />}
{currentHint.button && (
<GuideButton
className="t--hint-button"
onClick={hintButtonOnClick}
size="md"
>
{createMessage(PROCEED)}
</GuideButton>
)}
</HintTextWrapper>
{isArray(hintSteps) &&
hintSteps.length &&
hintSteps.map((step, index) => {
const completed = meta.completedSubSteps.includes(index);
const className = "hint-steps" + (completed ? " strike" : "");
return (
<div className={className} key={step?.toString()}>
<Icon
color={
completed ? "var(--ads-v2-color-fg-success)" : "inherit"
}
name={completed ? "oval-check-fill" : "oval-check"}
size="md"
/>
<span className="hint-steps-text">{hintSteps[index]}</span>
</div>
);
})}
</div>
</Hint>
</div>
);
}
interface CompletionContentProps {
step: number;
showInfoMessage: boolean;
}
function CompletionContent(props: CompletionContentProps) {
const [showSuccess, setShowSuccess] = useState(!props.showInfoMessage);
const [showSuccessButton, setShowSuccessButton] = useState(false);
const info = Steps[props.step].info;
const success = Steps[props.step].success;
const dispatch = useDispatch();
const tickMarkRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (showSuccess) {
const anim = lazyLottie.loadAnimation({
path: tickMarkAnimationURL,
autoplay: true,
container: tickMarkRef?.current as HTMLDivElement,
renderer: "svg",
loop: false,
});
return () => {
anim.destroy();
};
}
}, [tickMarkRef?.current, showSuccess]);
const onSuccessButtonClick = () => {
setShowSuccess(false);
success?.onClick && success?.onClick(dispatch);
if (info) {
// We skip showing success message again
dispatch(showInfoMessage());
}
};
useEffect(() => {
if (success?.timed && showSuccess) {
setTimeout(() => {
setShowSuccessButton(true);
}, 2000);
}
}, [success?.timed, showSuccess]);
const onInfoButtonClick = () => {
info?.onClick(dispatch);
};
if (showSuccess) {
return (
<SuccessMessageWrapper>
<div className="wrapper">
<div className="lottie-wrapper" ref={tickMarkRef} />
<div className="title-wrapper">
<Text className="success-message" kind="heading-s" renderAs="h2">
{Steps[props.step].success?.text}
</Text>
{/* Show the button after a delay */}
<GuideButton
className="t--success-button"
isVisible={showSuccessButton}
onClick={onSuccessButtonClick}
size="md"
>
{success?.buttonText ?? createMessage(CONTINUE)}
</GuideButton>
</div>
</div>
</SuccessMessageWrapper>
);
} else {
return (
<SuccessMessageWrapper>
<div className="wrapper info-wrapper">
{info?.icon && (
<Icon
color="var(--ads-v2-color-fg-information)"
name={info.icon}
size="lg"
/>
)}
<Description className="info">{info?.text}</Description>
<GuideButton
className="t--info-button"
onClick={onInfoButtonClick}
size="md"
>
{info?.buttonText ?? createMessage(PROCEED_TO_NEXT_STEP)}
</GuideButton>
</div>
</SuccessMessageWrapper>
);
}
}
export interface GuideBody {
exploring: boolean;
step: number;
showInfoMessage: boolean;
}
function GuideBody(props: GuideBody) {
const successMessage = useSelector(showSuccessMessage);
if (props.exploring) {
return <InitialContent />;
} else if (successMessage || props.showInfoMessage) {
return (
<CompletionContent
showInfoMessage={props.showInfoMessage}
step={props.step}
/>
);
} else {
return (
<GuideStepsContent
currentStep={props.step}
showInfoMessage={props.showInfoMessage}
/>
);
}
}
interface GuideProps {
className?: string;
}
// Guided tour steps
function Guide(props: GuideProps) {
const exploring = useSelector(isExploringSelector);
const step = useSelector(getCurrentStep);
const showInfoMessage = useSelector(showInfoMessageSelector);
return (
<GuideWrapper className={props.className}>
<CardWrapper>
<UpperContent>
<GuideBody
exploring={exploring}
showInfoMessage={showInfoMessage}
step={step}
/>
</UpperContent>
</CardWrapper>
</GuideWrapper>
);
}
export default Guide;

View File

@ -1,160 +0,0 @@
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import Rating from "react-rating";
import { Button, Icon, Text } from "design-system";
import {
getPostWelcomeTourState,
setPostWelcomeTourState,
} from "utils/storage";
import { getQueryParams } from "utils/URLUtils";
import { useDispatch } from "react-redux";
import { showPostCompletionMessage } from "actions/onboardingActions";
import AnalyticsUtil from "utils/AnalyticsUtil";
import {
createMessage,
END_BUTTON_TEXT,
END_DESCRIPTION,
END_TITLE,
RATING_DESCRIPTION,
RATING_TEXT,
RATING_TITLE,
} from "@appsmith/constants/messages";
import history from "utils/history";
import { APPLICATIONS_URL } from "constants/routes";
const Container = styled.div`
background-color: var(--ads-v2-color-bg-success);
padding: var(--ads-v2-spaces-5);
width: 100%;
`;
const Wrapper = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
.title {
color: var(--ads-v2-color-fg-emphasis);
}
`;
const Confetti = styled.span`
font-size: 30px;
margin-right: var(--ads-v2-spaces-5);
`;
const Description = styled.div`
font-size: 14px;
line-height: 16px;
margin-top: var(--ads-v2-spaces-2);
`;
const RatingText = styled.span`
/* color: #000000; */
`;
const RatingWrapper = styled.div`
display: flex;
align-items: center;
.star {
padding: 0 5px;
}
`;
const Left = styled.div`
display: flex;
`;
function CongratulationsView() {
const [ratingComplete, setRatingComplete] = useState(false);
const [show, setShow] = useState(false);
const dispatch = useDispatch();
const onValueChanged = (value: number) => {
AnalyticsUtil.logEvent("GUIDED_TOUR_RATING", {
rating: value,
});
setRatingComplete(true);
};
useEffect(() => {
const inPostCompletionState = async () => {
const postCompletionMessage = await getPostWelcomeTourState();
const queryParams = getQueryParams();
if (queryParams.guidedTourComplete === "true" && postCompletionMessage) {
setShow(true);
dispatch(showPostCompletionMessage(true));
}
};
inPostCompletionState();
}, [dispatch]);
const hideMessage = () => {
setShow(false);
dispatch(showPostCompletionMessage(false));
setPostWelcomeTourState(false);
history.push(APPLICATIONS_URL);
};
if (!show) return null;
if (!ratingComplete) {
return (
<Container>
<Wrapper>
<Left>
<Confetti>🎉</Confetti>
<div>
<Text className="title" kind="heading-s" renderAs="h2">
{createMessage(RATING_TITLE)}
</Text>
<Description>{createMessage(RATING_DESCRIPTION)}</Description>
</div>
</Left>
<RatingWrapper>
<RatingText>{createMessage(RATING_TEXT)}</RatingText>
<Rating
emptySymbol={
<Icon
className={"t--guided-tour-rating star"}
color={"var(--ads-v2-color-fg-success)"}
name="star-line"
size="lg"
/>
}
fullSymbol={
<Icon
className={"t--guided-tour-rating star"}
color={"var(--ads-v2-color-fg-success)"}
name="star-fill"
size="lg"
/>
}
onChange={onValueChanged}
/>
</RatingWrapper>
</Wrapper>
</Container>
);
} else {
return (
<Container>
<Wrapper>
<div>
<Text className="title" kind="heading-s" renderAs="h2">
{createMessage(END_TITLE)}
</Text>
<Description>{createMessage(END_DESCRIPTION)}</Description>
</div>
<Button className="t--start-building" onClick={hideMessage} size="md">
{createMessage(END_BUTTON_TEXT)}
</Button>
</Wrapper>
</Container>
);
}
}
export default CongratulationsView;

File diff suppressed because it is too large Load Diff

View File

@ -1,558 +0,0 @@
import React from "react";
import type { ReactNode } from "react";
import type { Dispatch } from "redux";
import TableData from "assets/gifs/table_data.gif";
import DefaultText from "assets/gifs/default_text.gif";
import {
setCurrentStepInit,
addOnboardingWidget,
forceShowContent,
focusWidget,
} from "actions/onboardingActions";
import { highlightSection, showIndicator } from "./utils";
import { setExplorerPinnedAction } from "actions/explorerActions";
import { forceOpenWidgetPanel } from "actions/widgetSidebarActions";
import {
createMessage,
STEP_EIGHT_SUCCESS_TEXT,
STEP_EIGHT_TITLE,
STEP_FIVE_HINT_TEXT,
STEP_FIVE_SUCCESS_BUTTON_TEXT,
STEP_FIVE_SUCCESS_TEXT,
STEP_FIVE_TITLE,
STEP_FOUR_HINT_BUTTON_TEXT,
STEP_FOUR_SUCCESS_BUTTON_TEXT,
STEP_FOUR_SUCCESS_TEXT,
STEP_FOUR_TITLE,
STEP_NINE_TITLE,
STEP_ONE_BUTTON_TEXT,
STEP_ONE_SUCCESS_TEXT,
STEP_ONE_TITLE,
STEP_SEVEN_TITLE,
STEP_SIX_SUCCESS_BUTTON_TEXT,
STEP_SIX_SUCCESS_TEXT,
STEP_SIX_TITLE,
STEP_THREE_SUCCESS_BUTTON_TEXT,
STEP_THREE_SUCCESS_TEXT,
STEP_THREE_TITLE,
STEP_TWO_TITLE,
} from "@appsmith/constants/messages";
import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants";
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
export const Classes = {
GUIDED_TOUR_BORDER: "guided-tour-border",
GUIDED_TOUR_SHOW_BORDER: "guided-tour-show-border",
GUIDED_TOUR_INDICATOR: "guided-tour-indicator",
};
export const GuidedTourEntityNames = {
BUTTON_WIDGET: "UpdateButton",
NAME_INPUT: "NameInput",
EMAIL_INPUT: "EmailInput",
COUNTRY_INPUT: "CountryInput",
DISPLAY_IMAGE: "DisplayImage",
};
export enum GUIDED_TOUR_STEPS {
DEFAULT = 0,
RUN_QUERY = 1,
SELECT_TABLE_WIDGET = 2,
TABLE_WIDGET_BINDING = 3,
NAME_INPUT_BINDING = 4,
BIND_OTHER_FORM_WIDGETS = 5,
ADD_BUTTON_WIDGET = 6,
BUTTON_ONCLICK_BINDING = 7,
BUTTON_ONSUCCESS_BINDING = 8,
DEPLOY = 9,
}
// We are using widget blueprints to create the form like container widget
export const onboardingContainerBlueprint = {
view: [
{
type: "CANVAS_WIDGET",
position: { top: 0, left: 0 },
props: {
containerStyle: "none",
canExtend: false,
detachFromLayout: true,
children: [],
version: 1,
blueprint: {
view: [
{
type: "TEXT_WIDGET",
position: {
left: 1,
top: 1,
},
size: {
rows: 4,
cols: 48,
},
props: {
textAlign: "LEFT",
fontStyle: "BOLD",
version: 1,
textColor: "#231F20",
fontSize: "HEADING2",
text: "\uD83D\uDC68\uD83D\uDCBC Customer Update Form",
},
},
{
type: "IMAGE_WIDGET",
position: {
left: 1,
top: 6,
},
size: {
rows: 12,
cols: 16,
},
props: {
imageShape: "RECTANGLE",
defaultImage: getAssetUrl(
`${ASSETS_CDN_URL}/widgets/default.png`,
),
objectFit: "contain",
image: "{{CustomersTable.selectedRow.image}}",
dynamicBindingPathList: [{ key: "image" }],
},
},
{
type: "TEXT_WIDGET",
position: {
top: 6,
left: 19,
},
size: {
rows: 4,
cols: 8,
},
props: {
text: "Name",
textAlign: "LEFT",
fontStyle: "BOLD",
textColor: "#231F20",
version: 1,
fontSize: "PARAGRAPH",
},
},
{
type: "INPUT_WIDGET_V2",
position: {
top: 6,
left: 30,
},
size: {
rows: 4,
cols: 32,
},
props: {
inputType: "TEXT",
label: "",
},
},
{
type: "TEXT_WIDGET",
position: {
top: 10,
left: 19,
},
size: {
rows: 4,
cols: 8,
},
props: {
text: "Email",
textAlign: "LEFT",
fontStyle: "BOLD",
textColor: "#231F20",
version: 1,
fontSize: "PARAGRAPH",
},
},
{
type: "INPUT_WIDGET_V2",
position: {
top: 10,
left: 30,
},
size: {
rows: 4,
cols: 32,
},
props: {
inputType: "TEXT",
label: "",
},
},
{
type: "TEXT_WIDGET",
position: {
top: 14,
left: 19,
},
size: {
rows: 4,
cols: 10,
},
props: {
text: "Country",
textAlign: "LEFT",
fontStyle: "BOLD",
textColor: "#231F20",
version: 1,
fontSize: "PARAGRAPH",
},
},
{
type: "INPUT_WIDGET_V2",
position: {
top: 14,
left: 30,
},
size: {
rows: 4,
cols: 32,
},
props: {
inputType: "TEXT",
label: "",
},
},
],
},
},
},
],
};
interface Step {
title: string;
description?: string;
elementSelector?: string;
hints: {
text: ReactNode;
image?: string;
button?: {
text: string;
onClick?: (dispatch: Dispatch<any>) => void;
};
steps?: ReactNode[];
}[];
success?: {
text: string;
onClick?: (dispatch: Dispatch<any>) => void;
timed?: boolean;
buttonText?: string;
};
info?: {
icon: string;
text: ReactNode;
onClick: (dispatch: Dispatch<any>) => void;
buttonText?: string;
};
}
type StepsType = Record<number, Step>;
export const Steps: StepsType = {
[GUIDED_TOUR_STEPS.RUN_QUERY]: {
title: createMessage(STEP_ONE_TITLE),
elementSelector: "query-table-response",
hints: [
{
text: (
<>
This command will fetch the first 20 items in the user_data
database. Hit <b>Run</b> to see the response.
</>
),
},
],
success: {
text: createMessage(STEP_ONE_SUCCESS_TEXT),
onClick: (dispatch) => {
dispatch(setExplorerPinnedAction(true));
dispatch(setCurrentStepInit(GUIDED_TOUR_STEPS.SELECT_TABLE_WIDGET));
setTimeout(() => {
showIndicator(`[data-guided-tour-iid='CustomersTable']`, "right", {
top: 5,
left: -15,
});
}, 1000);
},
buttonText: createMessage(STEP_ONE_BUTTON_TEXT),
timed: true,
},
},
[GUIDED_TOUR_STEPS.SELECT_TABLE_WIDGET]: {
title: createMessage(STEP_TWO_TITLE),
hints: [
{
text: (
<>
<b>Click on the CustomersTable widget</b> in the explorer on the
left.
</>
),
},
],
},
[GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING]: {
title: createMessage(STEP_THREE_TITLE),
hints: [
{
text: (
<>
Bind the response by typing{" "}
<b>
<code>
&#123;&#123;
{"getCustomers.data"}&#125;&#125;
</code>
</b>{" "}
in the Table data input field on the right pane.
</>
),
image: TableData,
},
],
success: {
text: createMessage(STEP_THREE_SUCCESS_TEXT),
onClick: (dispatch) => {
// Stop hiding rest of the properties in the propery pane
dispatch(forceShowContent(GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING));
setTimeout(() => {
highlightSection("property-pane");
}, 1000);
},
timed: true,
buttonText: createMessage(STEP_THREE_SUCCESS_BUTTON_TEXT),
},
info: {
icon: "lightbulb-flash-line",
text: (
<>
The pane on the right is called the&nbsp;<b>Property Pane</b>. Here
you can modify properties, data, or styling for every widget.
</>
),
onClick: (dispatch) => {
dispatch(setCurrentStepInit(GUIDED_TOUR_STEPS.NAME_INPUT_BINDING));
dispatch(
addOnboardingWidget({
type: "CONTAINER_WIDGET",
widgetName: "CustomersInfo",
topRow: 7,
rows: 30,
columns: 29,
leftColumn: 35,
props: {
blueprint: onboardingContainerBlueprint,
},
}),
);
},
buttonText: "Got it",
},
},
[GUIDED_TOUR_STEPS.NAME_INPUT_BINDING]: {
title: createMessage(STEP_FOUR_TITLE),
hints: [
{
text: (
<>
We{"'"}ll{" "}
<b>
display the data from a table{"'"}s selected row inside an input
field.
</b>
<br /> This will let us see the data before we update it.
</>
),
button: {
text: createMessage(STEP_FOUR_HINT_BUTTON_TEXT),
onClick: (dispatch) => {
// Select the NameInput widget and focus the defaultText input field
dispatch(focusWidget("NameInput", "defaultText"));
setTimeout(() => {
showIndicator(`[data-guided-tour-iid='defaultText']`, "top", {
top: 20,
left: 0,
});
}, 1000);
},
},
},
{
text: (
<>
In the property pane of {GuidedTourEntityNames.NAME_INPUT}, add the{" "}
<b>
<code>
&#123;&#123;CustomersTable.selectedRow.name&#125;&#125;
</code>
</b>{" "}
binding to the <b>Default Text</b> property
</>
),
// Get gif from url
image: DefaultText,
},
],
success: {
text: createMessage(STEP_FOUR_SUCCESS_TEXT),
timed: true,
onClick: (dispatch) => {
dispatch(setCurrentStepInit(GUIDED_TOUR_STEPS.BIND_OTHER_FORM_WIDGETS));
dispatch(focusWidget("EmailInput", "defaultText"));
setTimeout(() => {
showIndicator(`[data-guided-tour-iid='defaultText']`, "top", {
top: 20,
left: 0,
});
}, 1000);
},
buttonText: createMessage(STEP_FOUR_SUCCESS_BUTTON_TEXT),
},
},
[GUIDED_TOUR_STEPS.BIND_OTHER_FORM_WIDGETS]: {
title: createMessage(STEP_FIVE_TITLE),
hints: [
{
text: <>{createMessage(STEP_FIVE_HINT_TEXT)}</>,
steps: [
<>
Connect <b>{GuidedTourEntityNames.EMAIL_INPUT}</b>
{"'"}s Default Text Property to{" "}
<code>
&#123;&#123;CustomersTable.selectedRow.email&#125;&#125;
</code>
</>,
<>
Connect <b>{GuidedTourEntityNames.COUNTRY_INPUT}</b>
{"'"}s Default Text Property to{" "}
<code>
&#123;&#123;CustomersTable.selectedRow.country&#125;&#125;
</code>
</>,
],
},
],
success: {
text: createMessage(STEP_FIVE_SUCCESS_TEXT),
onClick: (dispatch) => {
dispatch(setCurrentStepInit(GUIDED_TOUR_STEPS.ADD_BUTTON_WIDGET));
dispatch(setExplorerPinnedAction(true));
dispatch(forceOpenWidgetPanel(true));
setTimeout(() => {
highlightSection("widget-card-buttonwidget");
}, 2000);
},
timed: true,
buttonText: createMessage(STEP_FIVE_SUCCESS_BUTTON_TEXT),
},
},
[GUIDED_TOUR_STEPS.ADD_BUTTON_WIDGET]: {
title: createMessage(STEP_SIX_TITLE),
hints: [
{
text: (
<>
Switch to the widget pane and then <b>Drag {"&"} Drop</b> a{" "}
<b>Button</b> widget into the left bottom of container, below the
image.
</>
),
},
],
success: {
text: createMessage(STEP_SIX_SUCCESS_TEXT),
timed: true,
onClick: (dispatch) => {
dispatch(forceOpenWidgetPanel(false));
setTimeout(() => {
highlightSection("explorer-entity-updateCustomerInfo");
}, 1000);
},
buttonText: createMessage(STEP_SIX_SUCCESS_BUTTON_TEXT),
},
info: {
icon: "lightbulb-flash-line",
text: (
<>
To&nbsp;<b>update the customers</b>&nbsp;through the button, we
created an&nbsp;<b>updateCustomerInfo query</b>&nbsp;for you which is
ready to use
</>
),
onClick: (dispatch) => {
dispatch(focusWidget(GuidedTourEntityNames.BUTTON_WIDGET));
dispatch(setCurrentStepInit(GUIDED_TOUR_STEPS.BUTTON_ONCLICK_BINDING));
requestAnimationFrame(() => {
showIndicator(`[data-guided-tour-iid='onClick']`, "top", {
top: 25,
left: 0,
});
});
},
},
},
[GUIDED_TOUR_STEPS.BUTTON_ONCLICK_BINDING]: {
title: createMessage(STEP_SEVEN_TITLE),
hints: [
{
text: (
<>
Select the button widget to see the properties in the property pane.
Click the <b>+</b> button beside the onClick property to add an
action, select <b>Execute a query</b> {"&"} then select{" "}
<b>updateCustomerInfo</b> query
</>
),
},
],
},
[GUIDED_TOUR_STEPS.BUTTON_ONSUCCESS_BINDING]: {
title: createMessage(STEP_EIGHT_TITLE),
hints: [
{
text: (
<>
Expand <b>Callbacks</b> section, click the <b>+</b> button beside{" "}
<b>On success</b>, select <b>Execute a query</b> {"&"} then choose{" "}
<b>getCustomers</b> Query
</>
),
},
],
success: {
text: createMessage(STEP_EIGHT_SUCCESS_TEXT),
onClick: (dispatch) => {
dispatch(setCurrentStepInit(GUIDED_TOUR_STEPS.DEPLOY));
setTimeout(() => {
showIndicator(`[data-guided-tour-iid='deploy']`, "bottom", {
top: -6,
left: 0,
});
}, 1000);
},
timed: true,
},
},
[GUIDED_TOUR_STEPS.DEPLOY]: {
title: createMessage(STEP_NINE_TITLE),
hints: [
{
text: (
<>
Test your app and ensure there are no errors. When you are ready,
click <b>Deploy</b> to deploy this app to a URL.
</>
),
},
],
},
};

View File

@ -1,338 +0,0 @@
import {
markStepComplete,
tableWidgetWasSelected,
enableGuidedTour,
updateButtonWidgetText,
forceShowContent,
focusWidgetProperty,
setCurrentStepInit,
disableStartSignpostingAction,
} from "actions/onboardingActions";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getApplicationLastDeployedAt } from "selectors/editorSelectors";
import {
getHadReachedStep,
isQueryExecutionSuccessful,
isTableWidgetSelected,
tableWidgetHasBinding,
containerWidgetAdded,
nameInputSelector,
isNameInputBoundSelector,
isCountryInputBound,
isEmailInputBound,
isButtonWidgetPresent,
buttonWidgetHasOnClickBinding,
buttonWidgetHasOnSuccessBinding,
countryInputSelector,
} from "selectors/onboardingSelectors";
import { getBaseWidgetClassName } from "constants/componentClassNameConstants";
import { GUIDED_TOUR_STEPS, Steps } from "./constants";
import {
closeSidebar,
hideIndicator,
highlightSection,
showIndicator,
} from "./utils";
function useComputeCurrentStep(showInfoMessage: boolean) {
let step = 1;
const meta: {
completedSubSteps: number[];
hintCount: number;
} = {
completedSubSteps: [],
hintCount: 0,
};
const dispatch = useDispatch();
const hadReachedStep = useSelector(getHadReachedStep);
// Step 1(Run the query) selectors
const queryExecutedSuccessfully = useSelector(isQueryExecutionSuccessful);
// 2 selectors
const tableWidgetSelected = useSelector(isTableWidgetSelected);
// 3
const isTableWidgetBound = useSelector(tableWidgetHasBinding);
// 4
const isContainerWidgetPreset = useSelector(containerWidgetAdded);
const nameInputWidgetId = useSelector(nameInputSelector);
const isNameInputBound = useSelector(isNameInputBoundSelector);
// 5
const countryInputBound = useSelector(isCountryInputBound);
const isCountryInputSelected = useSelector(countryInputSelector);
const emailInputBound = useSelector(isEmailInputBound);
// 6
const buttonWidgetPresent = useSelector(isButtonWidgetPresent);
// 7
const buttonWidgetonClickBinding = useSelector(buttonWidgetHasOnClickBinding);
// 8
const buttonWidgetSuccessBinding = useSelector(
buttonWidgetHasOnSuccessBinding,
);
// 9
const isDeployed = useSelector(getApplicationLastDeployedAt);
// If we are on the first step
if (step === GUIDED_TOUR_STEPS.RUN_QUERY) {
// If the query is executed successfully and if the user had gone to further steps before
// i.e probably the user is here after finishing step 5. This can happen if the query is updated
// to something unexpected.
// So we have `hadReachedStep` to keep track of the furthest the user had reached.
// Initially we don't automatically go to the next step, instead the user clicks on a button in the guide
// shown on top of the screen for the user clicking on which we update the current step
if (queryExecutedSuccessfully && hadReachedStep > 1) {
step = GUIDED_TOUR_STEPS.SELECT_TABLE_WIDGET;
}
}
// On the second step
if (step === GUIDED_TOUR_STEPS.SELECT_TABLE_WIDGET) {
if (tableWidgetSelected) {
step = GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING;
// Reset back the hintcount set in previous step
meta.hintCount = 0;
}
}
if (step === GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING) {
if (
!!isTableWidgetBound &&
isContainerWidgetPreset &&
hadReachedStep > GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING
) {
step = GUIDED_TOUR_STEPS.NAME_INPUT_BINDING;
}
}
if (step === GUIDED_TOUR_STEPS.NAME_INPUT_BINDING) {
if (!!isNameInputBound && hadReachedStep > 4) {
step = GUIDED_TOUR_STEPS.BIND_OTHER_FORM_WIDGETS;
}
}
if (step === GUIDED_TOUR_STEPS.BIND_OTHER_FORM_WIDGETS) {
if (emailInputBound) {
// We tick off widgets in the UI which are bound to the selected row.
// This is to keep track of which ones are bound
meta.completedSubSteps.push(0);
}
if (countryInputBound) {
meta.completedSubSteps.push(1);
}
// Once all three widgets are bound this step is complete
if (
meta.completedSubSteps.length === 2 &&
hadReachedStep > GUIDED_TOUR_STEPS.BIND_OTHER_FORM_WIDGETS
) {
step = GUIDED_TOUR_STEPS.ADD_BUTTON_WIDGET;
}
}
if (step === GUIDED_TOUR_STEPS.ADD_BUTTON_WIDGET) {
if (buttonWidgetPresent && hadReachedStep > 6) {
step = GUIDED_TOUR_STEPS.BUTTON_ONCLICK_BINDING;
}
}
if (step === GUIDED_TOUR_STEPS.BUTTON_ONCLICK_BINDING) {
if (buttonWidgetonClickBinding) {
step = GUIDED_TOUR_STEPS.BUTTON_ONSUCCESS_BINDING;
}
}
if (step === GUIDED_TOUR_STEPS.BUTTON_ONSUCCESS_BINDING) {
if (
buttonWidgetSuccessBinding &&
hadReachedStep > GUIDED_TOUR_STEPS.BUTTON_ONSUCCESS_BINDING
) {
step = GUIDED_TOUR_STEPS.DEPLOY;
}
}
// Update the step in the store
useEffect(() => {
dispatch(setCurrentStepInit(step));
}, [step]);
// Step 1 side effects
useEffect(() => {
// Success messages, indicators and highlighted sections are shown initially
// These are not shown again i.e if the user finishes step 5 and does some changes
// which bring the step back to 1, we don't do the following changes after completing step 1
// again.
if (
step === GUIDED_TOUR_STEPS.RUN_QUERY &&
hadReachedStep <= GUIDED_TOUR_STEPS.RUN_QUERY
) {
if (queryExecutedSuccessfully) {
dispatch(forceShowContent(GUIDED_TOUR_STEPS.RUN_QUERY));
// Hide the indicator after the user has successfully run the query
hideIndicator();
// This show the success message
dispatch(markStepComplete());
setTimeout(() => {
if (Steps[GUIDED_TOUR_STEPS.RUN_QUERY].elementSelector) {
// Highlight section which shows a temporary border around the target
highlightSection(
Steps[GUIDED_TOUR_STEPS.RUN_QUERY].elementSelector,
);
}
// Adding a slight delay to wait for the table to be visible
}, 1000);
} else {
dispatch(closeSidebar());
showIndicator(`[data-guided-tour-iid='run-query']`, "top");
}
}
}, [queryExecutedSuccessfully, step, hadReachedStep]);
// Step 3(table widget binding) side effects
// Focus the tableData input in the property pane
useEffect(() => {
if (
tableWidgetSelected &&
step === GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING &&
hadReachedStep <= GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING
) {
dispatch(tableWidgetWasSelected(true));
showIndicator(`[data-guided-tour-iid='tableData']`, "top", {
top: 20,
left: 0,
});
// Focus the tableData input field
dispatch(focusWidgetProperty("tableData"));
}
}, [step, tableWidgetSelected]);
// Show success message
useEffect(() => {
if (
!!isTableWidgetBound &&
step === GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING &&
hadReachedStep <= GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING
) {
dispatch(closeSidebar());
hideIndicator();
dispatch(markStepComplete());
}
}, [isTableWidgetBound, step, hadReachedStep]);
// Step 4(Add binding to the NameInput widget) side effects
// Highlight table widget's selected row
useEffect(() => {
if (
!!isTableWidgetBound &&
step === GUIDED_TOUR_STEPS.NAME_INPUT_BINDING &&
hadReachedStep <= GUIDED_TOUR_STEPS.NAME_INPUT_BINDING
) {
if (!!nameInputWidgetId) {
// Minor timeout to wait for the elements to exist
dispatch(closeSidebar());
setTimeout(() => {
// Highlight the selected row and the NameInput widget
highlightSection(
"selected-row",
getBaseWidgetClassName(isTableWidgetBound),
"class",
);
highlightSection(
getBaseWidgetClassName(nameInputWidgetId),
undefined,
"class",
);
}, 500);
}
}
}, [isTableWidgetBound, step, hadReachedStep, nameInputWidgetId]);
// Show success message
useEffect(() => {
if (
step === GUIDED_TOUR_STEPS.NAME_INPUT_BINDING &&
hadReachedStep <= GUIDED_TOUR_STEPS.NAME_INPUT_BINDING
) {
if (!!isNameInputBound) {
hideIndicator();
dispatch(markStepComplete());
}
}
}, [isNameInputBound, step, hadReachedStep]);
// Step 5
useEffect(() => {
if (
step === GUIDED_TOUR_STEPS.BIND_OTHER_FORM_WIDGETS &&
hadReachedStep <= GUIDED_TOUR_STEPS.BIND_OTHER_FORM_WIDGETS
) {
if (isCountryInputSelected) {
if (!countryInputBound) {
showIndicator(`[data-guided-tour-iid='defaultText']`, "top", {
top: 20,
left: 0,
});
} else {
hideIndicator();
}
}
}
}, [step, hadReachedStep, countryInputBound, isCountryInputSelected]);
// Show success message
useEffect(() => {
if (
step === GUIDED_TOUR_STEPS.BIND_OTHER_FORM_WIDGETS &&
hadReachedStep <= GUIDED_TOUR_STEPS.BIND_OTHER_FORM_WIDGETS
) {
if (meta.completedSubSteps.length === 1) {
hideIndicator();
}
if (meta.completedSubSteps.length === 2) {
dispatch(markStepComplete());
}
}
}, [step, meta.completedSubSteps.length, hadReachedStep]);
// 6
useEffect(() => {
if (
step === GUIDED_TOUR_STEPS.ADD_BUTTON_WIDGET &&
hadReachedStep <= GUIDED_TOUR_STEPS.ADD_BUTTON_WIDGET &&
!showInfoMessage
) {
if (buttonWidgetPresent) {
dispatch(updateButtonWidgetText());
dispatch(markStepComplete());
}
}
}, [step, buttonWidgetPresent, showInfoMessage]);
// 8
useEffect(() => {
if (
step === GUIDED_TOUR_STEPS.BUTTON_ONSUCCESS_BINDING &&
hadReachedStep <= GUIDED_TOUR_STEPS.BUTTON_ONSUCCESS_BINDING
) {
if (buttonWidgetSuccessBinding) {
dispatch(markStepComplete());
hideIndicator();
} else {
showIndicator(`[data-guided-tour-iid='onSuccess']`, "top", {
top: 20,
left: 0,
});
}
}
}, [step, hadReachedStep, buttonWidgetSuccessBinding]);
useEffect(() => {
if (step === GUIDED_TOUR_STEPS.DEPLOY) {
if (isDeployed) {
hideIndicator();
dispatch(enableGuidedTour(false));
dispatch(disableStartSignpostingAction());
}
}
}, [step, isDeployed]);
return meta;
}
export default useComputeCurrentStep;

View File

@ -1,6 +1,5 @@
import React from "react";
import WidgetsEditorEntityExplorer from "../../WidgetsEditorEntityExplorer";
import { useSelector } from "react-redux";
import styled from "styled-components";
import { Switch, useRouteMatch } from "react-router";
import { SentryRoute } from "@appsmith/AppRouter";
@ -15,7 +14,6 @@ import {
import AppSettingsPane from "./AppSettings";
import DataSidePane from "./DataSidePane";
import LibrarySidePane from "./LibrarySidePane";
import { inGuidedTour } from "selectors/onboardingSelectors";
import { useIsAppSidebarEnabled } from "../../../../navigation/featureFlagHooks";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
@ -33,8 +31,7 @@ const LeftPane = () => {
FEATURE_FLAG.release_show_new_sidebar_pages_pane_enabled,
);
const { path } = useRouteMatch();
const guidedTourEnabled = useSelector(inGuidedTour);
if (!isAppSidebarEnabled || guidedTourEnabled) {
if (!isAppSidebarEnabled) {
return <WidgetsEditorEntityExplorer />;
}
return (

View File

@ -7,7 +7,6 @@ import history, { NavigationMethod } from "utils/history";
import { useCurrentAppState } from "../hooks";
import { getCurrentWorkspaceId } from "@appsmith/selectors/workspaceSelectors";
import { fetchWorkspace } from "@appsmith/actions/workspaceActions";
import { inGuidedTour } from "selectors/onboardingSelectors";
import SidebarComponent from "./SidebarComponent";
import { BottomButtons, TopButtons } from "@appsmith/entities/IDE/constants";
@ -20,7 +19,6 @@ function Sidebar() {
const pageId = useSelector(getCurrentPageId);
const currentWorkspaceId = useSelector(getCurrentWorkspaceId);
const guidedTourEnabled = useSelector(inGuidedTour);
useEffect(() => {
dispatch(fetchWorkspace(currentWorkspaceId));
@ -41,10 +39,6 @@ function Sidebar() {
[pageId],
);
if (guidedTourEnabled) {
return null;
}
return (
<SidebarComponent
appState={appState}

View File

@ -9,8 +9,6 @@ import React from "react";
import PropertyControl from "./PropertyControl";
import PropertySection from "./PropertySection";
import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
import Boxed from "../GuidedTour/Boxed";
import { GUIDED_TOUR_STEPS } from "../GuidedTour/constants";
import { EmptySearchResult } from "./EmptySearchResult";
import { useSelector } from "react-redux";
import { getWidgetPropsForPropertyPane } from "selectors/propertyPaneSelectors";
@ -43,55 +41,38 @@ const generatePropertyControl = (
const sectionConfig: PropertyPaneSectionConfig =
config as PropertyPaneSectionConfig;
return (
<Boxed
<PropertySection
childrenId={sectionConfig.childrenId}
collapsible={sectionConfig.collapsible ?? true}
hidden={sectionConfig.hidden}
id={config.id || sectionConfig.sectionName}
isDefaultOpen={sectionConfig.isDefaultOpen}
key={config.id + props.id}
show={
sectionConfig.sectionName !== "General" &&
props.type === "TABLE_WIDGET"
}
step={GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING}
name={sectionConfig.sectionName}
panelPropertyPath={props.panelPropertyPath}
propertyPath={sectionConfig.propertySectionPath}
tag={sectionConfig.tag}
>
<PropertySection
childrenId={sectionConfig.childrenId}
collapsible={sectionConfig.collapsible ?? true}
hidden={sectionConfig.hidden}
id={config.id || sectionConfig.sectionName}
isDefaultOpen={sectionConfig.isDefaultOpen}
name={sectionConfig.sectionName}
panelPropertyPath={props.panelPropertyPath}
propertyPath={sectionConfig.propertySectionPath}
tag={sectionConfig.tag}
>
{config.children &&
generatePropertyControl(
config.children,
props,
isSearchResult,
enhancements,
)}
</PropertySection>
</Boxed>
{config.children &&
generatePropertyControl(
config.children,
props,
isSearchResult,
enhancements,
)}
</PropertySection>
);
} else if ((config as PropertyPaneControlConfig).controlType) {
return (
<Boxed
<PropertyControl
isPanelProperty={!!props.isPanelProperty}
key={config.id + props.id}
show={
(config as PropertyPaneControlConfig).propertyName !==
"tableData" && props.type === "TABLE_WIDGET"
}
step={GUIDED_TOUR_STEPS.TABLE_WIDGET_BINDING}
>
<PropertyControl
isPanelProperty={!!props.isPanelProperty}
key={config.id + props.id}
{...(config as PropertyPaneControlConfig)}
enhancements={enhancements}
isSearchResult={isSearchResult}
panel={props.panel}
theme={props.theme}
/>
</Boxed>
{...(config as PropertyPaneControlConfig)}
enhancements={enhancements}
isSearchResult={isSearchResult}
panel={props.panel}
theme={props.theme}
/>
);
}
throw Error("Unknown configuration provided: " + props.type);

View File

@ -19,7 +19,6 @@ import { ENTITY_TYPE } from "entities/AppsmithConsole";
import { DebugButton } from "components/editorComponents/Debugger/DebugCTA";
import { showDebugger } from "actions/debuggerActions";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { inGuidedTour } from "selectors/onboardingSelectors";
import type { InteractionAnalyticsEventDetail } from "utils/AppsmithUtils";
import {
interactionAnalyticsEvent,
@ -106,7 +105,6 @@ const useDependencyList = (name: string) => {
(state: AppState) => state.evaluations.dependencies.inverseDependencyMap,
equal,
);
const guidedTour = useSelector(inGuidedTour);
const getEntityId = useCallback(
(name) => {
@ -122,9 +120,8 @@ const useDependencyList = (name: string) => {
);
const entityDependencies = useMemo(() => {
if (guidedTour) return null;
return getDependenciesFromInverseDependencies(inverseDependencyMap, name);
}, [name, inverseDependencyMap, guidedTour]);
}, [name, inverseDependencyMap]);
const dependencyOptions = useMemo(
() =>

View File

@ -18,8 +18,6 @@ import { useToggleEditWidgetName } from "utils/hooks/dragResizeHooks";
import useInteractionAnalyticsEvent from "utils/hooks/useInteractionAnalyticsEvent";
import type { WidgetType } from "constants/WidgetConstants";
import { inGuidedTour } from "selectors/onboardingSelectors";
import { toggleShowDeviationDialog } from "actions/onboardingActions";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import { getIsCurrentWidgetRecentlyAdded } from "selectors/propertyPaneSelectors";
@ -63,7 +61,6 @@ const PropertyPaneTitle = memo(function PropertyPaneTitle(
const isCurrentWidgetRecentlyAdded = useSelector(
getIsCurrentWidgetRecentlyAdded,
);
const guidedTourEnabled = useSelector(inGuidedTour);
const { dispatchInteractionAnalyticsEvent, eventEmitterRef } =
useInteractionAnalyticsEvent<HTMLDivElement>();
@ -79,11 +76,6 @@ const PropertyPaneTitle = memo(function PropertyPaneTitle(
const { title, updatePropertyTitle } = props;
const updateNewTitle = useCallback(
(value: string) => {
if (guidedTourEnabled) {
dispatch(toggleShowDeviationDialog(true));
return;
}
if (
value &&
value.trim().length > 0 &&
@ -92,16 +84,12 @@ const PropertyPaneTitle = memo(function PropertyPaneTitle(
updatePropertyTitle && updatePropertyTitle(value.trim());
}
},
[updatePropertyTitle, title, guidedTourEnabled],
[updatePropertyTitle, title],
);
// End
const updateTitle = useCallback(
(value?: string) => {
if (guidedTourEnabled) {
dispatch(toggleShowDeviationDialog(true));
return;
}
if (
value &&
value.trim().length > 0 &&
@ -117,14 +105,7 @@ const PropertyPaneTitle = memo(function PropertyPaneTitle(
toggleEditWidgetName(props.widgetId, false);
}
},
[
dispatch,
widgets,
setName,
props.widgetId,
props.title,
guidedTourEnabled,
],
[dispatch, widgets, setName, props.widgetId, props.title],
);
useEffect(() => {

View File

@ -64,8 +64,6 @@ import type { Plugin } from "api/PluginApi";
import { UIComponentTypes } from "api/PluginApi";
import * as Sentry from "@sentry/react";
import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers";
import Guide from "pages/Editor/GuidedTour/Guide";
import { inGuidedTour } from "selectors/onboardingSelectors";
import { EDITOR_TABS, SQL_DATASOURCES } from "constants/QueryEditorConstants";
import type { FormEvalOutput } from "reducers/evaluationReducers/formEvaluationReducer";
import { isValidFormConfig } from "reducers/evaluationReducers/formEvaluationReducer";
@ -329,7 +327,6 @@ export function EditorJSONtoForm(props: Props) {
const actions: Action[] = useSelector((state: AppState) =>
state.entities.actions.map((action) => action.config),
);
const guidedTourEnabled = useSelector(inGuidedTour);
const currentActionConfig: Action | undefined = actions.find(
(action) => action.id === params.apiId || action.id === params.queryId,
);
@ -619,7 +616,8 @@ export function EditorJSONtoForm(props: Props) {
// here we check for normal conditions for opening action pane
// or if any of the flags are true, We should open the actionpane by default.
const shouldOpenActionPaneByDefault =
((hasDependencies || !!actionResponse) && !guidedTourEnabled) ||
hasDependencies ||
!!actionResponse ||
currentActionPluginName !== PluginName.SMTP;
// Datasource selection is hidden for Appsmith AI Plugin and for plugins that don't require datasource
@ -635,8 +633,7 @@ export function EditorJSONtoForm(props: Props) {
return (
<>
{!guidedTourEnabled && closeEditorLink}
{guidedTourEnabled && <Guide className="query-page" />}
{closeEditorLink}
<QueryFormContainer onSubmit={handleSubmit(noop)}>
<StyledFormRow>
<NameWrapper>

View File

@ -9,12 +9,10 @@ import { getAppMode } from "@appsmith/selectors/applicationSelectors";
import { setPreviewModeInitAction } from "actions/editorActions";
import { previewModeSelector } from "selectors/editorSelectors";
import { isExploringSelector } from "selectors/onboardingSelectors";
import { createMessage, EDITOR_HEADER } from "@appsmith/constants/messages";
function ToggleModeButton() {
const dispatch = useDispatch();
const isExploring = useSelector(isExploringSelector);
const isPreviewMode = useSelector(previewModeSelector);
const appMode = useSelector(getAppMode);
@ -25,7 +23,7 @@ function ToggleModeButton() {
dispatch(setPreviewModeInitAction(!isPreviewMode));
}, [dispatch, setPreviewModeInitAction, isPreviewMode]);
if (isExploring || isViewMode) return null;
if (isViewMode) return null;
return (
<Tooltip

View File

@ -23,9 +23,7 @@ import {
} from "@appsmith/selectors/applicationSelectors";
import { setCanvasSelectionFromEditor } from "actions/canvasSelectionActions";
import { useAllowEditorDragToSelect } from "utils/hooks/useAllowEditorDragToSelect";
import { inGuidedTour } from "selectors/onboardingSelectors";
import EditorContextProvider from "components/editorComponents/EditorContextProvider";
import Guide from "../GuidedTour/Guide";
import MainContainerWrapper from "./MainContainerWrapper";
import EmptyCanvasPrompts from "./EmptyCanvasPrompts";
import { useAutoHeightUIState } from "utils/hooks/autoHeightUIHooks";
@ -56,7 +54,6 @@ function WidgetsEditor() {
const currentPageId = useSelector(getCurrentPageId);
const currentPageName = useSelector(getCurrentPageName);
const currentApp = useSelector(getCurrentApplication);
const guidedTourEnabled = useSelector(inGuidedTour);
const isPreviewMode = useSelector(previewModeSelector);
const isProtectedMode = useSelector(protectedModeSelector);
const lastUpdatedTime = useSelector(getSnapshotUpdatedTime);
@ -164,7 +161,7 @@ function WidgetsEditor() {
);
const showNavigation = () => {
if (isPreviewingNavigation && !guidedTourEnabled) {
if (isPreviewingNavigation) {
return (
<NavigationPreview
isAppSettingsPaneWithNavigationTabOpen={
@ -179,7 +176,6 @@ function WidgetsEditor() {
PerformanceTracker.stopTracking();
return (
<EditorContextProvider renderMode="CANVAS">
{guidedTourEnabled && <Guide />}
<div className="relative flex flex-row w-full overflow-hidden">
<div
className={classNames({

View File

@ -8,13 +8,10 @@ import {
COMING_SOON,
COMMIT_CHANGES,
CONFLICTS_FOUND,
CONNECT_GIT,
CONNECT_GIT_BETA,
CONNECTING_TO_REPO_DISABLED,
CONTACT_ADMIN_FOR_GIT,
createMessage,
DISCARD_AND_PULL_SUCCESS,
DURING_ONBOARDING_TOUR,
GIT_SETTINGS,
MERGE,
NO_COMMITS_TO_PULL,
@ -42,7 +39,6 @@ import {
protectedModeSelector,
} from "selectors/gitSyncSelectors";
import SpinnerLoader from "pages/common/SpinnerLoader";
import { inGuidedTour } from "selectors/onboardingSelectors";
import { getTypographyByKey } from "design-system-old";
import { Button, Icon, Tooltip } from "design-system";
import AnalyticsUtil from "utils/AnalyticsUtil";
@ -231,16 +227,6 @@ const StyledIcon = styled(Icon)`
margin-right: ${(props) => props.theme.spaces[3]}px;
`;
const PlaceholderButton = styled.div`
padding: ${(props) =>
`${props.theme.spaces[1]}px ${props.theme.spaces[3]}px`};
border: solid 1px ${Colors.MERCURY};
${getTypographyByKey("btnSmall")};
text-transform: uppercase;
background-color: ${Colors.ALABASTER_ALT};
color: ${Colors.GRAY};
`;
const OuterContainer = styled.div`
padding: 4px 16px;
height: 100%;
@ -252,31 +238,20 @@ const CenterDiv = styled.div`
function ConnectGitPlaceholder() {
const dispatch = useDispatch();
const isInGuidedTour = useSelector(inGuidedTour);
const isConnectToGitPermitted = useHasConnectToGitPermission();
const isTooltipEnabled = isInGuidedTour || !isConnectToGitPermitted;
const isTooltipEnabled = !isConnectToGitPermitted;
const tooltipContent = useMemo(() => {
if (!isConnectToGitPermitted) {
return <CenterDiv>{createMessage(CONTACT_ADMIN_FOR_GIT)}</CenterDiv>;
}
if (isInGuidedTour) {
return (
<>
<div>{createMessage(CONNECTING_TO_REPO_DISABLED)}</div>
<div>{createMessage(DURING_ONBOARDING_TOUR)}</div>
</>
);
}
return (
<>
<div>{createMessage(NOT_LIVE_FOR_YOU_YET)}</div>
<div>{createMessage(COMING_SOON)}</div>
</>
);
}, [isInGuidedTour, isConnectToGitPermitted]);
const isGitConnectionEnabled = !isInGuidedTour;
}, [isConnectToGitPermitted]);
return (
<OuterContainer>
@ -287,32 +262,26 @@ function ConnectGitPlaceholder() {
name="git-commit"
size="lg"
/>
{isGitConnectionEnabled ? (
<Button
className="t--connect-git-bottom-bar"
isDisabled={!isConnectToGitPermitted}
kind="secondary"
onClick={() => {
AnalyticsUtil.logEvent("GS_CONNECT_GIT_CLICK", {
source: "BOTTOM_BAR_GIT_CONNECT_BUTTON",
});
<Button
className="t--connect-git-bottom-bar"
isDisabled={!isConnectToGitPermitted}
kind="secondary"
onClick={() => {
AnalyticsUtil.logEvent("GS_CONNECT_GIT_CLICK", {
source: "BOTTOM_BAR_GIT_CONNECT_BUTTON",
});
dispatch(
setIsGitSyncModalOpen({
isOpen: true,
tab: GitSyncModalTab.GIT_CONNECTION,
}),
);
}}
size="sm"
>
{createMessage(CONNECT_GIT_BETA)}
</Button>
) : (
<PlaceholderButton className="t--disabled-connect-git-bottom-bar">
{createMessage(CONNECT_GIT)}
</PlaceholderButton>
)}
dispatch(
setIsGitSyncModalOpen({
isOpen: true,
tab: GitSyncModalTab.GIT_CONNECTION,
}),
);
}}
size="sm"
>
{createMessage(CONNECT_GIT_BETA)}
</Button>
</Container>
</Tooltip>
</OuterContainer>

View File

@ -29,8 +29,6 @@ import DisconnectGitModal from "pages/Editor/gitSync/DisconnectGitModal";
import { setupPage, updateCurrentPage } from "actions/pageActions";
import { getCurrentPageId } from "selectors/editorSelectors";
import { getSearchQuery } from "utils/helpers";
import { loading } from "selectors/onboardingSelectors";
import GuidedTourModal from "./GuidedTour/DeviationModal";
import RepoLimitExceededErrorModal from "./gitSync/RepoLimitExceededErrorModal";
import ImportedApplicationSuccessModal from "./gitSync/ImportedAppSuccessModal";
import { getIsBranchUpdated } from "../utils";
@ -171,7 +169,6 @@ class Editor extends Component<Props> {
<GitSettingsModal />
<DisconnectGitModal />
<DisableAutocommitModal />
<GuidedTourModal />
<RepoLimitExceededErrorModal />
<TemplatesModal />
<ImportedApplicationSuccessModal />
@ -196,7 +193,6 @@ const mapStateToProps = (state: AppState) => ({
user: getCurrentUser(state),
currentApplicationName: state.ui.applications.currentApplication?.name,
currentPageId: getCurrentPageId(state),
loadingGuidedTour: loading(state),
});
const mapDispatchToProps = (dispatch: any) => {

View File

@ -1,146 +0,0 @@
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import { createReducer } from "utils/ReducerUtils";
const initialState: GuidedTourState = {
guidedTour: false,
loading: false,
exploring: false,
currentStep: 1,
showSuccessMessage: false,
showInfoMessage: false,
tableWidgetWasSelected: false,
hadReachedStep: 0,
showEndTourDialog: false,
showDeviatingDialog: false,
showPostCompletionMessage: false,
forceShowContent: 0,
};
export interface GuidedTourState {
guidedTour: boolean;
loading: boolean;
exploring: boolean;
currentStep: number;
showSuccessMessage: boolean;
showInfoMessage: boolean;
tableWidgetWasSelected: boolean;
hadReachedStep: number;
showEndTourDialog: boolean;
showDeviatingDialog: boolean;
showPostCompletionMessage: boolean;
forceShowContent: number;
}
const guidedTourReducer = createReducer(initialState, {
[ReduxActionTypes.ENABLE_GUIDED_TOUR]: (
state: GuidedTourState,
action: ReduxAction<boolean>,
) => {
return {
...state,
...initialState,
guidedTour: action.payload,
exploring: action.payload,
};
},
[ReduxActionTypes.GUIDED_TOUR_TOGGLE_LOADER]: (
state: GuidedTourState,
action: ReduxAction<boolean>,
) => {
return {
...state,
loading: action.payload,
exploring: !action.payload ? false : state.exploring,
};
},
[ReduxActionTypes.SET_CURRENT_STEP]: (
state: GuidedTourState,
action: ReduxAction<number>,
) => {
if (action.payload === state.currentStep) {
return state;
}
return {
...state,
currentStep: action.payload,
showSuccessMessage: false,
showInfoMessage: false,
hadReachedStep:
action.payload > state.hadReachedStep
? action.payload
: state.hadReachedStep,
};
},
[ReduxActionTypes.SHOW_INFO_MESSAGE]: (state: GuidedTourState) => {
return {
...state,
showInfoMessage: true,
};
},
[ReduxActionTypes.GUIDED_TOUR_MARK_STEP_COMPLETED]: (
state: GuidedTourState,
) => {
return {
...state,
showSuccessMessage: true,
};
},
[ReduxActionTypes.TABLE_WIDGET_WAS_SELECTED]: (
state: GuidedTourState,
action: ReduxAction<boolean>,
) => {
return {
...state,
tableWidgetWasSelected: action.payload,
};
},
[ReduxActionTypes.TOGGLE_DEVIATION_DIALOG]: (
state: GuidedTourState,
action: ReduxAction<boolean>,
) => {
return {
...state,
showDeviatingDialog: action.payload,
};
},
[ReduxActionTypes.TOGGLE_END_GUIDED_TOUR_DIALOG]: (
state: GuidedTourState,
action: ReduxAction<boolean>,
) => {
return {
...state,
showEndTourDialog: action.payload,
};
},
[ReduxActionTypes.SHOW_POST_COMPLETION_MESSAGE]: (
state: GuidedTourState,
action: ReduxAction<boolean>,
) => {
return {
...state,
showPostCompletionMessage: action.payload,
};
},
[ReduxActionTypes.FORCE_SHOW_CONTENT]: (
state: GuidedTourState,
action: ReduxAction<boolean>,
) => {
return {
...state,
forceShowContent: action.payload,
};
},
[ReduxActionTypes.LOAD_GUIDED_TOUR]: (
state: GuidedTourState,
action: ReduxAction<GuidedTourState>,
) => {
return {
...state,
...action.payload,
};
},
});
export default guidedTourReducer;

View File

@ -129,7 +129,6 @@ import { getQueryParams } from "utils/URLUtils";
import type { GenerateCRUDEnabledPluginMap, Plugin } from "api/PluginApi";
import { getIsGeneratePageInitiator } from "utils/GenerateCrudUtil";
import { shouldBeDefined, trimQueryString } from "utils/helpers";
import { inGuidedTour } from "selectors/onboardingSelectors";
import { updateReplayEntity } from "actions/pageActions";
import OAuthApi from "api/OAuthApi";
import type { AppState } from "@appsmith/reducers";
@ -317,8 +316,6 @@ export function* addMockDbToDatasources(actionPayload: addMockDb) {
const isGeneratePageInitiator =
getIsGeneratePageInitiator(isGeneratePageMode);
const isInGuidedTour: boolean = yield select(inGuidedTour);
if (isGeneratePageInitiator) {
history.push(
generateTemplateFormURL({
@ -329,7 +326,7 @@ export function* addMockDbToDatasources(actionPayload: addMockDb) {
}),
);
} else {
if (isInGuidedTour || skipRedirection) {
if (skipRedirection) {
return;
}

View File

@ -37,7 +37,6 @@ import {
selectCurrentApplicationSlug,
} from "selectors/editorSelectors";
import { getIsInitialized as getIsViewerInitialized } from "selectors/appViewSelectors";
import { enableGuidedTour } from "actions/onboardingActions";
import { setPreviewModeAction } from "actions/editorActions";
import type { AppEnginePayload } from "entities/Engine";
import { PageNotFoundError } from "entities/Engine";
@ -360,8 +359,6 @@ function* resetEditorSaga() {
yield put(resetPageList());
yield put(resetApplicationWidgets());
yield put(resetRecentEntities());
// End guided tour once user exits editor
yield put(enableGuidedTour(false));
// Reset to edit mode once user exits editor
// Without doing this if the user creates a new app they
// might end up in preview mode if they were in preview mode

View File

@ -1,8 +1,5 @@
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
import {
ReduxActionTypes,
WidgetReduxActionTypes,
} from "@appsmith/constants/ReduxActionConstants";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import {
all,
call,
@ -25,371 +22,27 @@ import {
import { getCurrentUser } from "selectors/usersSelectors";
import history from "utils/history";
import {
getHadReachedStep,
getOnboardingWorkspaces,
getQueryAction,
getSignpostingStepStateByStep,
getTableWidget,
} from "selectors/onboardingSelectors";
import type { Workspaces } from "@appsmith/constants/workspaceConstants";
import { getSignpostingStepStateByStep } from "selectors/onboardingSelectors";
import {
disableStartSignpostingAction,
enableGuidedTour,
focusWidgetProperty,
loadGuidedTour,
removeFirstTimeUserOnboardingApplicationId as removeFirstTimeUserOnboardingApplicationIdAction,
setCurrentStep,
setSignpostingOverlay,
showSignpostingTooltip,
signpostingStepUpdate,
toggleLoader,
} from "actions/onboardingActions";
import {
getCurrentApplicationId,
getIsEditorInitialized,
} from "selectors/editorSelectors";
import type { WidgetProps } from "widgets/BaseWidget";
import { getNextWidgetName } from "./WidgetOperationUtils";
import WidgetFactory from "WidgetProvider/factory";
import { generateReactKey } from "utils/generators";
import { RenderModes } from "constants/WidgetConstants";
import log from "loglevel";
import { getDataTree } from "selectors/dataTreeSelectors";
import { getWidgets } from "./selectors";
import {
clearActionResponse,
updateActionData,
} from "actions/pluginActionActions";
import {
importApplication,
updateApplicationLayout,
} from "@appsmith/actions/applicationActions";
import { setPreviewModeAction } from "actions/editorActions";
import type { FlattenedWidgetProps } from "WidgetProvider/constants";
import type { ActionData } from "@appsmith/reducers/entityReducers/actionsReducer";
import { batchUpdateMultipleWidgetProperties } from "actions/controlActions";
import {
setExplorerActiveAction,
setExplorerPinnedAction,
} from "actions/explorerActions";
import { selectWidgetInitAction } from "actions/widgetSelectionActions";
import { hideIndicator } from "pages/Editor/GuidedTour/utils";
import { updateWidgetName } from "actions/propertyPaneActions";
import AnalyticsUtil from "utils/AnalyticsUtil";
import type { DataTree } from "entities/DataTree/dataTreeTypes";
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
import type { User } from "constants/userConstants";
import { builderURL, queryEditorIdURL } from "@appsmith/RouteBuilder";
import { GuidedTourEntityNames } from "pages/Editor/GuidedTour/constants";
import type { GuidedTourState } from "reducers/uiReducers/guidedTourReducer";
import { sessionStorage } from "utils/localStorage";
import { SelectionRequestType } from "sagas/WidgetSelectUtils";
import { builderURL } from "@appsmith/RouteBuilder";
import type { SIGNPOSTING_STEP } from "pages/Editor/FirstTimeUserOnboarding/Utils";
import type { StepState } from "reducers/uiReducers/onBoardingReducer";
import { isUndefined } from "lodash";
import { isAirgapped } from "@appsmith/utils/airgapHelpers";
import { SIGNPOSTING_ANALYTICS_STEP_NAME } from "pages/Editor/FirstTimeUserOnboarding/constants";
const GUIDED_TOUR_STORAGE_KEY = "GUIDED_TOUR_STORAGE_KEY";
function* createApplication() {
// If we are starting onboarding from the editor wait for the editor to reset.
const isEditorInitialised: boolean = yield select(getIsEditorInitialized);
let userWorkspaces: Workspaces[] = yield select(getOnboardingWorkspaces);
if (isEditorInitialised) {
yield take(ReduxActionTypes.RESET_EDITOR_SUCCESS);
// If we haven't fetched the workspace list yet we wait for it to complete
// as we need an workspace where we create an application
if (!userWorkspaces.length) {
yield take(ReduxActionTypes.FETCH_USER_APPLICATIONS_WORKSPACES_SUCCESS);
}
}
userWorkspaces = yield select(getOnboardingWorkspaces);
const currentUser: User | undefined = yield select(getCurrentUser);
// @ts-expect-error: currentUser can be undefined
const currentWorkspaceId = currentUser.currentWorkspaceId;
let workspace;
if (!currentWorkspaceId) {
workspace = userWorkspaces[0];
} else {
const filteredWorkspaces = userWorkspaces.filter(
(workspace: any) => workspace.workspace.id === currentWorkspaceId,
);
workspace = filteredWorkspaces[0];
}
if (workspace) {
const TourAppPromise = import("pages/Editor/GuidedTour/app.json");
const TourApp: Awaited<typeof TourAppPromise> = yield TourAppPromise;
const appFileObject = new File([JSON.stringify(TourApp)], "app.json", {
type: "application/json",
});
yield put(enableGuidedTour(true));
yield put(
importApplication({
workspaceId: workspace.workspace.id,
applicationFile: appFileObject,
}),
);
}
yield put(setPreviewModeAction(true));
}
function* syncGuidedTourStateSaga() {
const applicationId: string = yield select(getCurrentApplicationId);
const guidedTourState: GuidedTourState = yield select(
(state) => state.ui.guidedTour,
);
yield call(
sessionStorage.setItem,
GUIDED_TOUR_STORAGE_KEY,
JSON.stringify({ applicationId, guidedTourState }),
);
}
function* loadGuidedTourInitSaga() {
const applicationId: string = yield select(getCurrentApplicationId);
const guidedTourState: undefined | string = yield call(
sessionStorage.getItem,
GUIDED_TOUR_STORAGE_KEY,
);
if (guidedTourState) {
const parsedGuidedTourState: {
applicationId: string;
guidedTourState: GuidedTourState;
} = JSON.parse(guidedTourState);
if (applicationId === parsedGuidedTourState.applicationId) {
yield put(loadGuidedTour(parsedGuidedTourState.guidedTourState));
}
}
}
function* setCurrentStepSaga(action: ReduxAction<number>) {
const hadReachedStep: number = yield select(getHadReachedStep);
// Log only once when we reach that step
if (action.payload > hadReachedStep) {
AnalyticsUtil.logEvent("GUIDED_TOUR_REACHED_STEP", {
step: action.payload,
});
}
yield call(syncGuidedTourStateSaga);
yield put(setCurrentStep(action.payload));
}
function* setUpTourAppSaga() {
yield put(setPreviewModeAction(false));
// Delete the container widget
const widgets: { [widgetId: string]: FlattenedWidgetProps } =
yield select(getWidgets);
const containerWidget = Object.values(widgets).find(
(widget) => widget.type === "CONTAINER_WIDGET",
);
yield put({
type: WidgetReduxActionTypes.WIDGET_DELETE,
payload: {
widgetId: containerWidget?.widgetId,
parentId: containerWidget?.parentId,
disallowUndo: true,
},
});
yield delay(500);
// @ts-expect-error: No type declared for getTableWidgetSelector.
const tableWidget = yield select(getTableWidget);
yield put(
batchUpdateMultipleWidgetProperties([
{
widgetId: tableWidget.widgetId,
updates: {
modify: {
tableData: "",
},
},
},
]),
);
// Update getCustomers query body
const query: ActionData | undefined = yield select(getQueryAction);
yield put(clearActionResponse(query?.config.id ?? ""));
yield put(
updateActionData([
{
entityName: query?.config.name || "",
dataPath: "data",
data: undefined,
},
]),
);
const applicationId: string = yield select(getCurrentApplicationId);
yield put(
updateApplicationLayout(applicationId || "", {
appLayout: {
type: "DESKTOP",
},
}),
);
if (!query) return;
history.push(
queryEditorIdURL({
pageId: query.config.pageId,
queryId: query.config.id,
}),
);
// Hide the explorer initialy
yield put(setExplorerPinnedAction(false));
yield put(setExplorerActiveAction(false));
yield put(toggleLoader(false));
}
function* addOnboardingWidget(action: ReduxAction<Partial<WidgetProps>>) {
const widgetConfig = action.payload;
if (!widgetConfig.type) return;
const defaultConfig = WidgetFactory.widgetConfigMap.get(widgetConfig.type);
const evalTree: DataTree = yield select(getDataTree);
const widgets: CanvasWidgetsReduxState = yield select(getWidgets);
const widgetName = getNextWidgetName(widgets, widgetConfig.type, evalTree, {
prefix: widgetConfig.widgetName,
});
try {
const newWidget = {
newWidgetId: generateReactKey(),
widgetId: "0",
parentId: "0",
renderMode: RenderModes.CANVAS,
isLoading: false,
...defaultConfig,
widgetName,
...widgetConfig,
};
yield put({
type: WidgetReduxActionTypes.WIDGET_ADD_CHILD,
payload: newWidget,
});
// Wait for widget names to be updated
// Updating widget names here as widget blueprints don't take widget names
yield take(ReduxActionTypes.SAVE_PAGE_SUCCESS);
const widgets: { [widgetId: string]: FlattenedWidgetProps } =
yield select(getWidgets);
const nameInput = Object.values(widgets).find(
(widget) => widget.widgetName === "Input1",
);
const emailInput = Object.values(widgets).find(
(widget) => widget.widgetName === "Input2",
);
const countryInput = Object.values(widgets).find(
(widget) => widget.widgetName === "Input3",
);
const imageWidget = Object.values(widgets).find(
(widget) => widget.widgetName === "Image1",
);
if (nameInput && emailInput && countryInput && imageWidget) {
yield put(
updateWidgetName(nameInput.widgetId, GuidedTourEntityNames.NAME_INPUT),
);
yield take(ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS);
yield put(
updateWidgetName(
emailInput.widgetId,
GuidedTourEntityNames.EMAIL_INPUT,
),
);
yield take(ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS);
yield put(
updateWidgetName(
countryInput.widgetId,
GuidedTourEntityNames.COUNTRY_INPUT,
),
);
yield take(ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS);
yield put(
updateWidgetName(
imageWidget.widgetId,
GuidedTourEntityNames.DISPLAY_IMAGE,
),
);
}
} catch (error) {
log.error(error);
}
}
// Update button widget text
function* updateWidgetTextSaga() {
const widgets: { [widgetId: string]: FlattenedWidgetProps } =
yield select(getWidgets);
const buttonWidget = Object.values(widgets).find(
(widget) => widget.type === "BUTTON_WIDGET",
);
if (buttonWidget) {
yield put(
batchUpdateMultipleWidgetProperties([
{
widgetId: buttonWidget.widgetId,
updates: {
modify: {
text: "Click to Update",
rightColumn: buttonWidget.leftColumn + 24,
bottomRow: buttonWidget.topRow + 5,
widgetName: GuidedTourEntityNames.BUTTON_WIDGET,
},
},
},
]),
);
}
}
function* focusWidgetPropertySaga(action: ReduxAction<string>) {
const input: HTMLElement | null = document.querySelector(
`[data-guided-tour-iid=${action.payload}] .CodeEditorTarget textarea`,
);
input?.focus();
}
function* endGuidedTourSaga(action: ReduxAction<boolean>) {
if (!action.payload) {
yield call(hideIndicator);
yield call(sessionStorage.removeItem, GUIDED_TOUR_STORAGE_KEY);
}
}
function* selectWidgetSaga(
action: ReduxAction<{ widgetName: string; propertyName?: string }>,
) {
const widgets: { [widgetId: string]: FlattenedWidgetProps } =
yield select(getWidgets);
const widget = Object.values(widgets).find((widget) => {
return widget.widgetName === action.payload.widgetName;
});
if (widget) {
yield put(
selectWidgetInitAction(SelectionRequestType.One, [widget.widgetId]),
);
// Delay to wait for the fields to render
yield delay(1000);
// If the propertyName exist then we focus the respective input field as well
if (action.payload.propertyName)
yield put(focusWidgetProperty(action.payload.propertyName));
}
}
// Signposting sagas
function* setFirstTimeUserOnboardingApplicationId(action: ReduxAction<string>) {
yield storeFirstTimeUserOnboardingApplicationId(action.payload);
@ -528,21 +181,6 @@ function* setSignpostingStepStateSaga(
export default function* onboardingActionSagas() {
yield all([
takeLatest(
ReduxActionTypes.ONBOARDING_CREATE_APPLICATION,
createApplication,
),
takeLatest(ReduxActionTypes.SET_UP_TOUR_APP, setUpTourAppSaga),
takeLatest(ReduxActionTypes.GUIDED_TOUR_ADD_WIDGET, addOnboardingWidget),
takeLatest(ReduxActionTypes.SET_CURRENT_STEP_INIT, setCurrentStepSaga),
takeLatest(
ReduxActionTypes.UPDATE_BUTTON_WIDGET_TEXT,
updateWidgetTextSaga,
),
takeLatest(ReduxActionTypes.ENABLE_GUIDED_TOUR, endGuidedTourSaga),
takeLatest(ReduxActionTypes.GUIDED_TOUR_FOCUS_WIDGET, selectWidgetSaga),
takeLatest(ReduxActionTypes.FOCUS_WIDGET_PROPERTY, focusWidgetPropertySaga),
takeLatest(ReduxActionTypes.LOAD_GUIDED_TOUR_INIT, loadGuidedTourInitSaga),
takeLatest(
ReduxActionTypes.SET_FIRST_TIME_USER_ONBOARDING_APPLICATION_ID,
setFirstTimeUserOnboardingApplicationId,

View File

@ -42,11 +42,6 @@ import {
} from "./WidgetOperationUtils";
import { showUndoRedoToast } from "utils/replayHelpers";
import WidgetFactory from "WidgetProvider/factory";
import {
inGuidedTour,
isExploringSelector,
} from "selectors/onboardingSelectors";
import { toggleShowDeviationDialog } from "actions/onboardingActions";
import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions";
import { SelectionRequestType } from "sagas/WidgetSelectUtils";
import { updateFlexLayersOnDelete } from "../layoutSystems/autolayout/utils/AutoLayoutUtils";
@ -146,13 +141,6 @@ function* deleteSagaInit(deleteAction: ReduxAction<WidgetDelete>) {
const selectedWidget: FlattenedWidgetProps | undefined =
yield select(getSelectedWidget);
const selectedWidgets: string[] = yield select(getSelectedWidgets);
const guidedTourEnabled: boolean = yield select(inGuidedTour);
const isExploring: boolean = yield select(isExploringSelector);
if (guidedTourEnabled && !isExploring) {
yield put(toggleShowDeviationDialog(true));
return;
}
if (selectedWidgets.length > 1) {
yield put({

View File

@ -1,16 +1,9 @@
import { hasCreateNewAppPermission } from "@appsmith/utils/permissionHelpers";
import type { AppState } from "@appsmith/reducers";
import { createSelector } from "reselect";
import { getUserApplicationsWorkspaces } from "@appsmith/selectors/applicationSelectors";
import { getWidgets } from "sagas/selectors";
import {
getActionResponses,
getActions,
getCurrentActions,
getCanvasWidgets,
} from "@appsmith/selectors/entitiesSelector";
import { getLastSelectedWidget } from "./ui";
import { GuidedTourEntityNames } from "pages/Editor/GuidedTour/constants";
import type { SIGNPOSTING_STEP } from "pages/Editor/FirstTimeUserOnboarding/Utils";
import { isBoolean, intersection } from "lodash";
import { getEvaluationInverseDependencyMap } from "./dataTreeSelectors";
@ -41,9 +34,6 @@ export const getIsFirstTimeUserOnboardingEnabled = createSelector(
export const getInOnboardingWidgetSelection = (state: AppState) =>
state.ui.onBoarding.inOnboardingWidgetSelection;
export const getIsOnboardingWidgetSelection = (state: AppState) =>
state.ui.onBoarding.inOnboardingWidgetSelection;
export const getSignpostingStepState = (state: AppState) =>
state.ui.onBoarding.stepState;
export const getSignpostingStepStateByStep = createSelector(
@ -98,249 +88,3 @@ export const isWidgetActionConnectionPresent = createSelector(
return isBindingAvailable;
},
);
// Guided Tour selectors
export const isExploringSelector = (state: AppState) =>
state.ui.guidedTour.exploring;
export const inGuidedTour = (state: AppState) => state.ui.guidedTour.guidedTour;
export const getCurrentStep = (state: AppState) =>
state.ui.guidedTour.currentStep;
export const wasTableWidgetSelected = (state: AppState) =>
state.ui.guidedTour.tableWidgetWasSelected;
export const showEndTourDialogSelector = (state: AppState) =>
state.ui.guidedTour.showEndTourDialog;
export const showDeviatingDialogSelector = (state: AppState) =>
state.ui.guidedTour.showDeviatingDialog;
export const showPostCompletionMessage = (state: AppState) =>
state.ui.guidedTour.showPostCompletionMessage;
export const forceShowContentSelector = (state: AppState) =>
state.ui.guidedTour.forceShowContent;
export const getTableWidget = createSelector(getWidgets, (widgets) => {
return Object.values(widgets).find(
(widget) => widget.widgetName === "CustomersTable",
);
});
export const getQueryAction = createSelector(getActions, (actions) => {
return actions.find((action) => {
return action.config.name === "getCustomers";
});
});
export const isQueryLimitUpdated = createSelector(getQueryAction, (query) => {
if (query) {
let body = query.config.actionConfiguration.body;
if (body) {
// eslint-disable-next-line no-console
const regex = /SELECT \* FROM user_data ORDER BY id LIMIT 10;/gi;
// Replacing new line characters
body = body.replace(/(?:\r\n|\r|\n)/g, "");
// Replace sql comments
body = body.replace(/(\/\*[^*]*\*\/)|(\/\/[^*]*)|(--[^.].*)/gm, "");
return regex.test(body);
}
}
return false;
});
export const isQueryExecutionSuccessful = createSelector(
getActionResponses,
getQueryAction,
(responses, query) => {
if (query?.config.id && responses[query.config.id]) {
return responses[query.config.id]?.isExecutionSuccess;
}
},
);
export const isTableWidgetSelected = createSelector(
getTableWidget,
getLastSelectedWidget,
wasTableWidgetSelected,
(tableWidget, selectedWidgetId, tableWidgetWasSelected) => {
if (!tableWidgetWasSelected) {
return tableWidget?.widgetId === selectedWidgetId;
}
return true;
},
);
export const tableWidgetHasBinding = createSelector(
getTableWidget,
(tableWidget) => {
if (tableWidget) {
if (tableWidget.tableData === `{{getCustomers.data}}`) {
return tableWidget.widgetId;
}
}
return "";
},
);
export const containerWidgetAdded = createSelector(getWidgets, (widgets) => {
return !!Object.values(widgets).find(
(widget) => widget.type === "CONTAINER_WIDGET",
);
});
export const getHadReachedStep = (state: AppState) =>
state.ui.guidedTour.hadReachedStep;
export const isNameInputBoundSelector = createSelector(
getTableWidget,
getWidgets,
(tableWidget, widgets) => {
if (tableWidget) {
const widgetValues = Object.values(widgets);
const countryInput = widgetValues.find((widget) => {
if (widget.type === "INPUT_WIDGET_V2") {
return (
widget.defaultText ===
`{{${tableWidget.widgetName}.selectedRow.name}}`
);
}
return false;
});
if (countryInput) return true;
}
return false;
},
);
// Get the id of NameInput
export const nameInputSelector = createSelector(getWidgets, (widgets) => {
const widgetValues = Object.values(widgets);
const nameInput = widgetValues.find((widget) => {
if (widget.type === "INPUT_WIDGET_V2") {
return widget.widgetName === "NameInput";
}
});
return nameInput ? nameInput.widgetId : "";
});
// Check if CountryInput is selected
export const countryInputSelector = createSelector(
getWidgets,
getLastSelectedWidget,
(widgets, selectedWidgetId) => {
const widgetValues = Object.values(widgets);
const countryInput = widgetValues.find((widget) => {
if (widget.type === "INPUT_WIDGET_V2") {
return widget.widgetName === "CountryInput";
}
});
return countryInput ? countryInput.widgetId === selectedWidgetId : false;
},
);
export const isCountryInputBound = createSelector(
getTableWidget,
getWidgets,
(tableWidget, widgets) => {
if (tableWidget) {
const widgetValues = Object.values(widgets);
const countryInput = widgetValues.find((widget) => {
if (widget.widgetName === GuidedTourEntityNames.COUNTRY_INPUT) {
return (
widget.defaultText ===
`{{${tableWidget.widgetName}.selectedRow.country}}`
);
}
return false;
});
if (countryInput) return true;
}
return false;
},
);
export const isEmailInputBound = createSelector(
getTableWidget,
getWidgets,
(tableWidget, widgets) => {
if (tableWidget) {
const widgetValues = Object.values(widgets);
const countryInput = widgetValues.find((widget) => {
if (widget.widgetName === GuidedTourEntityNames.EMAIL_INPUT) {
return (
widget.defaultText ===
`{{${tableWidget.widgetName}.selectedRow.email}}`
);
}
return false;
});
if (countryInput) return true;
}
return false;
},
);
export const isButtonWidgetPresent = createSelector(getWidgets, (widgets) => {
const widgetValues = Object.values(widgets);
const buttonWidget = widgetValues.find((widget) => {
return widget.type === "BUTTON_WIDGET";
});
return !!buttonWidget;
});
export const buttonWidgetHasOnClickBinding = createSelector(
getWidgets,
(widgets) => {
const widgetValues = Object.values(widgets);
const buttonWidget = widgetValues.find((widget) => {
return (
widget.type === "BUTTON_WIDGET" &&
widget.onClick &&
widget.onClick.includes("{{updateCustomerInfo.run(")
);
});
return !!buttonWidget;
},
);
export const buttonWidgetHasOnSuccessBinding = createSelector(
getWidgets,
(widgets) => {
const widgetValues = Object.values(widgets);
const buttonWidget = widgetValues.find((widget) => {
return (
widget.type === "BUTTON_WIDGET" &&
widget.onClick &&
widget.onClick.includes("getCustomers.run()")
);
});
return !!buttonWidget;
},
);
export const showSuccessMessage = (state: AppState) =>
state.ui.guidedTour.showSuccessMessage;
export const showInfoMessageSelector = (state: AppState) =>
state.ui.guidedTour.showInfoMessage;
export const loading = (state: AppState) => state.ui.guidedTour.loading;
// To find an workspace where the user has permission to create an
// application
export const getOnboardingWorkspaces = createSelector(
getUserApplicationsWorkspaces,
(userWorkspaces) => {
return userWorkspaces.filter((userWorkspace) =>
hasCreateNewAppPermission(userWorkspace.workspace.userPermissions ?? []),
);
},
);

View File

@ -12,7 +12,6 @@ export const STORAGE_KEYS: {
ROUTE_BEFORE_LOGIN: "RedirectPath",
COPIED_WIDGET: "CopiedWidget",
GROUP_COPIED_WIDGETS: "groupCopiedWidgets",
POST_WELCOME_TOUR: "PostWelcomeTour",
RECENT_ENTITIES: "RecentEntities",
TEMPLATES_NOTIFICATION_SEEN: "TEMPLATES_NOTIFICATION_SEEN",
ONBOARDING_FORM_IN_PROGRESS: "ONBOARDING_FORM_IN_PROGRESS",
@ -195,24 +194,6 @@ export const resetCurrentEnvironment = async () => {
}
};
export const setPostWelcomeTourState = async (flag: boolean) => {
try {
await store.setItem(STORAGE_KEYS.POST_WELCOME_TOUR, flag);
return true;
} catch (error) {
log.error("An error occurred when setting post welcome tour state", error);
return false;
}
};
export const getPostWelcomeTourState = async () => {
try {
return await store.getItem(STORAGE_KEYS.POST_WELCOME_TOUR);
} catch (error) {
log.error("An error occurred when getting post welcome tour state", error);
}
};
export const setRecentAppEntities = async (entities: any, appId: string) => {
try {
const recentEntities =