Merge branch 'feature/orchestration-using-applicationId' into 'release'
Use applicationId and pageId to render pages The builder and viewer are orchestrated using `applicationId` and `pageId` - Fixes #256 The application builder and application viewer both now work based on the `applicationId` and `pageId` in the URL. - Fixes #177 There are placeholders and loading indications, now, in the application viewer. See merge request theappsmith/internal-tools-client!151
This commit is contained in:
commit
a0a2cd46ac
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ["react", "@typescript-eslint", "prettier"],
|
||||
plugins: ["react", "@typescript-eslint", "prettier", "react-hooks"],
|
||||
extends: [
|
||||
"plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
"@types/react": "^16.8.2",
|
||||
"@types/react-dom": "^16.8.0",
|
||||
"@types/react-redux": "^7.0.1",
|
||||
"@types/react-router-dom": "^4.3.5",
|
||||
"@types/react-router-dom": "^5.1.2",
|
||||
"@types/styled-components": "^4.1.8",
|
||||
"@uppy/core": "^1.5.1",
|
||||
"@uppy/file-input": "^1.3.1",
|
||||
|
|
@ -65,8 +65,8 @@
|
|||
"react-netlify-identity": "^0.1.9",
|
||||
"react-redux": "^6.0.0",
|
||||
"react-rnd": "^10.1.1",
|
||||
"react-router": "^5.0.1",
|
||||
"react-router-dom": "^5.0.1",
|
||||
"react-router": "^5.1.2",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-scripts": "^3.1.1",
|
||||
"react-select": "^3.0.8",
|
||||
"redux": "^4.0.1",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { ReduxActionTypes } from "../constants/ReduxActionConstants";
|
||||
|
||||
export type EditorConfigIdsType = {
|
||||
propertyPaneConfigsId?: string;
|
||||
widgetCardsPaneId?: string;
|
||||
widgetConfigsId?: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
import {
|
||||
ReduxActionTypes,
|
||||
ReduxActionWithoutPayload,
|
||||
} from "../constants/ReduxActionConstants";
|
||||
ReduxAction,
|
||||
InitializeEditorPayload,
|
||||
} from "constants/ReduxActionConstants";
|
||||
|
||||
export const initEditor = (): ReduxActionWithoutPayload => ({
|
||||
export const initEditor = (
|
||||
applicationId: string,
|
||||
): ReduxAction<InitializeEditorPayload> => ({
|
||||
type: ReduxActionTypes.INITIALIZE_EDITOR,
|
||||
payload: {
|
||||
applicationId,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,21 +7,34 @@ import {
|
|||
UpdateCanvasPayload,
|
||||
SavePagePayload,
|
||||
SavePageSuccessPayload,
|
||||
FetchPageListPayload,
|
||||
} from "../constants/ReduxActionConstants";
|
||||
import { ContainerWidgetProps } from "../widgets/ContainerWidget";
|
||||
|
||||
export const fetchPageList = () => ({
|
||||
type: ReduxActionTypes.FETCH_PAGE_LIST_INIT,
|
||||
});
|
||||
export const fetchPageList = (
|
||||
applicationId: string,
|
||||
): ReduxAction<FetchPageListPayload> => {
|
||||
return {
|
||||
type: ReduxActionTypes.FETCH_PAGE_LIST_INIT,
|
||||
payload: {
|
||||
applicationId,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchPage = (pageId: string): ReduxAction<FetchPageRequest> => {
|
||||
return {
|
||||
type: ReduxActionTypes.FETCH_PAGE,
|
||||
type: ReduxActionTypes.FETCH_PAGE_INIT,
|
||||
payload: {
|
||||
pageId: pageId,
|
||||
},
|
||||
};
|
||||
};
|
||||
export const fetchPageSuccess = () => {
|
||||
return {
|
||||
type: ReduxActionTypes.FETCH_PAGE_SUCCESS,
|
||||
};
|
||||
};
|
||||
|
||||
export const addWidget = (
|
||||
pageId: string,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ export interface FetchPageRequest {
|
|||
|
||||
export interface FetchPublishedPageRequest {
|
||||
pageId: string;
|
||||
layoutId: string;
|
||||
}
|
||||
|
||||
export interface SavePageRequest {
|
||||
|
|
@ -70,8 +69,8 @@ class PageApi extends Api {
|
|||
return `v1/layouts/${layoutId}/pages/${pageId}`;
|
||||
};
|
||||
|
||||
static getPublishedPageURL = (pageId: string, layoutId: string) => {
|
||||
return `v1/layouts/${layoutId}/pages/${pageId}/view`;
|
||||
static getPublishedPageURL = (pageId: string) => {
|
||||
return `v1/pages/${pageId}/view`;
|
||||
};
|
||||
|
||||
static fetchPage(
|
||||
|
|
@ -97,9 +96,7 @@ class PageApi extends Api {
|
|||
static fetchPublishedPage(
|
||||
pageRequest: FetchPublishedPageRequest,
|
||||
): AxiosPromise<FetchPublishedPageResponse> {
|
||||
return Api.get(
|
||||
PageApi.getPublishedPageURL(pageRequest.pageId, pageRequest.layoutId),
|
||||
);
|
||||
return Api.get(PageApi.getPublishedPageURL(pageRequest.pageId));
|
||||
}
|
||||
|
||||
static createPage(
|
||||
|
|
@ -108,8 +105,10 @@ class PageApi extends Api {
|
|||
return Api.post(PageApi.url, createPageRequest);
|
||||
}
|
||||
|
||||
static fetchPageList(): AxiosPromise<FetchPageListResponse> {
|
||||
return Api.get(PageApi.url);
|
||||
static fetchPageList(
|
||||
applicationId: string,
|
||||
): AxiosPromise<FetchPageListResponse> {
|
||||
return Api.get(PageApi.url + "/application/" + applicationId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
<svg width="10" height="12" viewBox="0 0 10 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 6.001C10 6.3 9.695 6.515 9.695 6.515L1.134 11.818C0.51 12.227 0 11.924 0 11.149V0.852C0 0.0749997 0.51 -0.226 1.135 0.182L9.696 5.487C9.695 5.487 10 5.702 10 6.001Z" fill="#A3B3BF"/>
|
||||
<path d="M10 6.00102C10 6.30001 9.695 6.51502 9.695 6.51502L1.134 11.818C0.51 12.227 0 11.924 0 11.149V0.852015C0 0.0750149 0.51 -0.225985 1.135 0.182015L9.696 5.48702C9.695 5.48702 10 5.70202 10 6.00102Z" fill="#A3B3BF"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 326 B |
|
|
@ -0,0 +1,9 @@
|
|||
import styled from "styled-components";
|
||||
|
||||
export default styled.div`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
|
|
@ -38,7 +38,7 @@ ContainerComponent.defaultProps = {
|
|||
backgroundColor: "white",
|
||||
};
|
||||
|
||||
type ContainerStyle = "border" | "card" | "rounded-border" | "none";
|
||||
export type ContainerStyle = "border" | "card" | "rounded-border" | "none";
|
||||
|
||||
export interface ContainerComponentProps extends ComponentProps {
|
||||
containerStyle?: ContainerStyle;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ type ContextDropdownProps = {
|
|||
toggle: {
|
||||
type: "icon" | "button";
|
||||
icon?: ControlIconName;
|
||||
iconSize?: number;
|
||||
text?: string;
|
||||
placeholder?: string;
|
||||
};
|
||||
|
|
@ -37,7 +38,10 @@ type ContextDropdownProps = {
|
|||
export const ContextDropdown = (props: ContextDropdownProps) => {
|
||||
let trigger: ReactNode;
|
||||
if (props.toggle.type === "icon" && props.toggle.icon)
|
||||
trigger = ControlIcons[props.toggle.icon]();
|
||||
trigger = ControlIcons[props.toggle.icon]({
|
||||
width: props.toggle.iconSize,
|
||||
height: props.toggle.iconSize,
|
||||
});
|
||||
if (props.toggle.type === "button" && props.toggle.text)
|
||||
trigger = <Button text={props.toggle.text} />;
|
||||
|
||||
|
|
|
|||
37
app/client/src/components/editorComponents/PageLoader.tsx
Normal file
37
app/client/src/components/editorComponents/PageLoader.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import React from "react";
|
||||
import { Text, ProgressBar } from "@blueprintjs/core";
|
||||
import styled from "styled-components";
|
||||
|
||||
const PageLoaderWrapper = styled.div`
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: white;
|
||||
& > div {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
`;
|
||||
|
||||
type PageLoaderProps = {
|
||||
value?: number;
|
||||
};
|
||||
|
||||
export const PageLoader = (props: PageLoaderProps) => {
|
||||
return (
|
||||
<PageLoaderWrapper>
|
||||
<div>
|
||||
<ProgressBar value={props.value ? props.value / 100 : undefined} />
|
||||
<Text>Loading</Text>
|
||||
</div>
|
||||
</PageLoaderWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageLoader;
|
||||
|
|
@ -5,31 +5,27 @@ import {
|
|||
API_EDITOR_URL,
|
||||
BUILDER_URL,
|
||||
API_EDITOR_ID_URL,
|
||||
} from "../../constants/routes";
|
||||
import WidgetSidebar from "../../pages/Editor/WidgetSidebar";
|
||||
import ApiSidebar from "../../pages/Editor/ApiSidebar";
|
||||
} from "constants/routes";
|
||||
import WidgetSidebar from "pages/Editor/WidgetSidebar";
|
||||
import ApiSidebar from "pages/Editor/ApiSidebar";
|
||||
|
||||
const SidebarWrapper = styled.div`
|
||||
background-color: ${props => props.theme.colors.paneBG};
|
||||
padding: 5px 10px;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
`;
|
||||
|
||||
class Sidebar extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SidebarWrapper>
|
||||
<Switch>
|
||||
<Route exact path={BUILDER_URL} component={WidgetSidebar} />
|
||||
<Route exact path={API_EDITOR_URL} component={ApiSidebar} />
|
||||
<Route exact path={API_EDITOR_ID_URL()} component={ApiSidebar} />
|
||||
</Switch>
|
||||
</SidebarWrapper>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
export const Sidebar = () => {
|
||||
return (
|
||||
<SidebarWrapper>
|
||||
<Switch>
|
||||
<Route exact path={BUILDER_URL} component={WidgetSidebar} />
|
||||
<Route exact path={API_EDITOR_URL()} component={ApiSidebar} />
|
||||
<Route exact path={API_EDITOR_ID_URL()} component={ApiSidebar} />
|
||||
</Switch>
|
||||
</SidebarWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,4 @@ const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({
|
|||
),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(DatasourcesField);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(DatasourcesField);
|
||||
|
|
|
|||
|
|
@ -29,21 +29,3 @@ export interface APIHeaders {
|
|||
export interface APIRequest {
|
||||
requestId?: string;
|
||||
}
|
||||
|
||||
export const getEditorConfigs = () => {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
return {
|
||||
currentPageId: "5dcab609312463000408b92b",
|
||||
currentLayoutId: "5dcab609312463000408b92a",
|
||||
currentPageName: "page1",
|
||||
propertyPaneConfigsId: "5d8a04195cf8050004db6e30",
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
currentPageId: "5dcab609312463000408b92b",
|
||||
currentLayoutId: "5dcab609312463000408b92a",
|
||||
currentPageName: "page1",
|
||||
propertyPaneConfigsId: "5d8a04195cf8050004db6e30",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,4 +33,4 @@ export const Colors: Record<string, string> = {
|
|||
CADET_BLUE: "#A3B3BF",
|
||||
};
|
||||
|
||||
export type Color = (typeof Colors)[keyof typeof Colors];
|
||||
export type Color = typeof Colors[keyof typeof Colors];
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Colors, Color } from "./Colors";
|
|||
import * as FontFamilies from "./Fonts";
|
||||
import _ from "lodash";
|
||||
|
||||
export type FontFamily = (typeof FontFamilies)[keyof typeof FontFamilies];
|
||||
export type FontFamily = typeof FontFamilies[keyof typeof FontFamilies];
|
||||
|
||||
const {
|
||||
default: styled,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
|||
UPDATE_CANVAS: "UPDATE_CANVAS",
|
||||
FETCH_CANVAS: "FETCH_CANVAS",
|
||||
CLEAR_CANVAS: "CLEAR_CANVAS",
|
||||
FETCH_PAGE: "FETCH_PAGE",
|
||||
FETCH_PAGE_INIT: "FETCH_PAGE_INIT",
|
||||
FETCH_PAGE_SUCCESS: "FETCH_PAGE_SUCCESS",
|
||||
DROP_WIDGET_CANVAS: "DROP_WIDGET_CANVAS",
|
||||
REMOVE_WIDGET_CANVAS: "REMOVE_WIDGET_CANVAS",
|
||||
LOAD_WIDGET_PANE: "LOAD_WIDGET_PANE",
|
||||
|
|
@ -83,10 +84,10 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
|||
HIDE_PROPERTY_PANE: "HIDE_PROPERTY_PANE",
|
||||
};
|
||||
|
||||
export type ReduxActionType = (typeof ReduxActionTypes)[keyof typeof ReduxActionTypes];
|
||||
export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes];
|
||||
|
||||
export const ReduxActionErrorTypes: { [key: string]: string } = {
|
||||
INIT_EDITOR_ERROR: "INIT_EDITOR_ERROR",
|
||||
INITIALIZE_EDITOR_ERROR: "INITIALIZE_EDITOR_ERROR",
|
||||
API_ERROR: "API_ERROR",
|
||||
WIDGET_DELETE_ERROR: "WIDGET_DELETE_ERROR",
|
||||
WIDGET_MOVE_ERROR: "WIDGET_MOVE_ERROR",
|
||||
|
|
@ -117,7 +118,7 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
|
|||
CREATE_APPLICATION_ERROR: "CREATE_APPLICATION_ERROR",
|
||||
};
|
||||
|
||||
export type ReduxActionErrorType = (typeof ReduxActionErrorTypes)[keyof typeof ReduxActionErrorTypes];
|
||||
export type ReduxActionErrorType = typeof ReduxActionErrorTypes[keyof typeof ReduxActionErrorTypes];
|
||||
|
||||
export interface ReduxAction<T> {
|
||||
type: ReduxActionType | ReduxActionErrorType;
|
||||
|
|
@ -149,7 +150,6 @@ export interface ShowPropertyPanePayload {
|
|||
export type PageListPayload = Array<{
|
||||
pageName: string;
|
||||
pageId: string;
|
||||
layoutId: string;
|
||||
}>;
|
||||
|
||||
export type ApplicationPayload = {
|
||||
|
|
@ -175,3 +175,9 @@ export interface LoadWidgetSidebarPayload {
|
|||
export type SavePagePayload = {};
|
||||
export type SavePageErrorPayload = {};
|
||||
export type SavePageSuccessPayload = {};
|
||||
|
||||
export type InitializeEditorPayload = {
|
||||
applicationId: string;
|
||||
};
|
||||
|
||||
export type FetchPageListPayload = InitializeEditorPayload;
|
||||
|
|
|
|||
|
|
@ -14,5 +14,5 @@ export type ValidationResponse = {
|
|||
parsed: any;
|
||||
};
|
||||
|
||||
export type ValidationType = (typeof VALIDATION_TYPES)[keyof typeof VALIDATION_TYPES];
|
||||
export type ValidationType = typeof VALIDATION_TYPES[keyof typeof VALIDATION_TYPES];
|
||||
export type Validator = (value: any) => ValidationResponse;
|
||||
|
|
|
|||
|
|
@ -1,26 +1,62 @@
|
|||
import { MenuIcons } from "../icons/MenuIcons";
|
||||
|
||||
export const BASE_URL = "/";
|
||||
export const LOGIN_URL = "/login";
|
||||
export const BUILDER_URL = "/builder";
|
||||
export const API_EDITOR_URL = `${BUILDER_URL}/api`;
|
||||
export const API_EDITOR_ID_URL = (id = ":id") => `${API_EDITOR_URL}/${id}`;
|
||||
export const APP_VIEW_URL = `/view/pages/:pageId`;
|
||||
export const APPLICATIONS_URL = `/applications`;
|
||||
export const BUILDER_URL = "/applications/:applicationId/pages/:pageId/edit";
|
||||
|
||||
// TODO(abhinav): We probably need a utils/routes file for such functions.
|
||||
export const getApplicationBuilderURL = (applicationId: string) =>
|
||||
`${BUILDER_URL}/${applicationId}`;
|
||||
export type BuilderRouteParams = {
|
||||
applicationId: string;
|
||||
pageId: string;
|
||||
};
|
||||
|
||||
export type AppViewerRouteParams = {
|
||||
applicationId?: string;
|
||||
pageId?: string;
|
||||
};
|
||||
|
||||
export type APIEditorRouteParams = {
|
||||
applicationId: string;
|
||||
pageId: string;
|
||||
apiId?: string;
|
||||
};
|
||||
|
||||
export const BUILDER_BASE_URL = (applicationId = ":applicationId"): string =>
|
||||
`/applications/${applicationId}`;
|
||||
|
||||
export const BUILDER_PAGE_URL = (
|
||||
applicationId?: string,
|
||||
pageId?: string,
|
||||
): string => {
|
||||
if (!pageId) return APPLICATIONS_URL;
|
||||
return `${BUILDER_BASE_URL(applicationId)}/pages/${pageId}/edit`;
|
||||
};
|
||||
|
||||
export const API_EDITOR_URL = (
|
||||
applicationId = ":applicationId",
|
||||
pageId = ":pageId",
|
||||
): string => `${BUILDER_PAGE_URL(applicationId, pageId)}/api`;
|
||||
|
||||
export const API_EDITOR_ID_URL = (
|
||||
applicationId = ":applicationId",
|
||||
pageId = ":pageId",
|
||||
apiId = ":apiId",
|
||||
): string => `${API_EDITOR_URL(applicationId, pageId)}/${apiId}`;
|
||||
|
||||
export const APP_VIEW_URL = `/applications/:applicationId`;
|
||||
|
||||
export const getApplicationViewerURL = (
|
||||
applicationId: string,
|
||||
pageId?: string,
|
||||
) => `/view/application/${applicationId}/pages/${pageId}`;
|
||||
applicationId = ":applicationId",
|
||||
): string => `/applications/${applicationId}`;
|
||||
|
||||
export const getApplicationViewerPageURL = (
|
||||
applicationId = ":applicationId",
|
||||
pageId = ":pageId",
|
||||
): string => `/applications/${applicationId}/pages/${pageId}`;
|
||||
|
||||
export const EDITOR_ROUTES = [
|
||||
{
|
||||
icon: MenuIcons.WIDGETS_ICON,
|
||||
path: BUILDER_URL,
|
||||
path: BUILDER_PAGE_URL,
|
||||
title: "Widgets",
|
||||
exact: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -49,7 +49,11 @@ ReactDOM.render(
|
|||
<Route exact path={BASE_URL} component={App} />
|
||||
<ProtectedRoute path={BUILDER_URL} component={Editor} />
|
||||
<ProtectedRoute path={APP_VIEW_URL} component={AppViewer} />
|
||||
<ProtectedRoute path={APPLICATIONS_URL} component={Applications} />
|
||||
<ProtectedRoute
|
||||
exact
|
||||
path={APPLICATIONS_URL}
|
||||
component={Applications}
|
||||
/>
|
||||
<Route exact path={LOGIN_URL} component={LoginPage} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
|
|
|
|||
95
app/client/src/pages/AppViewer/AppViewerPageContainer.tsx
Normal file
95
app/client/src/pages/AppViewer/AppViewerPageContainer.tsx
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import React, { Component } from "react";
|
||||
import { RouteComponentProps, Link } from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
import {
|
||||
getIsFetchingPage,
|
||||
getCurrentPageLayoutDSL,
|
||||
} from "selectors/appViewSelectors";
|
||||
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { AppViewerRouteParams } from "constants/routes";
|
||||
import { AppState } from "reducers";
|
||||
import { theme } from "constants/DefaultTheme";
|
||||
import { NonIdealState, Icon, Spinner } from "@blueprintjs/core";
|
||||
import Centered from "components/designSystems/appsmith/CenteredWrapper";
|
||||
import AppPage from "./AppPage";
|
||||
|
||||
type AppViewerPageContainerProps = {
|
||||
isFetchingPage: boolean;
|
||||
dsl?: ContainerWidgetProps<WidgetProps>;
|
||||
fetchPage: (pageId: string) => void;
|
||||
} & RouteComponentProps<AppViewerRouteParams>;
|
||||
|
||||
class AppViewerPageContainer extends Component<AppViewerPageContainerProps> {
|
||||
componentDidMount() {
|
||||
const { pageId } = this.props.match.params;
|
||||
if (pageId) {
|
||||
this.props.fetchPage(pageId);
|
||||
}
|
||||
}
|
||||
componentDidUpdate(previously: AppViewerPageContainerProps) {
|
||||
const { pageId } = this.props.match.params;
|
||||
if (
|
||||
pageId &&
|
||||
previously.location.pathname !== this.props.location.pathname
|
||||
) {
|
||||
this.props.fetchPage(pageId);
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const pageNotFound = (
|
||||
<Centered>
|
||||
<NonIdealState
|
||||
icon={
|
||||
<Icon
|
||||
iconSize={theme.fontSizes[9]}
|
||||
icon="page-layout"
|
||||
color={theme.colors.primary}
|
||||
/>
|
||||
}
|
||||
title="This page seems to be blank"
|
||||
description={
|
||||
<p>
|
||||
Please add widgets to this page in the
|
||||
<Link to="/builder"> Appsmith Editor</Link>
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
</Centered>
|
||||
);
|
||||
const pageLoading = (
|
||||
<Centered>
|
||||
<Spinner />
|
||||
</Centered>
|
||||
);
|
||||
|
||||
if (this.props.isFetchingPage) {
|
||||
return pageLoading;
|
||||
} else if (!this.props.isFetchingPage && !this.props.dsl) {
|
||||
return pageNotFound;
|
||||
} else if (!this.props.isFetchingPage && this.props.dsl) {
|
||||
return <AppPage dsl={this.props.dsl} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
isFetchingPage: getIsFetchingPage(state),
|
||||
dsl: getCurrentPageLayoutDSL(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => ({
|
||||
fetchPage: (pageId: string) =>
|
||||
dispatch({
|
||||
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
|
||||
payload: {
|
||||
pageId,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(AppViewerPageContainer);
|
||||
|
|
@ -1,32 +1,31 @@
|
|||
import React, { Component } from "react";
|
||||
import styled from "styled-components";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router";
|
||||
import { withRouter, RouteComponentProps } from "react-router";
|
||||
import { Switch, Route } from "react-router-dom";
|
||||
import { AppState } from "../../reducers";
|
||||
import {
|
||||
AppViewerRouteParams,
|
||||
getApplicationViewerPageURL,
|
||||
} from "constants/routes";
|
||||
import {
|
||||
ReduxActionTypes,
|
||||
PageListPayload,
|
||||
} from "../../constants/ReduxActionConstants";
|
||||
import {
|
||||
getCurrentRoutePageId,
|
||||
getCurrentPageLayoutDSL,
|
||||
getPageList,
|
||||
getIsFetchingPage,
|
||||
getCurrentDSLPageId,
|
||||
} from "../../selectors/appViewSelectors";
|
||||
import { ContainerWidgetProps } from "../../widgets/ContainerWidget";
|
||||
import { WidgetProps } from "../../widgets/BaseWidget";
|
||||
import { executeAction } from "../../actions/widgetActions";
|
||||
import { ActionPayload } from "../../constants/ActionConstants";
|
||||
import AppPage from "./AppPage";
|
||||
import { Spinner, NonIdealState, Icon } from "@blueprintjs/core";
|
||||
import { Link } from "react-router-dom";
|
||||
import { theme } from "../../constants/DefaultTheme";
|
||||
import SideNav, { SideNavItem } from "./viewer/SideNav";
|
||||
import AppViewerHeader from "./viewer/AppViewerHeader";
|
||||
import { updateWidgetProperty } from "../../actions/controlActions";
|
||||
import { RenderModes } from "../../constants/WidgetConstants";
|
||||
import { EditorContext } from "components/editorComponents/EditorContextProvider";
|
||||
import AppViewerPageContainer from "./AppViewerPageContainer";
|
||||
import AppViewerSideNavWrapper from "./viewer/AppViewerSideNavWrapper";
|
||||
|
||||
const AppViewWrapper = styled.div`
|
||||
margin-top: ${props => props.theme.headerHeight};
|
||||
|
|
@ -39,57 +38,13 @@ const AppViewerBody = styled.section`
|
|||
justify-content: flex-start;
|
||||
height: calc(100vh - ${props => props.theme.headerHeight});
|
||||
`;
|
||||
const Centered = styled.div`
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
const SideNavWrapper = styled.div`
|
||||
background: ${props => props.theme.colors.paneBG};
|
||||
& button.sidenav-toggle,
|
||||
& button.sidenav-toggle:hover,
|
||||
& button.sidenav-toggle:active {
|
||||
background: ${props => props.theme.colors.paneBG};
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
& ul {
|
||||
background: ${props => props.theme.colors.paneBG};
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
& li {
|
||||
padding: 0;
|
||||
}
|
||||
& li div.bp3-menu-item {
|
||||
width: 100%;
|
||||
font-size: ${props => props.theme.fontSizes[3]}px;
|
||||
&.bp3-intent-primary {
|
||||
background: ${props => props.theme.sideNav.activeItemBGColor};
|
||||
}
|
||||
& div {
|
||||
line-height: ${props => props.theme.lineHeights[6]}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export type AppViewerProps = {
|
||||
currentRoutePageId?: string;
|
||||
currentDSLPageId?: string;
|
||||
currentLayoutId?: string;
|
||||
fetchPageWidgets: Function;
|
||||
pages?: PageListPayload;
|
||||
dsl?: ContainerWidgetProps<WidgetProps>;
|
||||
initializeAppViewer: Function;
|
||||
isFetching: boolean;
|
||||
match: any;
|
||||
location: any;
|
||||
history: any;
|
||||
executeAction: (actionPayloads?: ActionPayload[]) => void;
|
||||
updateWidgetProperty: (
|
||||
widgetId: string,
|
||||
|
|
@ -98,25 +53,15 @@ export type AppViewerProps = {
|
|||
) => void;
|
||||
};
|
||||
|
||||
class AppViewer extends Component<AppViewerProps> {
|
||||
handlePageSelect = (item: SideNavItem) => {
|
||||
this.props.fetchPageWidgets(item.id);
|
||||
this.props.history.push(`/view/pages/${item.id}`);
|
||||
};
|
||||
|
||||
class AppViewer extends Component<
|
||||
AppViewerProps & RouteComponentProps<AppViewerRouteParams>
|
||||
> {
|
||||
componentDidMount() {
|
||||
this.props.initializeAppViewer(this.props.currentRoutePageId);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: AppViewerProps) {
|
||||
if (
|
||||
prevProps.currentRoutePageId !== this.props.currentRoutePageId &&
|
||||
this.props.currentDSLPageId !== this.props.currentRoutePageId
|
||||
) {
|
||||
this.props.fetchPageWidgets(this.props.currentRoutePageId);
|
||||
const { applicationId } = this.props.match.params;
|
||||
if (this.props.match.params.applicationId) {
|
||||
this.props.initializeAppViewer(applicationId);
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const items: SideNavItem[] | undefined =
|
||||
this.props.pages &&
|
||||
|
|
@ -124,13 +69,12 @@ class AppViewer extends Component<AppViewerProps> {
|
|||
text: page.pageName,
|
||||
id: page.pageId,
|
||||
icon: "page-layout", //TODO: get the icon from page.
|
||||
path: getApplicationViewerPageURL(
|
||||
this.props.match.params.applicationId,
|
||||
page.pageId,
|
||||
),
|
||||
}));
|
||||
|
||||
const currentPage =
|
||||
this.props.pages &&
|
||||
this.props.pages.find(
|
||||
page => page.pageId === this.props.currentRoutePageId,
|
||||
);
|
||||
return (
|
||||
<EditorContext.Provider
|
||||
value={{
|
||||
|
|
@ -141,47 +85,20 @@ class AppViewer extends Component<AppViewerProps> {
|
|||
<AppViewWrapper>
|
||||
<AppViewerHeader />
|
||||
<AppViewerBody>
|
||||
{items && (
|
||||
<SideNavWrapper>
|
||||
<SideNav
|
||||
items={items}
|
||||
onSelect={this.handlePageSelect}
|
||||
iconSize={24}
|
||||
active={
|
||||
currentPage && {
|
||||
text: currentPage.pageName,
|
||||
id: currentPage.pageId,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</SideNavWrapper>
|
||||
)}
|
||||
{this.props.isFetching && (
|
||||
<Centered>
|
||||
<Spinner />
|
||||
</Centered>
|
||||
)}
|
||||
{!this.props.isFetching && !this.props.dsl && items && (
|
||||
<Centered>
|
||||
<NonIdealState
|
||||
icon={
|
||||
<Icon
|
||||
iconSize={theme.fontSizes[9]}
|
||||
icon="page-layout"
|
||||
color={theme.colors.primary}
|
||||
/>
|
||||
}
|
||||
title="This page seems to be blank"
|
||||
description={
|
||||
<p>
|
||||
Please add widgets to this page in the
|
||||
<Link to="/builder"> Appsmith Editor</Link>
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
</Centered>
|
||||
)}
|
||||
{this.props.dsl && <AppPage dsl={this.props.dsl} />}
|
||||
<AppViewerSideNavWrapper>
|
||||
<SideNav
|
||||
items={items}
|
||||
iconSize={24}
|
||||
active={this.props.currentDSLPageId}
|
||||
/>
|
||||
</AppViewerSideNavWrapper>
|
||||
<Switch>
|
||||
<Route
|
||||
path={getApplicationViewerPageURL()}
|
||||
exact
|
||||
component={AppViewerPageContainer}
|
||||
/>
|
||||
</Switch>
|
||||
</AppViewerBody>
|
||||
</AppViewWrapper>
|
||||
</EditorContext.Provider>
|
||||
|
|
@ -189,10 +106,8 @@ class AppViewer extends Component<AppViewerProps> {
|
|||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState, props: AppViewerProps) => ({
|
||||
currentRoutePageId: getCurrentRoutePageId(state, props),
|
||||
const mapStateToProps = (state: AppState) => ({
|
||||
currentDSLPageId: getCurrentDSLPageId(state),
|
||||
dsl: getCurrentPageLayoutDSL(state),
|
||||
pages: getPageList(state),
|
||||
isFetching: getIsFetchingPage(state),
|
||||
});
|
||||
|
|
@ -213,24 +128,13 @@ const mapDispatchToProps = (dispatch: any) => ({
|
|||
RenderModes.PAGE,
|
||||
),
|
||||
),
|
||||
fetchPageWidgets: (pageId: string) => {
|
||||
dispatch({
|
||||
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
|
||||
payload: {
|
||||
pageId,
|
||||
},
|
||||
});
|
||||
},
|
||||
initializeAppViewer: (pageId: string) =>
|
||||
initializeAppViewer: (applicationId: string) =>
|
||||
dispatch({
|
||||
type: ReduxActionTypes.INITIALIZE_PAGE_VIEWER,
|
||||
payload: { pageId },
|
||||
payload: { applicationId },
|
||||
}),
|
||||
});
|
||||
|
||||
export default withRouter(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(AppViewer),
|
||||
connect(mapStateToProps, mapDispatchToProps)(AppViewer),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
import styled from "styled-components";
|
||||
export default styled.div`
|
||||
background: ${props => props.theme.colors.paneBG};
|
||||
& button.sidenav-toggle,
|
||||
& button.sidenav-toggle:hover,
|
||||
& button.sidenav-toggle:active {
|
||||
background: ${props => props.theme.colors.paneBG};
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
& ul {
|
||||
background: ${props => props.theme.colors.paneBG};
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
& li {
|
||||
padding: 0;
|
||||
}
|
||||
& li div.bp3-menu-item {
|
||||
width: 100%;
|
||||
font-size: ${props => props.theme.fontSizes[3]}px;
|
||||
&.bp3-intent-primary {
|
||||
background: ${props => props.theme.sideNav.activeItemBGColor};
|
||||
}
|
||||
& div {
|
||||
line-height: ${props => props.theme.lineHeights[6]}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
@ -1,17 +1,25 @@
|
|||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Menu, MenuItem, IconName, Button, Icon } from "@blueprintjs/core";
|
||||
import {
|
||||
Menu,
|
||||
MenuItem,
|
||||
IconName,
|
||||
Button,
|
||||
Icon,
|
||||
Classes,
|
||||
} from "@blueprintjs/core";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export type SideNavItem = {
|
||||
id: string;
|
||||
icon?: string;
|
||||
text: string;
|
||||
path: string;
|
||||
};
|
||||
|
||||
type SideNavProps = {
|
||||
items: SideNavItem[];
|
||||
active?: SideNavItem;
|
||||
onSelect: Function;
|
||||
items?: SideNavItem[];
|
||||
active?: string;
|
||||
headeroffset?: number;
|
||||
iconSize?: number;
|
||||
};
|
||||
|
|
@ -44,6 +52,7 @@ const SideNavWrapper = styled.div<{
|
|||
flex-grow: 0;
|
||||
display: inline;
|
||||
width: ${props => (props.open ? 100 : 0)}px;
|
||||
color: ${props => props.theme.sideNav.fontColor};
|
||||
}
|
||||
& > span {
|
||||
margin-right: ${props => (props.open ? props.theme.spaces[3] : 0)}px;
|
||||
|
|
@ -70,23 +79,29 @@ const ToggleButton = styled(Button)<{
|
|||
|
||||
export const SideNav = (props: SideNavProps) => {
|
||||
const [open, setopen] = useState(true);
|
||||
const select = (item: SideNavItem) => () => {
|
||||
props.onSelect(item);
|
||||
};
|
||||
const renderItems = (items: SideNavItem[]) => {
|
||||
const renderItems = (sideNavItems?: SideNavItem[]) => {
|
||||
let items = sideNavItems;
|
||||
if (!items) {
|
||||
items = [
|
||||
{ id: "0", text: "", path: "" },
|
||||
{ id: "1", text: "", path: "" },
|
||||
{ id: "2", text: "", path: "" },
|
||||
];
|
||||
}
|
||||
return items.map(item => {
|
||||
const icon = (
|
||||
<Icon iconSize={props.iconSize} icon={item.icon as IconName} />
|
||||
);
|
||||
return (
|
||||
<MenuItem
|
||||
icon={icon}
|
||||
active={props.active && item.id === props.active.id}
|
||||
key={item.id}
|
||||
onClick={select(item)}
|
||||
text={open ? item.text : undefined}
|
||||
tagName="div"
|
||||
/>
|
||||
<Link to={item.path} key={item.id}>
|
||||
<MenuItem
|
||||
className={!sideNavItems ? Classes.SKELETON : undefined}
|
||||
icon={icon}
|
||||
active={props.active === item.id}
|
||||
text={open ? item.text : undefined}
|
||||
tagName="div"
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
getApplicationViewerPageURL,
|
||||
BUILDER_PAGE_URL,
|
||||
} from "constants/routes";
|
||||
import { Card, Tooltip, Classes } from "@blueprintjs/core";
|
||||
import { ApplicationPayload } from "constants/ReduxActionConstants";
|
||||
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
||||
|
|
@ -107,8 +112,9 @@ const Control = styled.button<{ fixed?: boolean }>`
|
|||
const APPLICATION_CONTROL_FONTSIZE_INDEX = 6;
|
||||
|
||||
const viewControlIcon = ControlIcons.VIEW_CONTROL({
|
||||
width: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
|
||||
height: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
|
||||
width: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX - 1],
|
||||
height: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX - 1],
|
||||
color: theme.colors.secondary,
|
||||
});
|
||||
|
||||
type ApplicationCardProps = {
|
||||
|
|
@ -150,33 +156,49 @@ export const ApplicationCard = (props: ApplicationCardProps) => {
|
|||
intent: "danger",
|
||||
},
|
||||
];
|
||||
const viewApplicationURL = getApplicationViewerPageURL(
|
||||
props.application.id,
|
||||
props.application.defaultPageId,
|
||||
);
|
||||
const editApplicationURL = BUILDER_PAGE_URL(
|
||||
props.application.id,
|
||||
props.application.defaultPageId,
|
||||
);
|
||||
return (
|
||||
<Wrapper key={props.application.id}>
|
||||
<ApplicationTitle
|
||||
className={props.loading ? Classes.SKELETON : undefined}
|
||||
>
|
||||
<span>{props.application.name}</span>
|
||||
<Control className="control">
|
||||
<Tooltip content="View Application" hoverOpenDelay={500}>
|
||||
{viewControlIcon}
|
||||
</Tooltip>
|
||||
</Control>
|
||||
<Link to={viewApplicationURL}>
|
||||
<Control className="control">
|
||||
<Tooltip content="View Application" hoverOpenDelay={500}>
|
||||
{viewControlIcon}
|
||||
</Tooltip>
|
||||
</Control>
|
||||
</Link>
|
||||
<ContextDropdown
|
||||
options={moreActionItems}
|
||||
toggle={{ type: "icon", icon: "MORE_VERTICAL_CONTROL" }}
|
||||
toggle={{
|
||||
type: "icon",
|
||||
icon: "MORE_VERTICAL_CONTROL",
|
||||
iconSize: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
|
||||
}}
|
||||
className="more"
|
||||
/>
|
||||
</ApplicationTitle>
|
||||
<ApplicationImage className="image-container">
|
||||
<Control className="control">
|
||||
<Tooltip content="Edit Application" hoverOpenDelay={500}>
|
||||
<BaseButton
|
||||
icon="edit"
|
||||
text="Edit"
|
||||
styleName="primary"
|
||||
filled={true}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Link to={editApplicationURL}>
|
||||
<Tooltip content="Edit Application" hoverOpenDelay={500}>
|
||||
<BaseButton
|
||||
icon="edit"
|
||||
text="Edit"
|
||||
styleName="primary"
|
||||
filled={true}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Link>
|
||||
</Control>
|
||||
</ApplicationImage>
|
||||
</Wrapper>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React, { Component } from "react";
|
||||
import styled from "styled-components";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router";
|
||||
import { AppState } from "../../reducers";
|
||||
import {
|
||||
getApplicationList,
|
||||
|
|
@ -54,7 +53,6 @@ type ApplicationProps = {
|
|||
isCreatingApplication: boolean;
|
||||
isFetchingApplications: boolean;
|
||||
createApplicationError?: string;
|
||||
history: any;
|
||||
};
|
||||
|
||||
class Applications extends Component<ApplicationProps> {
|
||||
|
|
@ -86,16 +84,20 @@ class Applications extends Component<ApplicationProps> {
|
|||
/>
|
||||
<SectionDivider />
|
||||
<ApplicationCardsWrapper>
|
||||
{applicationList.map((application: ApplicationPayload) => (
|
||||
<ApplicationCard
|
||||
key={application.id}
|
||||
loading={this.props.isFetchingApplications}
|
||||
application={application}
|
||||
share={noop}
|
||||
duplicate={noop}
|
||||
delete={noop}
|
||||
/>
|
||||
))}
|
||||
{applicationList.map((application: ApplicationPayload) => {
|
||||
return (
|
||||
application.pageCount > 0 && (
|
||||
<ApplicationCard
|
||||
key={application.id}
|
||||
loading={this.props.isFetchingApplications}
|
||||
application={application}
|
||||
share={noop}
|
||||
duplicate={noop}
|
||||
delete={noop}
|
||||
/>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</ApplicationCardsWrapper>
|
||||
</ApplicationsBody>
|
||||
</ApplicationsPageWrapper>
|
||||
|
|
@ -123,9 +125,4 @@ const mapDispatchToProps = (dispatch: any) => ({
|
|||
},
|
||||
});
|
||||
|
||||
export default withRouter(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(Applications),
|
||||
);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Applications);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ interface ReduxActionProps {
|
|||
|
||||
type Props = ReduxActionProps &
|
||||
ReduxStateProps &
|
||||
RouteComponentProps<{ id: string }>;
|
||||
RouteComponentProps<{ apiId: string; applicationId: string; pageId: string }>;
|
||||
|
||||
const EmptyStateContainer = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -47,21 +47,26 @@ const EmptyStateContainer = styled.div`
|
|||
|
||||
class ApiEditor extends React.Component<Props> {
|
||||
componentDidMount(): void {
|
||||
const currentId = this.props.match.params.id;
|
||||
if (!currentId) return;
|
||||
const currentApiId = this.props.match.params.apiId;
|
||||
const currentApplicationId = this.props.match.params.applicationId;
|
||||
const currentPageId = this.props.match.params.pageId;
|
||||
|
||||
if (!currentApiId) return;
|
||||
if (!this.props.actions.data.length) {
|
||||
this.props.history.push(API_EDITOR_URL);
|
||||
this.props.history.push(
|
||||
API_EDITOR_URL(currentApplicationId, currentPageId),
|
||||
);
|
||||
return;
|
||||
}
|
||||
const data = this.props.actions.data.filter(
|
||||
action => action.id === currentId,
|
||||
action => action.id === currentApiId,
|
||||
)[0];
|
||||
this.props.initialize(API_EDITOR_FORM_NAME, data);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<Props>): void {
|
||||
const currentId = this.props.match.params.id;
|
||||
if (currentId && currentId !== prevProps.match.params.id) {
|
||||
const currentId = this.props.match.params.apiId;
|
||||
if (currentId && currentId !== prevProps.match.params.apiId) {
|
||||
const data = this.props.actions.data.filter(
|
||||
action => action.id === currentId,
|
||||
)[0];
|
||||
|
|
@ -90,7 +95,7 @@ class ApiEditor extends React.Component<Props> {
|
|||
this.props.submitForm(API_EDITOR_FORM_NAME);
|
||||
};
|
||||
handleDeleteClick = () => {
|
||||
this.props.deleteAction(this.props.match.params.id);
|
||||
this.props.deleteAction(this.props.match.params.apiId);
|
||||
};
|
||||
handleRunClick = () => {
|
||||
this.props.runAction();
|
||||
|
|
@ -100,12 +105,12 @@ class ApiEditor extends React.Component<Props> {
|
|||
const {
|
||||
apiPane: { isSaving, isRunning, isDeleting },
|
||||
match: {
|
||||
params: { id },
|
||||
params: { apiId },
|
||||
},
|
||||
} = this.props;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{id ? (
|
||||
{apiId ? (
|
||||
<ApiEditorForm
|
||||
isSaving={isSaving}
|
||||
isRunning={isRunning}
|
||||
|
|
@ -142,7 +147,4 @@ const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({
|
|||
destroy: (formName: string) => dispatch(destroy(formName)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(ApiEditor);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ApiEditor);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ import { RouteComponentProps } from "react-router";
|
|||
import styled from "styled-components";
|
||||
import { AppState } from "../../reducers";
|
||||
import { ActionDataState } from "../../reducers/entityReducers/actionsReducer";
|
||||
import { API_EDITOR_ID_URL, API_EDITOR_URL } from "../../constants/routes";
|
||||
import {
|
||||
API_EDITOR_ID_URL,
|
||||
API_EDITOR_URL,
|
||||
APIEditorRouteParams,
|
||||
} from "../../constants/routes";
|
||||
import { BaseButton } from "../../components/designSystems/blueprint/ButtonComponent";
|
||||
import { FormIcons } from "../../icons/FormIcons";
|
||||
import { Spinner } from "@blueprintjs/core";
|
||||
|
|
@ -12,6 +16,7 @@ import { ApiPaneReduxState } from "../../reducers/uiReducers/apiPaneReducer";
|
|||
import { BaseTextInput } from "../../components/designSystems/appsmith/TextInputComponent";
|
||||
import { TICK } from "@blueprintjs/icons/lib/esm/generated/iconNames";
|
||||
import { createActionRequest } from "../../actions/actionActions";
|
||||
import { RestAction } from "api/ActionAPI";
|
||||
import Fuse from "fuse.js";
|
||||
|
||||
const LoadingContainer = styled.div`
|
||||
|
|
@ -134,7 +139,7 @@ interface ReduxDispatchProps {
|
|||
|
||||
type Props = ReduxStateProps &
|
||||
ReduxDispatchProps &
|
||||
RouteComponentProps<{ id: string }>;
|
||||
RouteComponentProps<APIEditorRouteParams>;
|
||||
type State = {
|
||||
isCreating: boolean;
|
||||
name: string;
|
||||
|
|
@ -160,7 +165,7 @@ class ApiSidebar extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<Props>): void {
|
||||
if (!prevProps.match.params.id && this.props.match.params.id) {
|
||||
if (!prevProps.match.params.apiId && this.props.match.params.apiId) {
|
||||
this.setState({
|
||||
isCreating: false,
|
||||
name: "",
|
||||
|
|
@ -170,7 +175,9 @@ class ApiSidebar extends React.Component<Props, State> {
|
|||
|
||||
handleCreateNew = () => {
|
||||
const { history } = this.props;
|
||||
history.push(API_EDITOR_URL);
|
||||
const { pageId, applicationId } = this.props.match.params;
|
||||
|
||||
history.push(API_EDITOR_URL(applicationId, pageId));
|
||||
this.setState({
|
||||
isCreating: true,
|
||||
name: "",
|
||||
|
|
@ -202,6 +209,7 @@ class ApiSidebar extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { applicationId, pageId } = this.props.match.params;
|
||||
const {
|
||||
apiPane,
|
||||
history,
|
||||
|
|
@ -209,9 +217,9 @@ class ApiSidebar extends React.Component<Props, State> {
|
|||
actions: { data },
|
||||
} = this.props;
|
||||
const { isCreating, search, name } = this.state;
|
||||
const activeActionId = match.params.id;
|
||||
const activeActionId = match.params.apiId;
|
||||
const fuse = new Fuse(data, fuseOptions);
|
||||
const actions = search ? fuse.search(search) : data;
|
||||
const actions: RestAction[] = search ? fuse.search(search) : data;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{apiPane.isFetching ? (
|
||||
|
|
@ -232,7 +240,11 @@ class ApiSidebar extends React.Component<Props, State> {
|
|||
{actions.map(action => (
|
||||
<ApiItem
|
||||
key={action.id}
|
||||
onClick={() => history.push(API_EDITOR_ID_URL(action.id))}
|
||||
onClick={() =>
|
||||
history.push(
|
||||
API_EDITOR_ID_URL(applicationId, pageId, action.id),
|
||||
)
|
||||
}
|
||||
isSelected={activeActionId === action.id}
|
||||
>
|
||||
{action.actionConfiguration ? (
|
||||
|
|
@ -291,7 +303,4 @@ const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({
|
|||
createAction: (name: string) => dispatch(createActionRequest({ name })),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(ApiSidebar);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ApiSidebar);
|
||||
|
|
|
|||
|
|
@ -39,11 +39,11 @@ const StretchedBreadCrumb = styled(Breadcrumbs)`
|
|||
|
||||
type EditorHeaderProps = {
|
||||
isSaving?: boolean;
|
||||
pageName: string;
|
||||
pageName?: string;
|
||||
onPublish: React.FormEventHandler;
|
||||
onCreatePage: (name: string) => void;
|
||||
pages?: PageListPayload;
|
||||
currentPageId: string;
|
||||
currentPageId?: string;
|
||||
switchToPage: (selectedPage: string) => void;
|
||||
isPublishing: boolean;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -197,7 +197,4 @@ export interface PropertyPaneFunctions {
|
|||
hidePropertyPane: () => void;
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(PropertyPane);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PropertyPane);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import React from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import SidebarComponent from "../../components/editorComponents/Sidebar";
|
||||
import NavBarItem from "../../components/editorComponents/NavBarItem";
|
||||
import { EDITOR_ROUTES } from "../../constants/routes";
|
||||
import { EDITOR_ROUTES, BuilderRouteParams } from "../../constants/routes";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: grid;
|
||||
|
|
@ -23,11 +24,16 @@ const EditorSidebar = styled(SidebarComponent)`
|
|||
`;
|
||||
|
||||
const Sidebar = () => {
|
||||
const params = useParams<BuilderRouteParams>();
|
||||
return (
|
||||
<Wrapper>
|
||||
<NavBar>
|
||||
{EDITOR_ROUTES.map(config => (
|
||||
<NavBarItem key={config.path} {...config} />
|
||||
<NavBarItem
|
||||
key={config.title}
|
||||
{...config}
|
||||
path={config.path(params.applicationId, params.pageId)}
|
||||
/>
|
||||
))}
|
||||
</NavBar>
|
||||
<EditorSidebar />
|
||||
|
|
|
|||
|
|
@ -1,16 +1,23 @@
|
|||
import React from "react";
|
||||
import React, { useEffect, ReactNode } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import Canvas from "./Canvas";
|
||||
import PropertyPane from "./PropertyPane";
|
||||
import { AppState } from "../../reducers";
|
||||
import { WidgetProps } from "../../widgets/BaseWidget";
|
||||
import { savePage } from "../../actions/pageActions";
|
||||
import { getDenormalizedDSL } from "../../selectors/editorSelectors";
|
||||
import {
|
||||
getDenormalizedDSL,
|
||||
getIsFetchingPage,
|
||||
getCurrentPageId,
|
||||
} from "selectors/editorSelectors";
|
||||
import { ContainerWidgetProps } from "../../widgets/ContainerWidget";
|
||||
import { ReduxActionTypes } from "../../constants/ReduxActionConstants";
|
||||
|
||||
import { BuilderRouteParams } from "constants/routes";
|
||||
import Centered from "components/designSystems/appsmith/CenteredWrapper";
|
||||
import EditorContextProvider from "components/editorComponents/EditorContextProvider";
|
||||
import { Spinner } from "@blueprintjs/core";
|
||||
|
||||
const EditorWrapper = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -38,31 +45,56 @@ const CanvasContainer = styled.section`
|
|||
`;
|
||||
|
||||
type EditorProps = {
|
||||
dsl: ContainerWidgetProps<WidgetProps> | any;
|
||||
dsl?: ContainerWidgetProps<WidgetProps>;
|
||||
savePageLayout: Function;
|
||||
showPropertyPane: (
|
||||
widgetId?: string,
|
||||
node?: HTMLDivElement,
|
||||
toggle?: boolean,
|
||||
) => void;
|
||||
fetchPage: (pageId: string) => void;
|
||||
currentPageId?: string;
|
||||
isFetchingPage: boolean;
|
||||
};
|
||||
|
||||
const WidgetsEditor = (props: EditorProps) => (
|
||||
<EditorContextProvider>
|
||||
<EditorWrapper>
|
||||
<CanvasContainer>
|
||||
{props.dsl && (
|
||||
<Canvas dsl={props.dsl} showPropertyPane={props.showPropertyPane} />
|
||||
)}
|
||||
</CanvasContainer>
|
||||
<PropertyPane />
|
||||
</EditorWrapper>
|
||||
</EditorContextProvider>
|
||||
);
|
||||
const WidgetsEditor = (props: EditorProps) => {
|
||||
const params = useParams<BuilderRouteParams>();
|
||||
const { pageId } = params;
|
||||
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
useEffect(() => {
|
||||
if (pageId !== props.currentPageId) {
|
||||
props.fetchPage(pageId);
|
||||
}
|
||||
}, [pageId]);
|
||||
|
||||
const pageLoading = (
|
||||
<Centered>
|
||||
<Spinner />
|
||||
</Centered>
|
||||
);
|
||||
let node: ReactNode;
|
||||
if (props.isFetchingPage) {
|
||||
node = pageLoading;
|
||||
}
|
||||
if (!props.isFetchingPage && props.dsl) {
|
||||
node = <Canvas dsl={props.dsl} showPropertyPane={props.showPropertyPane} />;
|
||||
}
|
||||
return (
|
||||
<EditorContextProvider>
|
||||
<EditorWrapper>
|
||||
<CanvasContainer>{node}</CanvasContainer>
|
||||
<PropertyPane />
|
||||
</EditorWrapper>
|
||||
</EditorContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
return {
|
||||
dsl: getDenormalizedDSL(state),
|
||||
isFetchingPage: getIsFetchingPage(state),
|
||||
currentPageId: getCurrentPageId(state),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -83,10 +115,12 @@ const mapDispatchToProps = (dispatch: any) => {
|
|||
payload: { widgetId, node, toggle },
|
||||
});
|
||||
},
|
||||
fetchPage: (pageId: string) =>
|
||||
dispatch({
|
||||
type: ReduxActionTypes.FETCH_PAGE_INIT,
|
||||
payload: { pageId },
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(WidgetsEditor);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WidgetsEditor);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect } from "react-router-dom";
|
||||
import { withRouter, RouteComponentProps } from "react-router-dom";
|
||||
import {
|
||||
BuilderRouteParams,
|
||||
getApplicationViewerPageURL,
|
||||
BUILDER_PAGE_URL,
|
||||
} from "constants/routes";
|
||||
import { AppState } from "../../reducers";
|
||||
import EditorHeader from "./EditorHeader";
|
||||
import MainContainer from "./MainContainer";
|
||||
|
|
@ -11,20 +18,23 @@ import {
|
|||
getIsPublishingApplication,
|
||||
getPublishingError,
|
||||
getIsPageSaving,
|
||||
} from "../../selectors/editorSelectors";
|
||||
getIsEditorLoading,
|
||||
getLoadingError,
|
||||
getPublishedTime,
|
||||
} from "selectors/editorSelectors";
|
||||
import {
|
||||
ReduxActionTypes,
|
||||
PageListPayload,
|
||||
} from "../../constants/ReduxActionConstants";
|
||||
} from "constants/ReduxActionConstants";
|
||||
import { Dialog, Classes, AnchorButton } from "@blueprintjs/core";
|
||||
import { initEditor } from "../../actions/initActions";
|
||||
import { initEditor } from "actions/initActions";
|
||||
|
||||
type EditorProps = {
|
||||
currentPageName: string;
|
||||
currentPageName?: string;
|
||||
isSaving: boolean;
|
||||
currentApplicationId?: string;
|
||||
currentLayoutId: string;
|
||||
currentPageId: string;
|
||||
currentLayoutId?: string;
|
||||
currentPageId?: string;
|
||||
publishApplication: Function;
|
||||
previewPage: Function;
|
||||
initEditor: Function;
|
||||
|
|
@ -32,8 +42,12 @@ type EditorProps = {
|
|||
pages: PageListPayload;
|
||||
switchPage: (pageId: string) => void;
|
||||
isPublishing: boolean;
|
||||
isEditorLoading: boolean;
|
||||
editorLoadingError: boolean;
|
||||
errorPublishing: boolean;
|
||||
};
|
||||
publishedTime: string | boolean;
|
||||
isPageSwitching: boolean;
|
||||
} & RouteComponentProps<BuilderRouteParams>;
|
||||
|
||||
class Editor extends Component<EditorProps> {
|
||||
public state = {
|
||||
|
|
@ -41,20 +55,37 @@ class Editor extends Component<EditorProps> {
|
|||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.props.initEditor();
|
||||
}
|
||||
componentDidUpdate(currently: EditorProps) {
|
||||
const previously = this.props;
|
||||
if (
|
||||
!currently.isPublishing &&
|
||||
previously.isPublishing &&
|
||||
!currently.errorPublishing
|
||||
) {
|
||||
this.setState({
|
||||
isDialogOpen: true,
|
||||
});
|
||||
if (this.props.match.params.applicationId) {
|
||||
this.props.initEditor(this.props.match.params.applicationId);
|
||||
}
|
||||
}
|
||||
componentDidUpdate(previously: EditorProps) {
|
||||
// const currently = this.props;
|
||||
// if (currently.publishedTime !== previously.publishedTime) {
|
||||
// this.setState({
|
||||
// isDialogOpen: true,
|
||||
// });
|
||||
// }
|
||||
// if (
|
||||
// currently.currentPageId &&
|
||||
// previously.currentPageId !== currently.currentPageId &&
|
||||
// currently.currentApplicationId
|
||||
// ) {
|
||||
// this.props.history.replace(
|
||||
// BUILDER_PAGE_URL(
|
||||
// currently.currentApplicationId,
|
||||
// currently.currentPageId,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// if (
|
||||
// previously.match.params.pageId !== currently.match.params.pageId &&
|
||||
// currently.currentPageId !== currently.match.params.pageId
|
||||
// ) {
|
||||
// this.props.switchPage(currently.match.params.pageId);
|
||||
// }
|
||||
}
|
||||
|
||||
handleDialogClose = () => {
|
||||
this.setState({
|
||||
isDialogOpen: false,
|
||||
|
|
@ -68,7 +99,17 @@ class Editor extends Component<EditorProps> {
|
|||
handleCreatePage = (pageName: string) => {
|
||||
this.props.createPage(this.props.currentApplicationId, pageName);
|
||||
};
|
||||
redirectToPage = (pageId: string) => {
|
||||
if (this.props.currentApplicationId) {
|
||||
this.props.history.push(
|
||||
BUILDER_PAGE_URL(this.props.currentApplicationId, pageId),
|
||||
);
|
||||
}
|
||||
};
|
||||
public render() {
|
||||
if (!this.props.match.params.applicationId) {
|
||||
return <Redirect to="/applications" />;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<EditorHeader
|
||||
|
|
@ -78,7 +119,7 @@ class Editor extends Component<EditorProps> {
|
|||
onCreatePage={this.handleCreatePage}
|
||||
pages={this.props.pages}
|
||||
currentPageId={this.props.currentPageId}
|
||||
switchToPage={this.props.switchPage}
|
||||
switchToPage={this.redirectToPage}
|
||||
isPublishing={this.props.isPublishing}
|
||||
/>
|
||||
<MainContainer />
|
||||
|
|
@ -101,7 +142,10 @@ class Editor extends Component<EditorProps> {
|
|||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<AnchorButton
|
||||
target="_blank"
|
||||
href={`/view/pages/${this.props.currentPageId}`}
|
||||
href={getApplicationViewerPageURL(
|
||||
this.props.currentApplicationId,
|
||||
this.props.currentPageId,
|
||||
)}
|
||||
text="View Application"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -121,11 +165,14 @@ const mapStateToProps = (state: AppState) => ({
|
|||
pages: getPageList(state),
|
||||
errorPublishing: getPublishingError(state),
|
||||
isPublishing: getIsPublishingApplication(state),
|
||||
isEditorLoading: getIsEditorLoading(state),
|
||||
editorLoadingError: getLoadingError(state),
|
||||
publishedTime: getPublishedTime(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => {
|
||||
return {
|
||||
initEditor: () => dispatch(initEditor()),
|
||||
initEditor: (applicationId: string) => dispatch(initEditor(applicationId)),
|
||||
publishApplication: (applicationId: string) => {
|
||||
dispatch({
|
||||
type: ReduxActionTypes.PUBLISH_APPLICATION_INIT,
|
||||
|
|
@ -154,7 +201,7 @@ const mapDispatchToProps = (dispatch: any) => {
|
|||
},
|
||||
switchPage: (pageId: string) => {
|
||||
dispatch({
|
||||
type: ReduxActionTypes.FETCH_PAGE,
|
||||
type: ReduxActionTypes.FETCH_PAGE_INIT,
|
||||
payload: {
|
||||
pageId,
|
||||
},
|
||||
|
|
@ -163,7 +210,4 @@ const mapDispatchToProps = (dispatch: any) => {
|
|||
};
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(Editor);
|
||||
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Editor));
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ import ApiEditor from "./ApiEditor";
|
|||
import {
|
||||
API_EDITOR_ID_URL,
|
||||
API_EDITOR_URL,
|
||||
BUILDER_URL,
|
||||
BUILDER_PAGE_URL,
|
||||
BUILDER_BASE_URL,
|
||||
BuilderRouteParams,
|
||||
APIEditorRouteParams,
|
||||
} from "../../constants/routes";
|
||||
import styled from "styled-components";
|
||||
|
||||
|
|
@ -39,27 +42,39 @@ interface RouterState {
|
|||
isVisible: boolean;
|
||||
}
|
||||
|
||||
class EditorsRouter extends React.Component<RouteComponentProps, RouterState> {
|
||||
constructor(props: RouteComponentProps) {
|
||||
class EditorsRouter extends React.Component<
|
||||
RouteComponentProps<BuilderRouteParams>,
|
||||
RouterState
|
||||
> {
|
||||
constructor(props: RouteComponentProps<APIEditorRouteParams>) {
|
||||
super(props);
|
||||
const { applicationId, pageId } = this.props.match.params;
|
||||
this.state = {
|
||||
isVisible: this.props.location.pathname !== BUILDER_URL,
|
||||
isVisible:
|
||||
this.props.location.pathname !== BUILDER_BASE_URL(applicationId) &&
|
||||
this.props.location.pathname !==
|
||||
BUILDER_PAGE_URL(applicationId, pageId),
|
||||
};
|
||||
}
|
||||
componentDidUpdate(prevProps: Readonly<RouteComponentProps>): void {
|
||||
if (this.props.location.pathname !== prevProps.location.pathname) {
|
||||
const { applicationId, pageId } = this.props.match.params;
|
||||
this.setState({
|
||||
isVisible: this.props.location.pathname !== BUILDER_URL,
|
||||
isVisible:
|
||||
this.props.location.pathname !== BUILDER_BASE_URL(applicationId) &&
|
||||
this.props.location.pathname !==
|
||||
BUILDER_PAGE_URL(applicationId, pageId),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleClose = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const { applicationId, pageId } = this.props.match.params;
|
||||
this.setState({
|
||||
isVisible: false,
|
||||
});
|
||||
this.props.history.replace(BUILDER_URL);
|
||||
this.props.history.replace(BUILDER_PAGE_URL(applicationId, pageId));
|
||||
};
|
||||
|
||||
preventClose = (e: React.MouseEvent) => {
|
||||
|
|
@ -74,8 +89,8 @@ class EditorsRouter extends React.Component<RouteComponentProps, RouterState> {
|
|||
onClick={this.preventClose}
|
||||
>
|
||||
<Switch>
|
||||
<Route exact path={API_EDITOR_URL} component={ApiEditor} />
|
||||
<Route path={API_EDITOR_ID_URL()} component={ApiEditor} />
|
||||
<Route exact path={API_EDITOR_URL()} component={ApiEditor} />
|
||||
<Route exact path={API_EDITOR_ID_URL()} component={ApiEditor} />
|
||||
</Switch>
|
||||
</DrawerWrapper>
|
||||
</Wrapper>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,32 @@
|
|||
import * as React from "react";
|
||||
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { NonIdealState, Button, Card, Elevation } from "@blueprintjs/core";
|
||||
import { RouterProps } from "react-router";
|
||||
|
||||
const NotFoundPageWrapper = styled.div`
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
const Title = styled.div`
|
||||
font-size: ${props => props.theme.fontSizes[10]}px;
|
||||
text-align: center;
|
||||
`;
|
||||
class PageNotFound extends React.PureComponent<RouterProps> {
|
||||
public render() {
|
||||
return (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<NotFoundPageWrapper>
|
||||
<Card elevation={Elevation.TWO}>
|
||||
<Title>
|
||||
<span role="img" aria-label="Page Not Found">
|
||||
🙊
|
||||
</span>
|
||||
</Title>
|
||||
<NonIdealState
|
||||
icon={"search"}
|
||||
title="Page not found"
|
||||
description={
|
||||
"The page you were looking for does not appear to exist"
|
||||
"We didn't mean for you to reach this page. Let's find your way back to building awesome applications."
|
||||
}
|
||||
action={
|
||||
<Button
|
||||
|
|
@ -25,7 +39,7 @@ class PageNotFound extends React.PureComponent<RouterProps> {
|
|||
}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</NotFoundPageWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const ProtectedRoute = ({
|
|||
}: {
|
||||
path: string;
|
||||
component: React.ReactType;
|
||||
exact?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<Route
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import actionsReducer from "./actionsReducer";
|
|||
import propertyPaneConfigReducer from "./propertyPaneConfigReducer";
|
||||
import datasourceReducer from "./datasourceReducer";
|
||||
import bindingsReducer from "./bindingsReducer";
|
||||
import pageListReducer from "./pageListReducer";
|
||||
|
||||
const entityReducer = combineReducers({
|
||||
canvasWidgets: canvasWidgetsReducer,
|
||||
|
|
@ -17,5 +18,7 @@ const entityReducer = combineReducers({
|
|||
propertyConfig: propertyPaneConfigReducer,
|
||||
datasources: datasourceReducer,
|
||||
nameBindings: bindingsReducer,
|
||||
pageList: pageListReducer,
|
||||
});
|
||||
|
||||
export default entityReducer;
|
||||
|
|
|
|||
36
app/client/src/reducers/entityReducers/pageListReducer.tsx
Normal file
36
app/client/src/reducers/entityReducers/pageListReducer.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { createReducer } from "utils/AppsmithUtils";
|
||||
import {
|
||||
ReduxAction,
|
||||
ReduxActionTypes,
|
||||
PageListPayload,
|
||||
} from "constants/ReduxActionConstants";
|
||||
|
||||
const initialState: PageListReduxState = {
|
||||
pages: [],
|
||||
};
|
||||
|
||||
const pageListReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS]: (
|
||||
state: PageListReduxState,
|
||||
action: ReduxAction<{ pages: PageListPayload; applicationId: string }>,
|
||||
) => {
|
||||
return {
|
||||
...state,
|
||||
...action.payload,
|
||||
};
|
||||
},
|
||||
[ReduxActionTypes.CREATE_PAGE_SUCCESS]: (
|
||||
state: PageListReduxState,
|
||||
action: ReduxAction<{ pageName: string; pageId: string; layoutId: string }>,
|
||||
) => {
|
||||
state.pages.push(action.payload);
|
||||
return { ...state };
|
||||
},
|
||||
});
|
||||
|
||||
export interface PageListReduxState {
|
||||
pages: PageListPayload;
|
||||
applicationId?: string;
|
||||
}
|
||||
|
||||
export default pageListReducer;
|
||||
|
|
@ -16,6 +16,7 @@ import { DatasourceDataState } from "./entityReducers/datasourceReducer";
|
|||
import { AppViewReduxState } from "./uiReducers/appViewReducer";
|
||||
import { ApplicationsReduxState } from "./uiReducers/applicationsReducer";
|
||||
import { BindingsDataState } from "./entityReducers/bindingsReducer";
|
||||
import { PageListReduxState } from "./entityReducers/pageListReducer";
|
||||
import { ApiPaneReduxState } from "./uiReducers/apiPaneReducer";
|
||||
|
||||
const appReducer = combineReducers({
|
||||
|
|
@ -45,6 +46,7 @@ export interface AppState {
|
|||
widgetConfig: WidgetConfigReducerState;
|
||||
datasources: DatasourceDataState;
|
||||
nameBindings: BindingsDataState;
|
||||
pageList: PageListReduxState;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,16 +9,14 @@ import {
|
|||
|
||||
const initialState: AppViewReduxState = {
|
||||
isFetchingPage: false,
|
||||
loading: false,
|
||||
pages: [],
|
||||
pageWidgetId: "0",
|
||||
};
|
||||
|
||||
const appViewReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS]: (
|
||||
state: AppViewReduxState,
|
||||
action: ReduxAction<PageListPayload>,
|
||||
) => {
|
||||
return { ...state, pages: action.payload };
|
||||
[ReduxActionTypes.INITIALIZE_PAGE_VIEWER]: (state: AppViewReduxState) => {
|
||||
return { ...state, loading: true };
|
||||
},
|
||||
[ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT]: (state: AppViewReduxState) => {
|
||||
return { ...state, dsl: undefined, isFetchingPage: true };
|
||||
|
|
@ -30,16 +28,13 @@ const appViewReducer = createReducer(initialState, {
|
|||
state: AppViewReduxState,
|
||||
action: ReduxAction<{
|
||||
pageId: string;
|
||||
layoutId: string;
|
||||
dsl: ContainerWidgetProps<WidgetProps>;
|
||||
pageWidgetId: string;
|
||||
}>,
|
||||
) => {
|
||||
return {
|
||||
pages: state.pages,
|
||||
dsl: action.payload.dsl,
|
||||
currentPageId: action.payload.pageId,
|
||||
currentLayoutId: action.payload.layoutId,
|
||||
isFetchingPage: false,
|
||||
pageWidgetId: action.payload.pageWidgetId,
|
||||
};
|
||||
|
|
@ -50,6 +45,7 @@ export interface AppViewReduxState {
|
|||
dsl?: ContainerWidgetProps<WidgetProps>;
|
||||
isFetchingPage: boolean;
|
||||
currentPageId?: string;
|
||||
loading: boolean;
|
||||
currentLayoutId?: string;
|
||||
pages: PageListPayload;
|
||||
pageWidgetId: string;
|
||||
|
|
|
|||
|
|
@ -1,28 +1,69 @@
|
|||
import { createReducer } from "../../utils/AppsmithUtils";
|
||||
import { getEditorConfigs } from "../../constants/ApiConstants";
|
||||
import { ReduxActionTypes } from "../../constants/ReduxActionConstants";
|
||||
import {
|
||||
ReduxActionTypes,
|
||||
ReduxActionErrorTypes,
|
||||
} from "../../constants/ReduxActionConstants";
|
||||
import { WidgetProps } from "../../widgets/BaseWidget";
|
||||
import { ContainerWidgetProps } from "../../widgets/ContainerWidget";
|
||||
import moment from "moment";
|
||||
import {
|
||||
ReduxAction,
|
||||
UpdateCanvasPayload,
|
||||
PageListPayload,
|
||||
} from "../../constants/ReduxActionConstants";
|
||||
} from "constants/ReduxActionConstants";
|
||||
|
||||
const editorConfigs = getEditorConfigs();
|
||||
const initialState: EditorReduxState = {
|
||||
pageWidgetId: "0",
|
||||
...editorConfigs,
|
||||
pages: [],
|
||||
loadingStates: {
|
||||
publishing: false,
|
||||
publishingError: false,
|
||||
published: false,
|
||||
saving: false,
|
||||
savingError: false,
|
||||
loading: false,
|
||||
loadingError: false,
|
||||
pageSwitchingError: false,
|
||||
isPageSwitching: false,
|
||||
},
|
||||
};
|
||||
|
||||
const editorReducer = createReducer(initialState, {
|
||||
[ReduxActionTypes.FETCH_PAGE_INIT]: (state: EditorReduxState) => ({
|
||||
...state,
|
||||
loadingStates: {
|
||||
...state.loadingStates,
|
||||
isPageSwitching: true,
|
||||
},
|
||||
}),
|
||||
[ReduxActionTypes.FETCH_PAGE_SUCCESS]: (state: EditorReduxState) => ({
|
||||
...state,
|
||||
loadingStates: {
|
||||
...state.loadingStates,
|
||||
isPageSwitching: false,
|
||||
},
|
||||
}),
|
||||
[ReduxActionErrorTypes.FETCH_PAGE_ERROR]: (state: EditorReduxState) => ({
|
||||
...state,
|
||||
loadingStates: {
|
||||
...state.loadingStates,
|
||||
isPageSwitching: false,
|
||||
},
|
||||
}),
|
||||
[ReduxActionTypes.INIT_EDITOR]: (state: EditorReduxState) => {
|
||||
state.loadingStates.loading = true;
|
||||
state.loadingStates.loadingError = false;
|
||||
return { ...state };
|
||||
},
|
||||
[ReduxActionTypes.INIT_EDITOR_SUCCESS]: (state: EditorReduxState) => {
|
||||
state.loadingStates.loading = false;
|
||||
state.loadingStates.loadingError = false;
|
||||
return { ...state };
|
||||
},
|
||||
[ReduxActionErrorTypes.INITIALIZE_EDITOR_ERROR]: (
|
||||
state: EditorReduxState,
|
||||
) => {
|
||||
state.loadingStates.loading = false;
|
||||
state.loadingStates.loadingError = true;
|
||||
return { ...state };
|
||||
},
|
||||
[ReduxActionTypes.PUBLISH_APPLICATION_INIT]: (state: EditorReduxState) => {
|
||||
state.loadingStates.publishing = true;
|
||||
state.loadingStates.publishingError = false;
|
||||
|
|
@ -36,19 +77,7 @@ const editorReducer = createReducer(initialState, {
|
|||
[ReduxActionTypes.PUBLISH_APPLICATION_SUCCESS]: (state: EditorReduxState) => {
|
||||
state.loadingStates.publishing = false;
|
||||
state.loadingStates.publishingError = false;
|
||||
return { ...state };
|
||||
},
|
||||
[ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS]: (
|
||||
state: EditorReduxState,
|
||||
action: ReduxAction<PageListPayload>,
|
||||
) => {
|
||||
return { ...state, pages: action.payload };
|
||||
},
|
||||
[ReduxActionTypes.CREATE_PAGE_SUCCESS]: (
|
||||
state: EditorReduxState,
|
||||
action: ReduxAction<{ pageName: string; pageId: string; layoutId: string }>,
|
||||
) => {
|
||||
state.pages.push(action.payload);
|
||||
state.loadingStates.published = moment().format();
|
||||
return { ...state };
|
||||
},
|
||||
[ReduxActionTypes.SAVE_PAGE_INIT]: (state: EditorReduxState) => {
|
||||
|
|
@ -86,18 +115,20 @@ const editorReducer = createReducer(initialState, {
|
|||
|
||||
export interface EditorReduxState {
|
||||
dsl?: ContainerWidgetProps<WidgetProps>;
|
||||
pageWidgetId: string;
|
||||
currentPageId: string;
|
||||
currentLayoutId: string;
|
||||
currentPageName: string;
|
||||
propertyPaneConfigsId: string;
|
||||
currentApplicationId?: string;
|
||||
pages: PageListPayload;
|
||||
pageWidgetId?: string;
|
||||
currentPageId?: string;
|
||||
currentLayoutId?: string;
|
||||
currentPageName?: string;
|
||||
loadingStates: {
|
||||
saving: boolean;
|
||||
savingError: boolean;
|
||||
publishing: boolean;
|
||||
published: string | boolean;
|
||||
publishingError: boolean;
|
||||
loading: boolean;
|
||||
loadingError: boolean;
|
||||
isPageSwitching: boolean;
|
||||
pageSwitchingError: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -237,9 +237,7 @@ export function* createActionSaga(actionPayload: ReduxAction<RestAction>) {
|
|||
|
||||
export function* fetchActionsSaga() {
|
||||
try {
|
||||
const response: GenericApiResponse<
|
||||
RestAction[]
|
||||
> = yield ActionAPI.fetchActions();
|
||||
const response: GenericApiResponse<RestAction[]> = yield ActionAPI.fetchActions();
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
yield put({
|
||||
|
|
@ -281,9 +279,9 @@ export function* updateActionSaga(
|
|||
export function* deleteActionSaga(actionPayload: ReduxAction<{ id: string }>) {
|
||||
try {
|
||||
const id = actionPayload.payload.id;
|
||||
const response: GenericApiResponse<
|
||||
RestAction
|
||||
> = yield ActionAPI.deleteAction(id);
|
||||
const response: GenericApiResponse<RestAction> = yield ActionAPI.deleteAction(
|
||||
id,
|
||||
);
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
AppToaster.show({
|
||||
|
|
|
|||
|
|
@ -3,15 +3,17 @@ import {
|
|||
ReduxActionErrorTypes,
|
||||
ReduxAction,
|
||||
ApplicationPayload,
|
||||
} from "../constants/ReduxActionConstants";
|
||||
} from "constants/ReduxActionConstants";
|
||||
import ApplicationApi, {
|
||||
PublishApplicationResponse,
|
||||
PublishApplicationRequest,
|
||||
FetchApplicationsResponse,
|
||||
CreateApplicationRequest,
|
||||
CreateApplicationResponse,
|
||||
ApplicationResponsePayload,
|
||||
ApplicationPagePayload,
|
||||
} from "../api/ApplicationApi";
|
||||
} from "api/ApplicationApi";
|
||||
import { getDefaultPageId } from "./SagaUtils";
|
||||
import { call, put, takeLatest, all, select } from "redux-saga/effects";
|
||||
|
||||
import { validateResponse } from "./ErrorSagas";
|
||||
|
|
@ -42,19 +44,6 @@ export function* publishApplicationSaga(
|
|||
}
|
||||
}
|
||||
|
||||
const getDefaultPageId = (
|
||||
pages?: ApplicationPagePayload[],
|
||||
): string | undefined => {
|
||||
let defaultPage: ApplicationPagePayload | undefined = undefined;
|
||||
if (pages) {
|
||||
pages.find(page => page.isDefault);
|
||||
if (!defaultPage) {
|
||||
defaultPage = pages[0];
|
||||
}
|
||||
}
|
||||
return defaultPage ? defaultPage.id : undefined;
|
||||
};
|
||||
|
||||
export function* fetchApplicationListSaga() {
|
||||
try {
|
||||
const response: FetchApplicationsResponse = yield call(
|
||||
|
|
@ -63,7 +52,11 @@ export function* fetchApplicationListSaga() {
|
|||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
const applicationListPayload: ApplicationPayload[] = response.data.map(
|
||||
application => ({
|
||||
(
|
||||
application: ApplicationResponsePayload & {
|
||||
pages: ApplicationPagePayload[];
|
||||
},
|
||||
) => ({
|
||||
name: application.name,
|
||||
organizationId: application.organizationId,
|
||||
id: application.id,
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ import { validateResponse } from "./ErrorSagas";
|
|||
|
||||
function* fetchDatasourcesSaga() {
|
||||
try {
|
||||
const response: GenericApiResponse<
|
||||
Datasource[]
|
||||
> = yield DatasourcesApi.fetchDatasources();
|
||||
const response: GenericApiResponse<Datasource[]> = yield DatasourcesApi.fetchDatasources();
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
yield put({
|
||||
|
|
@ -37,9 +35,9 @@ function* createDatasourceSaga(
|
|||
actionPayload: ReduxAction<CreateDatasourceConfig>,
|
||||
) {
|
||||
try {
|
||||
const response: GenericApiResponse<
|
||||
Datasource
|
||||
> = yield DatasourcesApi.createDatasource(actionPayload.payload);
|
||||
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.createDatasource(
|
||||
actionPayload.payload,
|
||||
);
|
||||
if (response.responseMeta.success) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,
|
||||
|
|
|
|||
|
|
@ -1,56 +1,52 @@
|
|||
import { all, select, put, takeLatest, take } from "redux-saga/effects";
|
||||
import { all, put, takeLatest, take } from "redux-saga/effects";
|
||||
import {
|
||||
ReduxAction,
|
||||
ReduxActionTypes,
|
||||
} from "../constants/ReduxActionConstants";
|
||||
import { getCurrentPageId } from "../selectors/editorSelectors";
|
||||
InitializeEditorPayload,
|
||||
} from "constants/ReduxActionConstants";
|
||||
|
||||
import { fetchEditorConfigs } from "../actions/configsActions";
|
||||
import { fetchPage, fetchPageList } from "../actions/pageActions";
|
||||
import { fetchPageList } from "../actions/pageActions";
|
||||
import { fetchActions } from "../actions/actionActions";
|
||||
import { fetchDatasources } from "../actions/datasourcesActions";
|
||||
import { initBindingMapListener } from "../actions/bindingActions";
|
||||
|
||||
function* initializeEditorSaga() {
|
||||
// Step 1: Start getting all the data needed by the app
|
||||
const currentPageId = yield select(getCurrentPageId);
|
||||
function* initializeEditorSaga(
|
||||
initializeEditorAction: ReduxAction<InitializeEditorPayload>,
|
||||
) {
|
||||
const { applicationId } = initializeEditorAction.payload;
|
||||
// Step 1: Start getting all the data needed by the
|
||||
yield all([
|
||||
put(initBindingMapListener()),
|
||||
put(fetchPageList()),
|
||||
put(fetchPageList(applicationId)),
|
||||
put(fetchEditorConfigs()),
|
||||
put(fetchPage(currentPageId)),
|
||||
put(initBindingMapListener()),
|
||||
put(fetchActions()),
|
||||
put(fetchDatasources()),
|
||||
]);
|
||||
// Step 2: Wait for all data to be in the state
|
||||
yield all([
|
||||
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
||||
take(ReduxActionTypes.UPDATE_CANVAS),
|
||||
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_DATASOURCES_SUCCESS),
|
||||
]);
|
||||
// Step 3: Create the success;
|
||||
|
||||
// Step 6: Notify UI that the editor is ready to go
|
||||
yield put({
|
||||
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
|
||||
});
|
||||
}
|
||||
|
||||
export function* initializeAppViewerSaga(
|
||||
action: ReduxAction<{ pageId: string }>,
|
||||
action: ReduxAction<{ pageId: string; applicationId: string }>,
|
||||
) {
|
||||
const { applicationId } = action.payload;
|
||||
yield put(initBindingMapListener());
|
||||
yield all([put(fetchPageList()), put(fetchActions())]);
|
||||
yield all([put(fetchPageList(applicationId)), put(fetchActions())]);
|
||||
|
||||
yield all([
|
||||
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
||||
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
|
||||
]);
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
|
||||
payload: action.payload,
|
||||
});
|
||||
yield take(ReduxActionTypes.UPDATE_CANVAS);
|
||||
yield put({
|
||||
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
|
||||
});
|
||||
}
|
||||
|
||||
export default function* watchInitSagas() {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,13 @@ import {
|
|||
ReduxAction,
|
||||
UpdateCanvasPayload,
|
||||
PageListPayload,
|
||||
FetchPageListPayload,
|
||||
} from "../constants/ReduxActionConstants";
|
||||
import { updateCanvas, savePageSuccess } from "../actions/pageActions";
|
||||
import {
|
||||
updateCanvas,
|
||||
savePageSuccess,
|
||||
fetchPageSuccess,
|
||||
} from "../actions/pageActions";
|
||||
import PageApi, {
|
||||
FetchPageResponse,
|
||||
SavePageResponse,
|
||||
|
|
@ -26,7 +31,6 @@ import {
|
|||
takeEvery,
|
||||
all,
|
||||
} from "redux-saga/effects";
|
||||
import { getPageLayoutId } from "./selectors";
|
||||
|
||||
import { extractCurrentDSL } from "../utils/WidgetPropsUtils";
|
||||
import { getEditorConfigs, getWidgets } from "./selectors";
|
||||
|
|
@ -34,19 +38,27 @@ import { validateResponse } from "./ErrorSagas";
|
|||
import { RenderModes } from "constants/WidgetConstants";
|
||||
import { UpdateWidgetPropertyPayload } from "actions/controlActions";
|
||||
|
||||
export function* fetchPageListSaga() {
|
||||
export function* fetchPageListSaga(
|
||||
fetchPageListAction: ReduxAction<FetchPageListPayload>,
|
||||
) {
|
||||
try {
|
||||
const response: FetchPageListResponse = yield call(PageApi.fetchPageList);
|
||||
const { applicationId } = fetchPageListAction.payload;
|
||||
const response: FetchPageListResponse = yield call(
|
||||
PageApi.fetchPageList,
|
||||
applicationId,
|
||||
);
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
const pageList: PageListPayload = response.data.map(page => ({
|
||||
const pages: PageListPayload = response.data.map(page => ({
|
||||
pageName: page.name,
|
||||
pageId: page.id,
|
||||
layoutId: page.layouts[0].id,
|
||||
}));
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS,
|
||||
payload: pageList,
|
||||
payload: {
|
||||
pages,
|
||||
applicationId,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -76,21 +88,6 @@ const getCanvasWidgetsPayload = (
|
|||
};
|
||||
};
|
||||
|
||||
// TODO(abhinav): Make this similar for both Render Modes
|
||||
const getAppViewWidgetsPayload = (pageResponse: any) => {
|
||||
const normalizedResponse = CanvasWidgetsNormalizer.normalize(
|
||||
pageResponse.data.dsl,
|
||||
);
|
||||
return {
|
||||
pageWidgetId: normalizedResponse.result,
|
||||
currentPageName: pageResponse.data.name,
|
||||
currentPageId: pageResponse.data.id,
|
||||
widgets: normalizedResponse.entities.canvasWidgets,
|
||||
currentLayoutId: pageResponse.data.id,
|
||||
currentApplicationId: pageResponse.data.applicationId,
|
||||
};
|
||||
};
|
||||
|
||||
export function* fetchPageSaga(
|
||||
pageRequestAction: ReduxAction<FetchPageRequest>,
|
||||
) {
|
||||
|
|
@ -104,6 +101,7 @@ export function* fetchPageSaga(
|
|||
if (isValidResponse) {
|
||||
const canvasWidgetsPayload = getCanvasWidgetsPayload(fetchPageResponse);
|
||||
yield put(updateCanvas(canvasWidgetsPayload));
|
||||
yield put(fetchPageSuccess());
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
|
|
@ -120,10 +118,8 @@ export function* fetchPublishedPageSaga(
|
|||
) {
|
||||
try {
|
||||
const { pageId } = pageRequestAction.payload;
|
||||
const layoutId: string = yield select(getPageLayoutId, pageId);
|
||||
const request: FetchPublishedPageRequest = {
|
||||
pageId,
|
||||
layoutId,
|
||||
};
|
||||
const response: FetchPublishedPageResponse = yield call(
|
||||
PageApi.fetchPublishedPage,
|
||||
|
|
@ -131,12 +127,12 @@ export function* fetchPublishedPageSaga(
|
|||
);
|
||||
const isValidResponse = yield validateResponse(response);
|
||||
if (isValidResponse) {
|
||||
const canvasWidgetsPayload = getAppViewWidgetsPayload(response);
|
||||
const canvasWidgetsPayload = getCanvasWidgetsPayload(response);
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS,
|
||||
payload: {
|
||||
dsl: response.data.dsl,
|
||||
layoutId: response.data.id,
|
||||
dsl: response.data.layouts[0].dsl,
|
||||
layoutId: response.data.layouts[0].id,
|
||||
pageId: request.pageId,
|
||||
pageWidgetId: canvasWidgetsPayload.pageWidgetId,
|
||||
},
|
||||
|
|
@ -248,7 +244,7 @@ export function* createPageSaga(
|
|||
|
||||
export default function* pageSagas() {
|
||||
yield all([
|
||||
takeLatest(ReduxActionTypes.FETCH_PAGE, fetchPageSaga),
|
||||
takeLatest(ReduxActionTypes.FETCH_PAGE_INIT, fetchPageSaga),
|
||||
takeLatest(
|
||||
ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
|
||||
fetchPublishedPageSaga,
|
||||
|
|
|
|||
13
app/client/src/sagas/SagaUtils.ts
Normal file
13
app/client/src/sagas/SagaUtils.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { ApplicationPagePayload } from "api/ApplicationApi";
|
||||
export const getDefaultPageId = (
|
||||
pages?: ApplicationPagePayload[],
|
||||
): string | undefined => {
|
||||
let defaultPage: ApplicationPagePayload | undefined = undefined;
|
||||
if (pages) {
|
||||
pages.find(page => page.isDefault);
|
||||
if (!defaultPage) {
|
||||
defaultPage = pages[0];
|
||||
}
|
||||
}
|
||||
return defaultPage ? defaultPage.id : undefined;
|
||||
};
|
||||
|
|
@ -14,12 +14,14 @@ export const getWidget = (state: AppState, widgetId: string): WidgetProps => {
|
|||
|
||||
export const getEditorConfigs = (
|
||||
state: AppState,
|
||||
): { pageId: string; layoutId: string } => {
|
||||
): { pageId: string; layoutId: string } | undefined => {
|
||||
const { currentLayoutId, currentPageId } = state.ui.editor;
|
||||
return {
|
||||
pageId: currentPageId,
|
||||
layoutId: currentLayoutId,
|
||||
};
|
||||
return currentLayoutId && currentPageId
|
||||
? {
|
||||
pageId: currentPageId,
|
||||
layoutId: currentLayoutId,
|
||||
}
|
||||
: undefined;
|
||||
};
|
||||
|
||||
export const getDefaultWidgetConfig = (
|
||||
|
|
@ -33,11 +35,20 @@ export const getDefaultWidgetConfig = (
|
|||
return widgetConfig;
|
||||
};
|
||||
|
||||
export const getPageLayoutId = (state: AppState, pageId: string): string => {
|
||||
const pages = state.ui.appView.pages;
|
||||
// export const getPageLayoutId = (state: AppState, pageId: string): string => {
|
||||
// const { pages } = state.entities.pageList;
|
||||
// const page = pages.find(page => page.pageId === pageId);
|
||||
// if (!page) {
|
||||
// throw Error("Page not found");
|
||||
// }
|
||||
// return page.layoutId;
|
||||
// };
|
||||
|
||||
//TODO(abhinav): The api will return a default flag in the future. use that
|
||||
// Also, check out `sagaUtils.ts` this has a getDefaultPageId. when the flag is available
|
||||
// remove that and use this.
|
||||
export const getDefaultPageId = (state: AppState, pageId?: string): string => {
|
||||
const { pages } = state.entities.pageList;
|
||||
const page = pages.find(page => page.pageId === pageId);
|
||||
if (!page) {
|
||||
throw Error("Page not found");
|
||||
}
|
||||
return page.layoutId;
|
||||
return page ? page.pageId : pages[0].pageId;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createSelector } from "reselect";
|
||||
import { AppState, DataTree } from "../reducers";
|
||||
import { AppViewReduxState } from "../reducers/uiReducers/appViewReducer";
|
||||
import { AppViewerProps } from "../pages/AppViewer";
|
||||
import { PageListReduxState } from "reducers/entityReducers/pageListReducer";
|
||||
import { getDataTree } from "./entitiesSelector";
|
||||
import createCachedSelector from "re-reselect";
|
||||
import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer";
|
||||
|
|
@ -9,13 +9,10 @@ import { getValidatedDynamicProps } from "./editorSelectors";
|
|||
import { CanvasWidgetsReduxState } from "../reducers/entityReducers/canvasWidgetsReducer";
|
||||
|
||||
const getAppViewState = (state: AppState) => state.ui.appView;
|
||||
|
||||
export const getCurrentLayoutId = (state: AppState, props: AppViewerProps) =>
|
||||
state.ui.appView.currentLayoutId || props.match.params.layoutId;
|
||||
export const getCurrentPageId = (state: AppState, props: AppViewerProps) =>
|
||||
state.ui.appView.currentPageId || props.match.params.pageId;
|
||||
export const getCurrentRoutePageId = (state: AppState, props: AppViewerProps) =>
|
||||
props.match.params.pageId;
|
||||
const getPageListState = (state: AppState): PageListReduxState =>
|
||||
state.entities.pageList;
|
||||
export const getCurrentPageId = (state: AppState) =>
|
||||
state.ui.appView.currentPageId;
|
||||
|
||||
// For the viewer, this does not need to be wrapped in createCachedSelector, as it will not change in subsequent renders.
|
||||
// export const getCurrentPageLayoutDSL = createSelector(
|
||||
|
|
@ -26,8 +23,9 @@ export const getCurrentRoutePageId = (state: AppState, props: AppViewerProps) =>
|
|||
// );
|
||||
|
||||
export const getPageList = createSelector(
|
||||
getAppViewState,
|
||||
(view: AppViewReduxState) => (view.pages.length > 0 ? view.pages : undefined),
|
||||
getPageListState,
|
||||
(pageList: PageListReduxState) =>
|
||||
pageList.pages.length > 0 ? pageList.pages : undefined,
|
||||
);
|
||||
|
||||
export const getIsFetchingPage = createSelector(
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
FlattenedWidgetProps,
|
||||
CanvasWidgetsReduxState,
|
||||
} from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import { PageListReduxState } from "reducers/entityReducers/pageListReducer";
|
||||
|
||||
import { OccupiedSpace } from "constants/editorConstants";
|
||||
import { WidgetTypes } from "constants/WidgetConstants";
|
||||
|
|
@ -20,18 +21,33 @@ import { WidgetTypes } from "constants/WidgetConstants";
|
|||
const getEditorState = (state: AppState) => state.ui.editor;
|
||||
const getWidgetConfigs = (state: AppState) => state.entities.widgetConfig;
|
||||
const getWidgetSideBar = (state: AppState) => state.ui.widgetSidebar;
|
||||
const getPageListState = (state: AppState) => state.entities.pageList;
|
||||
|
||||
const getWidgets = (state: AppState): CanvasWidgetsReduxState =>
|
||||
state.entities.canvasWidgets;
|
||||
|
||||
export const getPageList = createSelector(
|
||||
export const getIsEditorLoading = createSelector(
|
||||
getEditorState,
|
||||
(editor: EditorReduxState) => editor.pages,
|
||||
(editor: EditorReduxState) => editor.loadingStates.loading,
|
||||
);
|
||||
export const getIsFetchingPage = createSelector(
|
||||
getEditorState,
|
||||
(editor: EditorReduxState) => editor.loadingStates.isPageSwitching,
|
||||
);
|
||||
|
||||
export const getPropertyPaneConfigsId = createSelector(
|
||||
export const getPublishedTime = createSelector(
|
||||
getEditorState,
|
||||
(editor: EditorReduxState) => editor.propertyPaneConfigsId,
|
||||
(editor: EditorReduxState) => editor.loadingStates.published,
|
||||
);
|
||||
|
||||
export const getLoadingError = createSelector(
|
||||
getEditorState,
|
||||
(editor: EditorReduxState) => editor.loadingStates.loadingError,
|
||||
);
|
||||
|
||||
export const getPageList = createSelector(
|
||||
getPageListState,
|
||||
(pageList: PageListReduxState) => pageList.pages,
|
||||
);
|
||||
|
||||
export const getCurrentPageId = createSelector(
|
||||
|
|
@ -46,7 +62,7 @@ export const getCurrentLayoutId = createSelector(
|
|||
|
||||
export const getPageWidgetId = createSelector(
|
||||
getEditorState,
|
||||
(editor: EditorReduxState) => editor.pageWidgetId,
|
||||
(editor: EditorReduxState) => editor.pageWidgetId || "0",
|
||||
);
|
||||
|
||||
export const getCurrentPageName = createSelector(
|
||||
|
|
@ -55,8 +71,8 @@ export const getCurrentPageName = createSelector(
|
|||
);
|
||||
|
||||
export const getCurrentApplicationId = createSelector(
|
||||
getEditorState,
|
||||
(editor: EditorReduxState) => editor.currentApplicationId,
|
||||
getPageListState,
|
||||
(pageList: PageListReduxState) => pageList.applicationId,
|
||||
);
|
||||
|
||||
export const getIsPageSaving = createSelector(
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ const defaultDSL = {
|
|||
parentColumnSpace: 1,
|
||||
parentRowSpace: 1,
|
||||
renderMode: "CANVAS",
|
||||
rightColumn: 1300,
|
||||
snapColumns: 16,
|
||||
rightColumn: 1200,
|
||||
snapColumns: 24,
|
||||
snapRows: 32,
|
||||
topRow: 0,
|
||||
type: "CONTAINER_WIDGET",
|
||||
|
|
@ -47,8 +47,6 @@ export const extractCurrentDSL = (
|
|||
fetchPageResponse: FetchPageResponse,
|
||||
): ContainerWidgetProps<WidgetProps> => {
|
||||
const currentDSL = fetchPageResponse.data.layouts[0].dsl || defaultDSL;
|
||||
currentDSL.rightColumn = 1200;
|
||||
currentDSL.snapColumns = 24;
|
||||
return currentDSL;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -278,6 +278,6 @@ export const WidgetOperations = {
|
|||
DELETE: "DELETE",
|
||||
};
|
||||
|
||||
export type WidgetOperation = (typeof WidgetOperations)[keyof typeof WidgetOperations];
|
||||
export type WidgetOperation = typeof WidgetOperations[keyof typeof WidgetOperations];
|
||||
|
||||
export default BaseWidget;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import React from "react";
|
||||
import _ from "lodash";
|
||||
|
||||
import ContainerComponent from "../components/designSystems/appsmith/ContainerComponent";
|
||||
import ContainerComponent, {
|
||||
ContainerStyle,
|
||||
} from "components/designSystems/appsmith/ContainerComponent";
|
||||
import { ContainerOrientation, WidgetType } from "constants/WidgetConstants";
|
||||
import WidgetFactory from "utils/WidgetFactory";
|
||||
import { Color } from "constants/Colors";
|
||||
|
|
@ -107,6 +109,7 @@ export interface ContainerWidgetProps<T extends WidgetProps>
|
|||
snapRows?: number;
|
||||
orientation?: ContainerOrientation;
|
||||
backgroundColor?: Color;
|
||||
containerStyle?: ContainerStyle;
|
||||
}
|
||||
|
||||
export default ContainerWidget;
|
||||
|
|
|
|||
1308
app/client/yarn.lock
1308
app/client/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user