Feature - Applications Scaffold Page - Create Application

This commit is contained in:
Abhinav Jha 2019-11-07 04:59:40 +00:00
parent b244c4d784
commit 9ba528830d
20 changed files with 554 additions and 9 deletions

View File

@ -10,6 +10,31 @@ export interface PublishApplicationResponse extends ApiResponse {
data: {};
}
export interface ApplicationPagePayload {
id: string;
name: string;
isDefault: boolean;
}
export interface ApplicationResponsePayload {
id: string;
name: string;
organizationId: string;
pages?: ApplicationPagePayload[];
}
export interface FetchApplicationsResponse extends ApiResponse {
data: Array<ApplicationResponsePayload & { pages: ApplicationPagePayload[] }>;
}
export interface CreateApplicationResponse extends ApiResponse {
data: ApplicationResponsePayload;
}
export interface CreateApplicationRequest {
name: string;
}
class ApplicationApi extends Api {
static baseURL = "v1/applications/";
static publishURLPath = (applicationId: string) => `publish/${applicationId}`;
@ -23,6 +48,14 @@ class ApplicationApi extends Api {
{},
);
}
static fetchApplications(): AxiosPromise<FetchApplicationsResponse> {
return Api.get(ApplicationApi.baseURL);
}
static createApplication(
request: CreateApplicationRequest,
): AxiosPromise<CreateApplicationResponse> {
return Api.post(ApplicationApi.baseURL, request);
}
}
export default ApplicationApi;

View File

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="10" cy="10" r="10" fill="#21282C"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 10.9513C9.47555 10.9513 9.04876 10.5245 9.04876 10C9.04876 9.47556 9.47555 9.04876 10 9.04876C10.5245 9.04876 10.9513 9.47556 10.9513 10C10.9513 10.5245 10.5245 10.9513 10 10.9513ZM10 7.78042C8.77606 7.78042 7.78041 8.77607 7.78041 10C7.78041 11.224 8.77606 12.2196 10 12.2196C11.224 12.2196 12.2196 11.224 12.2196 10C12.2196 8.77607 11.224 7.78042 10 7.78042ZM10.1393 13.1694C7.4086 13.2328 5.62721 10.8971 5.03616 9.99723C5.68682 8.97938 7.32552 6.89549 9.86094 6.83081C12.5809 6.76168 14.3724 9.10304 14.9635 10.0029C14.3135 11.0208 12.6741 13.1047 10.1393 13.1694ZM16.2578 9.68458C15.8532 8.97938 13.6184 5.44451 9.8286 5.5631C6.3229 5.65188 4.28403 8.7403 3.74245 9.68458C3.6302 9.8799 3.6302 10.1203 3.74245 10.3156C4.14134 11.0113 6.29753 14.439 10.0157 14.439C10.0677 14.439 10.1197 14.4383 10.1717 14.4371C13.6768 14.3476 15.7163 11.2599 16.2578 10.3156C16.3694 10.1203 16.3694 9.8799 16.2578 9.68458Z" fill="#BCCCD9"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 10.9513C9.47555 10.9513 9.04876 10.5245 9.04876 10C9.04876 9.47556 9.47555 9.04876 10 9.04876C10.5245 9.04876 10.9513 9.47556 10.9513 10C10.9513 10.5245 10.5245 10.9513 10 10.9513ZM10 7.78042C8.77606 7.78042 7.78041 8.77607 7.78041 10C7.78041 11.224 8.77606 12.2196 10 12.2196C11.224 12.2196 12.2196 11.224 12.2196 10C12.2196 8.77607 11.224 7.78042 10 7.78042ZM10.1393 13.1694C7.4086 13.2328 5.62721 10.8971 5.03616 9.99723C5.68682 8.97938 7.32552 6.89549 9.86094 6.83081C12.5809 6.76168 14.3724 9.10304 14.9635 10.0029C14.3135 11.0208 12.6741 13.1047 10.1393 13.1694ZM16.2578 9.68458C15.8532 8.97938 13.6184 5.44451 9.8286 5.5631C6.3229 5.65188 4.28403 8.7403 3.74245 9.68458C3.6302 9.8799 3.6302 10.1203 3.74245 10.3156C4.14134 11.0113 6.29753 14.439 10.0157 14.439C10.0677 14.439 10.1197 14.4383 10.1717 14.4371C13.6768 14.3476 15.7163 11.2599 16.2578 10.3156C16.3694 10.1203 16.3694 9.8799 16.2578 9.68458Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,3 +1,5 @@
/* eslint-disable no-useless-escape */
// TODO (hetu): Remove useless escapes and re-enable the above lint rule
export type NamePathBindingMap = Record<string, string>;
export const DATA_BIND_REGEX = /{{(\s*[\w\.\[\]\d]+\s*)}}/g;
export const DATA_PATH_REGEX = /[\w\.\[\]\d]+/;

