Merge branch 'feature/datasourcesDraft' into 'release'
Feature/datasources draft Add drafts for the datasource pane. See merge request theappsmith/internal-tools-client!600
This commit is contained in:
commit
7e173fc2a7
|
|
@ -22,6 +22,13 @@ export const updateDatasource = (payload: Datasource) => {
|
|||
};
|
||||
};
|
||||
|
||||
export const changeDatasource = (payload: Datasource) => {
|
||||
return {
|
||||
type: ReduxActionTypes.CHANGE_DATASOURCE,
|
||||
payload,
|
||||
};
|
||||
};
|
||||
|
||||
export const testDatasource = (payload: Partial<Datasource>) => {
|
||||
return {
|
||||
type: ReduxActionTypes.TEST_DATASOURCE_INIT,
|
||||
|
|
|
|||
|
|
@ -26,16 +26,6 @@ const KeyValueRow = (props: Props & WrappedFieldArrayProps) => {
|
|||
}
|
||||
}, [props.fields]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof props.fields.getAll() === "string") {
|
||||
const fieldsValue: [] = JSON.parse(`${props.fields.getAll()}`);
|
||||
props.fields.removeAll();
|
||||
fieldsValue.forEach((value, index) => {
|
||||
props.fields.insert(index, value);
|
||||
});
|
||||
}
|
||||
}, [props.fields]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{typeof props.fields.getAll() === "object" && (
|
||||
|
|
|
|||
|
|
@ -67,9 +67,12 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
|||
CREATE_DATASOURCE_FROM_FORM_INIT: "CREATE_DATASOURCE_FROM_FORM_INIT",
|
||||
UPDATE_DATASOURCE_INIT: "UPDATE_DATASOURCE_INIT",
|
||||
UPDATE_DATASOURCE_SUCCESS: "UPDATE_DATASOURCE_SUCCESS",
|
||||
CHANGE_DATASOURCE: "CHANGE_DATASOURCE",
|
||||
SELECT_PLUGIN: "SELECT_PLUGIN",
|
||||
TEST_DATASOURCE_INIT: "TEST_DATASOURCE_INIT",
|
||||
TEST_DATASOURCE_SUCCESS: "TEST_DATASOURCE_SUCCESS",
|
||||
DELETE_DATASOURCE_DRAFT: "DELETE_DATASOURCE_DRAFT",
|
||||
UPDATE_DATASOURCE_DRAFT: "UPDATE_DATASOURCE_DRAFT",
|
||||
FETCH_PUBLISHED_PAGE_INIT: "FETCH_PUBLISHED_PAGE_INIT",
|
||||
FETCH_PUBLISHED_PAGE_SUCCESS: "FETCH_PUBLISHED_PAGE_SUCCESS",
|
||||
DELETE_DATASOURCE_INIT: "DELETE_DATASOURCE_INIT",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
initDatasourcePane,
|
||||
storeDatastoreRefs,
|
||||
deleteDatasource,
|
||||
changeDatasource,
|
||||
} from "actions/datasourceActions";
|
||||
import { ControlIcons } from "icons/ControlIcons";
|
||||
import { theme } from "constants/DefaultTheme";
|
||||
|
|
@ -25,10 +26,7 @@ import ImageAlt from "assets/images/placeholder-image.svg";
|
|||
import Postgres from "assets/images/Postgress.png";
|
||||
import MongoDB from "assets/images/MongoDB.png";
|
||||
import RestTemplateImage from "assets/images/RestAPI.png";
|
||||
import {
|
||||
DATA_SOURCES_EDITOR_ID_URL,
|
||||
DATA_SOURCES_EDITOR_URL,
|
||||
} from "constants/routes";
|
||||
import { DATA_SOURCES_EDITOR_URL } from "constants/routes";
|
||||
import { REST_PLUGIN_PACKAGE_NAME } from "constants/ApiEditorConstants";
|
||||
import {
|
||||
PLUGIN_PACKAGE_POSTGRES,
|
||||
|
|
@ -44,6 +42,7 @@ interface ReduxDispatchProps {
|
|||
storeDatastoreRefs: (refsList: []) => void;
|
||||
fetchFormConfig: (id: string) => void;
|
||||
deleteDatasource: (id: string) => void;
|
||||
onDatasourceChange: (datasource: Datasource) => void;
|
||||
}
|
||||
|
||||
interface ReduxStateProps {
|
||||
|
|
@ -51,6 +50,7 @@ interface ReduxStateProps {
|
|||
plugins: Plugin[];
|
||||
datastoreRefs: Record<string, any>;
|
||||
formConfigs: Record<string, []>;
|
||||
drafts: Record<string, Datasource>;
|
||||
}
|
||||
|
||||
type DataSourceSidebarProps = {};
|
||||
|
|
@ -172,6 +172,15 @@ const Container = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
const DraftIconIndicator = styled.span<{ isHidden: boolean }>`
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 8px;
|
||||
background-color: #f2994a;
|
||||
margin: 0 5px;
|
||||
opacity: ${({ isHidden }) => (isHidden ? 0 : 1)};
|
||||
`;
|
||||
|
||||
type Props = DataSourceSidebarProps &
|
||||
RouteComponentProps<{
|
||||
pageId: string;
|
||||
|
|
@ -202,6 +211,13 @@ class DataSourceSidebar extends React.Component<Props, State> {
|
|||
storeDatastoreRefs(this.refsCollection);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: Readonly<Props>): boolean {
|
||||
if (Object.keys(nextProps.drafts) !== Object.keys(this.props.drafts)) {
|
||||
return true;
|
||||
}
|
||||
return nextProps.dataSources !== this.props.dataSources;
|
||||
}
|
||||
|
||||
handleCreateNewDatasource = () => {
|
||||
const { history } = this.props;
|
||||
const { pageId, applicationId } = this.props.match.params;
|
||||
|
|
@ -210,17 +226,7 @@ class DataSourceSidebar extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
handleItemSelected = (datasource: Datasource) => {
|
||||
const { history, formConfigs } = this.props;
|
||||
const { pageId, applicationId } = this.props.match.params;
|
||||
|
||||
this.props.initializeForm(datasource);
|
||||
this.props.selectPlugin(datasource.pluginId);
|
||||
if (!formConfigs[datasource.pluginId]) {
|
||||
this.props.fetchFormConfig(datasource.pluginId);
|
||||
}
|
||||
history.push(
|
||||
DATA_SOURCES_EDITOR_ID_URL(applicationId, pageId, datasource.id),
|
||||
);
|
||||
this.props.onDatasourceChange(datasource);
|
||||
};
|
||||
|
||||
handleSearchChange = (e: React.ChangeEvent<{ value: string }>) => {
|
||||
|
|
@ -253,6 +259,7 @@ class DataSourceSidebar extends React.Component<Props, State> {
|
|||
},
|
||||
datastoreRefs,
|
||||
deleteDatasource,
|
||||
drafts,
|
||||
} = this.props;
|
||||
|
||||
return datasources.map(datasource => {
|
||||
|
|
@ -272,6 +279,7 @@ class DataSourceSidebar extends React.Component<Props, State> {
|
|||
/>
|
||||
<ActionName>{datasource.name}</ActionName>
|
||||
</ActionItem>
|
||||
<DraftIconIndicator isHidden={!(datasource.id in drafts)} />
|
||||
<TreeDropdown
|
||||
defaultText=""
|
||||
onSelect={() => {
|
||||
|
|
@ -335,17 +343,21 @@ class DataSourceSidebar extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
const mapStateToProps = (state: AppState): ReduxStateProps => {
|
||||
const { drafts } = state.ui.datasourcePane;
|
||||
return {
|
||||
formConfigs: state.entities.plugins.formConfigs,
|
||||
dataSources: getDataSources(state),
|
||||
plugins: getPlugins(state),
|
||||
datastoreRefs: state.ui.datasourcePane.datasourceRefs,
|
||||
drafts,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({
|
||||
initDatasourcePane: (pluginType: string, urlId?: string) =>
|
||||
dispatch(initDatasourcePane(pluginType, urlId)),
|
||||
onDatasourceChange: (datasource: Datasource) =>
|
||||
dispatch(changeDatasource(datasource)),
|
||||
fetchFormConfig: (id: string) => dispatch(fetchPluginForm({ id })),
|
||||
selectPlugin: (pluginId: string) => dispatch(selectPlugin(pluginId)),
|
||||
initializeForm: (data: Record<string, any>) =>
|
||||
|
|
|
|||
|
|
@ -2,15 +2,18 @@ import React from "react";
|
|||
import { createReducer } from "utils/AppsmithUtils";
|
||||
import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants";
|
||||
import { Datasource } from "api/DatasourcesApi";
|
||||
import _ from "lodash";
|
||||
|
||||
const initialState: DatasourcePaneReduxState = {
|
||||
selectedPlugin: "",
|
||||
datasourceRefs: {},
|
||||
drafts: {},
|
||||
};
|
||||
|
||||
export interface DatasourcePaneReduxState {
|
||||
selectedPlugin: string;
|
||||
datasourceRefs: {};
|
||||
drafts: Record<string, Datasource>;
|
||||
}
|
||||
|
||||
const datasourcePaneReducer = createReducer(initialState, {
|
||||
|
|
@ -42,6 +45,25 @@ const datasourcePaneReducer = createReducer(initialState, {
|
|||
},
|
||||
};
|
||||
},
|
||||
[ReduxActionTypes.UPDATE_DATASOURCE_DRAFT]: (
|
||||
state: DatasourcePaneReduxState,
|
||||
action: ReduxAction<{ id: string; draft: Partial<Datasource> }>,
|
||||
) => {
|
||||
return {
|
||||
...state,
|
||||
drafts: {
|
||||
...state.drafts,
|
||||
[action.payload.id]: action.payload.draft,
|
||||
},
|
||||
};
|
||||
},
|
||||
[ReduxActionTypes.DELETE_DATASOURCE_DRAFT]: (
|
||||
state: DatasourcePaneReduxState,
|
||||
action: ReduxAction<{ id: string }>,
|
||||
) => ({
|
||||
...state,
|
||||
drafts: _.omit(state.drafts, action.payload.id),
|
||||
}),
|
||||
});
|
||||
|
||||
export default datasourcePaneReducer;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,25 @@
|
|||
import { all, put, takeEvery, select } from "redux-saga/effects";
|
||||
import { change, initialize } from "redux-form";
|
||||
import { all, put, takeEvery, select, call } from "redux-saga/effects";
|
||||
import { change, initialize, getFormValues } from "redux-form";
|
||||
import _ from "lodash";
|
||||
import {
|
||||
ReduxAction,
|
||||
ReduxActionErrorTypes,
|
||||
ReduxActionTypes,
|
||||
ReduxFormActionTypes,
|
||||
ReduxActionWithMeta,
|
||||
} from "constants/ReduxActionConstants";
|
||||
import {
|
||||
getCurrentApplicationId,
|
||||
getCurrentPageId,
|
||||
} from "selectors/editorSelectors";
|
||||
import { getDatasourceRefs, getPluginForm } from "selectors/entitiesSelector";
|
||||
import {
|
||||
getDatasourceRefs,
|
||||
getPluginForm,
|
||||
getDatasource,
|
||||
getDatasourceDraft,
|
||||
} from "selectors/entitiesSelector";
|
||||
import { selectPlugin } from "actions/datasourceActions";
|
||||
import { fetchPluginForm } from "actions/pluginActions";
|
||||
import { GenericApiResponse } from "api/ApiResponses";
|
||||
import DatasourcesApi, {
|
||||
CreateDatasourceConfig,
|
||||
|
|
@ -99,6 +108,12 @@ export function* deleteDatasourceSaga(
|
|||
type: ReduxActionTypes.DELETE_DATASOURCE_SUCCESS,
|
||||
payload: response.data,
|
||||
});
|
||||
yield put({
|
||||
type: ReduxActionTypes.DELETE_DATASOURCE_DRAFT,
|
||||
payload: {
|
||||
id: response.data.id,
|
||||
},
|
||||
});
|
||||
history.push(DATA_SOURCES_EDITOR_URL(applicationId, pageId));
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -125,6 +140,12 @@ function* updateDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
|
|||
type: ReduxActionTypes.UPDATE_DATASOURCE_SUCCESS,
|
||||
payload: response.data,
|
||||
});
|
||||
yield put({
|
||||
type: ReduxActionTypes.DELETE_DATASOURCE_DRAFT,
|
||||
payload: {
|
||||
id: response.data.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
yield put({
|
||||
|
|
@ -269,6 +290,60 @@ function* createDatasourceFromFormSaga(
|
|||
}
|
||||
}
|
||||
|
||||
function* updateDraftsSaga() {
|
||||
const values = yield select(getFormValues(DATASOURCE_DB_FORM));
|
||||
|
||||
if (!values.id) return;
|
||||
const datasource = yield select(getDatasource, values.id);
|
||||
|
||||
if (_.isEqual(values, datasource)) {
|
||||
yield put({
|
||||
type: ReduxActionTypes.DELETE_DATASOURCE_DRAFT,
|
||||
payload: { id: values.id },
|
||||
});
|
||||
} else {
|
||||
yield put({
|
||||
type: ReduxActionTypes.UPDATE_DATASOURCE_DRAFT,
|
||||
payload: { id: values.id, draft: values },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function* changeDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
|
||||
const { id, pluginId } = actionPayload.payload;
|
||||
const datasource = actionPayload.payload;
|
||||
const state = yield select();
|
||||
const draft = yield select(getDatasourceDraft, id);
|
||||
const formConfigs = state.entities.plugins.formConfigs;
|
||||
const applicationId = yield select(getCurrentApplicationId);
|
||||
const pageId = yield select(getCurrentPageId);
|
||||
let data;
|
||||
|
||||
if (_.isEmpty(draft)) {
|
||||
data = actionPayload.payload;
|
||||
} else {
|
||||
data = draft;
|
||||
}
|
||||
|
||||
yield put(initialize(DATASOURCE_DB_FORM, data));
|
||||
yield put(selectPlugin(pluginId));
|
||||
|
||||
if (!formConfigs[pluginId]) {
|
||||
yield put(fetchPluginForm({ id: pluginId }));
|
||||
}
|
||||
history.push(
|
||||
DATA_SOURCES_EDITOR_ID_URL(applicationId, pageId, datasource.id),
|
||||
);
|
||||
}
|
||||
|
||||
function* formValueChangeSaga(
|
||||
actionPayload: ReduxActionWithMeta<string, { field: string; form: string }>,
|
||||
) {
|
||||
const { form } = actionPayload.meta;
|
||||
if (form !== DATASOURCE_DB_FORM) return;
|
||||
yield all([call(updateDraftsSaga)]);
|
||||
}
|
||||
|
||||
export function* watchDatasourcesSagas() {
|
||||
yield all([
|
||||
takeEvery(ReduxActionTypes.FETCH_DATASOURCES_INIT, fetchDatasourcesSaga),
|
||||
|
|
@ -280,5 +355,8 @@ export function* watchDatasourcesSagas() {
|
|||
takeEvery(ReduxActionTypes.UPDATE_DATASOURCE_INIT, updateDatasourceSaga),
|
||||
takeEvery(ReduxActionTypes.TEST_DATASOURCE_INIT, testDatasourceSaga),
|
||||
takeEvery(ReduxActionTypes.DELETE_DATASOURCE_INIT, deleteDatasourceSaga),
|
||||
takeEvery(ReduxActionTypes.CHANGE_DATASOURCE, changeDatasourceSaga),
|
||||
// Intercepting the redux-form change actionType
|
||||
takeEvery(ReduxFormActionTypes.VALUE_CHANGE, formValueChangeSaga),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { QUERY_CONSTANT } from "constants/QueryEditorConstants";
|
|||
import { API_CONSTANT } from "constants/ApiEditorConstants";
|
||||
import { createSelector } from "reselect";
|
||||
import { Page } from "constants/ReduxActionConstants";
|
||||
import { Datasource } from "api/DatasourcesApi";
|
||||
|
||||
export const getEntities = (state: AppState): AppState["entities"] =>
|
||||
state.entities;
|
||||
|
|
@ -107,6 +108,20 @@ export const getActions = (state: AppState): ActionDataState =>
|
|||
export const getDatasourceRefs = (state: AppState): any =>
|
||||
state.ui.datasourcePane.datasourceRefs;
|
||||
|
||||
export const getDatasource = (
|
||||
state: AppState,
|
||||
datasourceId: string,
|
||||
): Partial<Datasource> | undefined =>
|
||||
state.entities.datasources.list.find(
|
||||
datasource => datasource.id === datasourceId,
|
||||
);
|
||||
|
||||
export const getDatasourceDraft = (state: AppState, id: string) => {
|
||||
const drafts = state.ui.datasourcePane.drafts;
|
||||
if (id in drafts) return drafts[id];
|
||||
return {};
|
||||
};
|
||||
|
||||
export const getPlugins = (state: AppState) => state.entities.plugins.list;
|
||||
|
||||
export const getApiActions = (state: AppState): ActionDataState => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user