View File

@ -45,6 +45,10 @@ export type Theme = {
activeItemBGColor: Color;
navItemHeight: number;
};
card: {
minWidth: number;
minHeight: number;
};
};
export const getColorWithOpacity = (color: Color, opacity: number) => {
@ -134,6 +138,10 @@ export const theme: Theme = {
activeItemBGColor: Colors.SHARK,
navItemHeight: 42,
},
card: {
minWidth: 300,
minHeight: 300,
},
};
export { css, createGlobalStyle, keyframes, ThemeProvider };

View File

@ -5,6 +5,7 @@ export type IconProps = {
width: number;
height: number;
color: Color;
background: Color;
};
export const IconWrapper = styled.div<IconProps>`
@ -17,5 +18,8 @@ export const IconWrapper = styled.div<IconProps>`
path {
fill: ${props => props.color || props.theme.colors.textOnDarkBG};
}
circle {
fill: ${props => props.background || props.theme.colors.paneBG};
}
}
`;

View File

@ -66,6 +66,10 @@ export const ReduxActionTypes: { [key: string]: string } = {
FETCH_PAGE_LIST_INIT: "FETCH_PAGE_LIST_INIT",
FETCH_PAGE_LIST_SUCCESS: "FETCH_PAGE_LIST_SUCCESS",
INITIALIZE_PAGE_VIEWER: "INITIALIZE_PAGE_VIEWER",
FETCH_APPLICATION_LIST_INIT: "FETCH_APPLICATION_LIST_INIT",
FETCH_APPLICATION_LIST_SUCCESS: "FETCH_APPLICATION_LIST_SUCCESS",
CREATE_APPLICATION_INIT: "CREATE_APPLICATION_INIT",
CREATE_APPLICATION_SUCCESS: "CREATE_APPLICATION_SUCCESS",
CREATE_UPDATE_BINDINGS_MAP_INIT: "CREATE_UPDATE_BINDINGS_MAP_INIT",
CREATE_UPDATE_BINDINGS_MAP_SUCCESS: "CREATE_UPDATE_BINDINGS_MAP_SUCCESS",
};
@ -97,6 +101,8 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
PUBLISH_APPLICATION_ERROR: "PUBLISH_APPLICATION_ERROR",
CREATE_PAGE_ERROR: "CREATE_PAGE_ERROR",
FETCH_PAGE_LIST_ERROR: "FETCH_PAGE_LIST_ERROR",
FETCH_APPLICATION_LIST_ERROR: "FETCH_APPLICATION_LIST_ERROR",
CREATE_APPLICATION_ERROR: "CREATE_APPLICATION_ERROR",
};
export type ReduxActionErrorType = (typeof ReduxActionErrorTypes)[keyof typeof ReduxActionErrorTypes];
@ -134,6 +140,14 @@ export type PageListPayload = Array<{
layoutId: string;
}>;
export type ApplicationPayload = {
id: string;
name: string;
organizationId: string;
pageCount: number;
defaultPageId?: string;
};
// export interface LoadAPIResponsePayload extends ExecuteActionResponse {}
// export interface LoadQueryResponsePayload extends ExecuteActionResponse {}

View File

@ -6,13 +6,23 @@ 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`;
// TODO(abhinav): We probably need a utils/routes file for such functions.
export const getApplicationBuilderURL = (applicationId: string) =>
`${BUILDER_URL}/${applicationId}`;
export const getApplicationViewerURL = (
applicationId: string,
pageId?: string,
) => `/view/application/${applicationId}/pages/${pageId}`;
export const EDITOR_ROUTES = [
{
icon: MenuIcons.WIDGETS_ICON,
path: BUILDER_URL,
title: "Widgets",
exact: true,
exact: false,
},
{
icon: MenuIcons.APIS_ICON,

View File

@ -3,6 +3,7 @@ import { IconProps, IconWrapper } from "../constants/IconConstants";
import { ReactComponent as DeleteIcon } from "../assets/icons/control/delete.svg";
import { ReactComponent as MoveIcon } from "../assets/icons/control/move.svg";
import { ReactComponent as EditIcon } from "../assets/icons/control/edit.svg";
import { ReactComponent as ViewIcon } from "../assets/icons/control/view.svg";
/* eslint-disable react/display-name */
@ -24,4 +25,9 @@ export const ControlIcons: {
<EditIcon />
</IconWrapper>
),
VIEW_CONTROL: (props: IconProps) => (
<IconWrapper {...props}>
<ViewIcon />
</IconWrapper>
),
};

View File

@ -26,7 +26,9 @@ import {
BUILDER_URL,
LOGIN_URL,
APP_VIEW_URL,
APPLICATIONS_URL,
} from "./constants/routes";
import Applications from "./pages/Applications";
appInitializer();
const sagaMiddleware = createSagaMiddleware();
@ -44,6 +46,7 @@ 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} />
<Route exact path={LOGIN_URL} component={LoginPage} />
<Route component={PageNotFound} />
</Switch>

View File

@ -0,0 +1,51 @@
import React from "react";
import styled from "styled-components";
import StyledHeader from "../../components/designSystems/appsmith/StyledHeader";
import {
Popover,
Button,
Position,
IIconProps,
PopoverInteractionKind,
} from "@blueprintjs/core";
const StyledAddButton = styled(Button)<IIconProps>`
&&& {
background: ${props => props.theme.colors.primary};
span {
color: white;
}
}
`;
type ApplicationsHeaderProps = {
add?: {
form: JSX.Element;
title: string;
};
};
export const ApplicationsHeader = (props: ApplicationsHeaderProps) => {
return (
<StyledHeader>
{props.add && (
<Popover
interactionKind={PopoverInteractionKind.CLICK}
popoverClassName="bp3-popover-content-sizing"
position={Position.BOTTOM}
>
<StyledAddButton
text={props.add.title}
icon="plus"
title={props.add.title}
minimal
large
/>
{props.add.form}
</Popover>
)}
</StyledHeader>
);
};
export default ApplicationsHeader;

View File

@ -0,0 +1,38 @@
import React, { useRef, MutableRefObject } from "react";
import { BaseButton } from "../../components/designSystems/blueprint/ButtonComponent";
import { ControlGroup, Classes } from "@blueprintjs/core";
type CreateApplicationFormProps = {
onCreate: (name: string) => void;
creating: boolean;
error?: string;
};
export const CreateApplicationForm = (props: CreateApplicationFormProps) => {
const inputRef: MutableRefObject<HTMLInputElement | null> = useRef(null);
const handleCreate = () => {
if (inputRef && inputRef.current) {
props.onCreate(inputRef.current.value);
} else {
//TODO (abhinav): Add validation code.
}
};
return (
<ControlGroup fill vertical>
<input
type="text"
className={Classes.INPUT}
ref={inputRef}
placeholder="Application Name"
/>
<BaseButton
text="Create Application"
onClick={handleCreate}
styleName="secondary"
loading={props.creating}
></BaseButton>
</ControlGroup>
);
};
export default CreateApplicationForm;

View File

@ -0,0 +1,199 @@
import React, { Component } from "react";
import styled from "styled-components";
import { connect } from "react-redux";
import { withRouter } from "react-router";
import {
getApplicationBuilderURL,
getApplicationViewerURL,
} from "../../constants/routes";
import { AppState } from "../../reducers";
import {
getApplicationList,
getIsFetchingApplications,
getIsCreatingApplication,
} from "../../selectors/applicationSelectors";
import {
ReduxActionTypes,
ApplicationPayload,
} from "../../constants/ReduxActionConstants";
import { Card, Spinner, Tooltip } from "@blueprintjs/core";
import { ControlIcons } from "../../icons/ControlIcons";
import { theme } from "../../constants/DefaultTheme";
import ApplicationsHeader from "./ApplicationsHeader";
import CreateApplicationForm from "./CreateApplicationForm";
const APPLICATION_CONTROL_FONTSIZE_INDEX = 7;
const ApplicationsBody = styled.section`
width: 100vw;
min-height: calc(100vh - ${props => props.theme.headerHeight});
display: flex;
justify-content: center;
align-items: center;
background: white;
`;
const ApplicationCardsWrapper = styled.div`
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: space-around;
width: 80%;
`;
const ApplicationCard = styled(Card)`
display: flex;
flex-direction: column;
justify-content: center;
min-width: ${props => props.theme.card.minWidth}px;
min-height: ${props => props.theme.card.minHeight}px;
position: relative;
margin-bottom: ${props => props.theme.spaces[2]}px;
margin-right: ${props => props.theme.spaces[2]}px;
&:hover {
& div.controls {
display: flex;
}
}
`;
const ApplicationTitle = styled.h1`
font-size: ${props => props.theme.fontSizes[7]}px;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
`;
const ApplicationControls = styled.div`
display: none;
flex-flow: row nowrap;
justify-content: space-around;
`;
const Control = styled.button<{ fixed?: boolean }>`
outline: none;
background: none;
border: none;
cursor: pointer;
position: ${props => (props.fixed ? "absolute" : "auto")};
right: ${props => props.theme.spaces[2]}px;
top: ${props => props.theme.spaces[2]}px;
`;
const editControlIcon = ControlIcons.EDIT_CONTROL({
width: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
height: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
});
const deleteControlIcon = ControlIcons.DELETE_CONTROL({
width: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
height: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
background: theme.colors.error,
});
const viewControlIcon = ControlIcons.VIEW_CONTROL({
width: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
height: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
});
type ApplicationProps = {
applicationList: ApplicationPayload[];
fetchApplications: () => void;
createApplication: (appName: string) => void;
isCreatingApplication: boolean;
history: any;
};
class Applications extends Component<ApplicationProps> {
handleEditApplication = (applicationId: string) => () => {
this.props.history.push(getApplicationBuilderURL(applicationId));
};
handleViewApplication = (applicationId: string, pageId?: string) => () => {
this.props.history.push(getApplicationViewerURL(applicationId, pageId));
};
renderApplicationCard = (application: ApplicationPayload) => {
return (
<ApplicationCard interactive key={application.id}>
<ApplicationTitle>{application.name}</ApplicationTitle>
<ApplicationControls className="controls">
<Control fixed>
<Tooltip content="Delete Application" hoverOpenDelay={500}>
{deleteControlIcon}
</Tooltip>
</Control>
<Control
onClick={this.handleViewApplication(
application.id,
application.defaultPageId,
)}
>
<Tooltip content="View Application" hoverOpenDelay={500}>
{viewControlIcon}
</Tooltip>
</Control>
<Control onClick={this.handleEditApplication(application.id)}>
<Tooltip content="Edit Application" hoverOpenDelay={500}>
{editControlIcon}
</Tooltip>
</Control>
</ApplicationControls>
</ApplicationCard>
);
};
componentDidMount() {
this.props.fetchApplications();
}
public render() {
return (
<div>
<ApplicationsHeader
add={{
form: (
<CreateApplicationForm
onCreate={this.props.createApplication}
creating={this.props.isCreatingApplication}
/>
),
title: "Create Application",
}}
/>
<ApplicationsBody>
{this.props.applicationList ? (
<ApplicationCardsWrapper>
{this.props.applicationList.map(this.renderApplicationCard)}
</ApplicationCardsWrapper>
) : (
<Spinner />
)}
</ApplicationsBody>
</div>
);
}
}
const mapStateToProps = (state: AppState) => ({
applicationList: getApplicationList(state),
isFetchingApplications: getIsFetchingApplications(state),
isCreatingApplication: getIsCreatingApplication(state),
});
const mapDispatchToProps = (dispatch: any) => ({
fetchApplications: () =>
dispatch({ type: ReduxActionTypes.FETCH_APPLICATION_LIST_INIT }),
createApplication: (appName: string) => {
dispatch({
type: ReduxActionTypes.CREATE_APPLICATION_INIT,
payload: {
name: appName,
},
});
},
});
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(Applications),
);

View File

@ -14,6 +14,7 @@ import { WidgetConfigReducerState } from "./entityReducers/widgetConfigReducer";
import { WidgetSidebarReduxState } from "./uiReducers/widgetSidebarReducer";
import { ResourceDataState } from "./entityReducers/resourcesReducer";
import { AppViewReduxState } from "./uiReducers/appViewReducer";
import { ApplicationsReduxState } from "./uiReducers/applicationsReducer";
import { BindingsDataState } from "./entityReducers/bindingsReducer";
const appReducer = combineReducers({
@ -30,7 +31,8 @@ export interface AppState {
editor: EditorReduxState;
propertyPane: PropertyPaneReduxState;
errors: ErrorReduxState;
view: AppViewReduxState;
appView: AppViewReduxState;
applications: ApplicationsReduxState;
};
entities: {
canvasWidgets: CanvasWidgetsReduxState;

View File

@ -0,0 +1,52 @@
import { createReducer } from "../../utils/AppsmithUtils";
import {
ReduxAction,
ReduxActionTypes,
ReduxActionErrorTypes,
ApplicationPayload,
} from "../../constants/ReduxActionConstants";
const initialState: ApplicationsReduxState = {
isFetchingApplications: false,
applicationList: [],
creatingApplication: false,
};
const applicationsReducer = createReducer(initialState, {
[ReduxActionTypes.FETCH_APPLICATION_LIST_INIT]: (
state: ApplicationsReduxState,
) => ({ ...state, isFetchingApplications: true }),
[ReduxActionTypes.FETCH_APPLICATION_LIST_SUCCESS]: (
state: ApplicationsReduxState,
action: ReduxAction<{ applicationList: ApplicationPayload[] }>,
) => ({ ...state, applicationList: action.payload }),
[ReduxActionTypes.CREATE_APPLICATION_INIT]: (
state: ApplicationsReduxState,
) => ({ ...state, creatingApplication: true }),
[ReduxActionTypes.CREATE_APPLICATION_SUCCESS]: (
state: ApplicationsReduxState,
action: ReduxAction<ApplicationPayload>,
) => {
return {
...state,
creatingApplication: false,
applicationList: [...state.applicationList, action.payload],
};
},
[ReduxActionErrorTypes.CREATE_APPLICATION_ERROR]: (
state: ApplicationsReduxState,
) => {
return {
...state,
creatingApplication: false,
};
},
});
export interface ApplicationsReduxState {
applicationList: ApplicationPayload[];
isFetchingApplications: boolean;
creatingApplication: boolean;
}
export default applicationsReducer;

View File

@ -3,6 +3,7 @@ import editorReducer from "./editorReducer";
import errorReducer from "./errorReducer";
import propertyPaneReducer from "./propertyPaneReducer";
import appViewReducer from "./appViewReducer";
import applicationsReducer from "./applicationsReducer";
import { widgetSidebarReducer } from "./widgetSidebarReducer";
const uiReducer = combineReducers({
@ -10,6 +11,7 @@ const uiReducer = combineReducers({
editor: editorReducer,
errors: errorReducer,
propertyPane: propertyPaneReducer,
view: appViewReducer,
appView: appViewReducer,
applications: applicationsReducer,
});
export default uiReducer;

View File

@ -2,10 +2,15 @@ import {
ReduxActionTypes,
ReduxActionErrorTypes,
ReduxAction,
ApplicationPayload,
} from "../constants/ReduxActionConstants";
import ApplicationApi, {
PublishApplicationResponse,
PublishApplicationRequest,
FetchApplicationsResponse,
CreateApplicationRequest,
CreateApplicationResponse,
ApplicationPagePayload,
} from "../api/ApplicationApi";
import { call, put, takeLatest, all } from "redux-saga/effects";
@ -36,11 +41,92 @@ 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(
ApplicationApi.fetchApplications,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
const applicationListPayload: ApplicationPayload[] = response.data.map(
application => ({
name: application.name,
organizationId: application.organizationId,
id: application.id,
pageCount: application.pages ? application.pages.length : 0,
defaultPageId: getDefaultPageId(application.pages),
}),
);
yield put({
type: ReduxActionTypes.FETCH_APPLICATION_LIST_SUCCESS,
payload: applicationListPayload,
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.FETCH_APPLICATION_LIST_ERROR,
payload: {
error,
},
});
}
}
export function* createApplicationSaga(
request: ReduxAction<CreateApplicationRequest>,
) {
try {
const response: CreateApplicationResponse = yield call(
ApplicationApi.createApplication,
request.payload,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
const application: ApplicationPayload = {
id: response.data.id,
name: response.data.name,
organizationId: response.data.organizationId,
pageCount: response.data.pages ? response.data.pages.length : 0,
defaultPageId: getDefaultPageId(response.data.pages),
};
yield put({
type: ReduxActionTypes.CREATE_APPLICATION_SUCCESS,
payload: application,
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.CREATE_APPLICATION_ERROR,
payload: {
error,
},
});
}
}
export default function* applicationSagas() {
yield all([
takeLatest(
ReduxActionTypes.PUBLISH_APPLICATION_INIT,
publishApplicationSaga,
),
takeLatest(
ReduxActionTypes.FETCH_APPLICATION_LIST_INIT,
fetchApplicationListSaga,
),
takeLatest(ReduxActionTypes.CREATE_APPLICATION_INIT, createApplicationSaga),
]);
}

View File

@ -196,8 +196,13 @@ export function* saveLayoutSaga(
type: ReduxActionTypes.SAVE_PAGE_INIT,
payload: getLayoutSavePayload(widgets, editorConfigs),
});
} catch (err) {
console.log(err);
} catch (error) {
yield put({
type: ReduxActionErrorTypes.SAVE_PAGE_ERROR,
payload: {
error,
},
});
}
}
@ -221,7 +226,6 @@ export function* asyncSaveLayout() {
throw Error("Error when saving layout");
}
} catch (error) {
console.log(error);
yield put({
type: ReduxActionErrorTypes.UPDATE_WIDGET_PROPERTY_ERROR,
payload: {

View File

@ -34,7 +34,7 @@ export const getDefaultWidgetConfig = (
};
export const getPageLayoutId = (state: AppState, pageId: string): string => {
const pages = state.ui.view.pages;
const pages = state.ui.appView.pages;
const page = pages.find(page => page.pageId === pageId);
if (!page) {
throw Error("Page not found");

View File

@ -3,10 +3,12 @@ import { AppState } from "../reducers";
import { AppViewReduxState } from "../reducers/uiReducers/appViewReducer";
import { AppViewerProps } from "../pages/AppViewer";
const getAppViewState = (state: AppState) => state.ui.view;
const getAppViewState = (state: AppState) => state.ui.appView;
export const getCurrentLayoutId = (state: AppState, props: AppViewerProps) =>
state.ui.view.currentLayoutId || props.match.params.layoutId;
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;

View File

@ -0,0 +1,24 @@
import { createSelector } from "reselect";
import { AppState } from "../reducers";
import { ApplicationsReduxState } from "../reducers/uiReducers/applicationsReducer";
import { ApplicationPayload } from "../constants/ReduxActionConstants";
const getApplicationsState = (state: AppState) => state.ui.applications;
export const getApplicationList = createSelector(
getApplicationsState,
(applications: ApplicationsReduxState): ApplicationPayload[] =>
applications.applicationList,
);
export const getIsFetchingApplications = createSelector(
getApplicationsState,
(applications: ApplicationsReduxState): boolean =>
applications.isFetchingApplications,
);
export const getIsCreatingApplication = createSelector(
getApplicationsState,
(applications: ApplicationsReduxState): boolean =>
applications.creatingApplication,
);