2019-10-18 08:16:26 +00:00
|
|
|
import React from "react";
|
2019-10-21 15:12:45 +00:00
|
|
|
import { connect } from "react-redux";
|
|
|
|
|
import { RouteComponentProps } from "react-router";
|
|
|
|
|
import styled from "styled-components";
|
|
|
|
|
import { AppState } from "../../reducers";
|
|
|
|
|
import { ActionDataState } from "../../reducers/entityReducers/actionsReducer";
|
2019-10-25 05:35:20 +00:00
|
|
|
import { API_EDITOR_ID_URL, API_EDITOR_URL } from "../../constants/routes";
|
2019-11-05 05:09:50 +00:00
|
|
|
import { BaseButton } from "../../components/designSystems/blueprint/ButtonComponent";
|
2019-10-25 05:35:20 +00:00
|
|
|
import { FormIcons } from "../../icons/FormIcons";
|
2019-10-29 12:02:58 +00:00
|
|
|
import { Spinner } from "@blueprintjs/core";
|
2019-11-13 07:34:59 +00:00
|
|
|
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";
|
2019-11-14 09:01:23 +00:00
|
|
|
import Fuse from "fuse.js";
|
2019-10-25 05:35:20 +00:00
|
|
|
|
2019-11-13 11:34:11 +00:00
|
|
|
const LoadingContainer = styled.div`
|
|
|
|
|
height: 50%;
|
|
|
|
|
width: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
`;
|
|
|
|
|
|
2019-10-25 05:35:20 +00:00
|
|
|
const ApiSidebarWrapper = styled.div`
|
2019-11-13 11:34:11 +00:00
|
|
|
margin-top: 10px;
|
2019-10-25 05:35:20 +00:00
|
|
|
height: 100%;
|
2019-10-29 12:02:58 +00:00
|
|
|
width: 100%;
|
2019-10-25 05:35:20 +00:00
|
|
|
flex-direction: column;
|
|
|
|
|
`;
|
2019-11-14 09:01:23 +00:00
|
|
|
|
|
|
|
|
const SearchBar = styled(BaseTextInput)`
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
input {
|
|
|
|
|
background-color: #23292e;
|
|
|
|
|
border: none;
|
|
|
|
|
color: ${props => props.theme.colors.textOnDarkBG}
|
|
|
|
|
:focus {
|
|
|
|
|
background-color: #23292e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.bp3-icon {
|
|
|
|
|
background-color: #23292e;
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
2019-10-25 05:35:20 +00:00
|
|
|
const ApiItemsWrapper = styled.div`
|
|
|
|
|
flex: 1;
|
2019-11-13 11:34:11 +00:00
|
|
|
margin-bottom: 15px;
|
2019-10-25 05:35:20 +00:00
|
|
|
`;
|
2019-10-21 15:12:45 +00:00
|
|
|
|
|
|
|
|
const ApiItem = styled.div<{ isSelected: boolean }>`
|
2019-10-25 05:35:20 +00:00
|
|
|
height: 32px;
|
2019-10-21 15:12:45 +00:00
|
|
|
width: 100%;
|
2019-11-13 11:34:11 +00:00
|
|
|
padding: 8px 12px;
|
2019-10-21 15:12:45 +00:00
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
display: flex;
|
2019-10-25 05:35:20 +00:00
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 2px;
|
2019-10-21 15:12:45 +00:00
|
|
|
background-color: ${props =>
|
|
|
|
|
props.isSelected ? props.theme.colors.paneCard : props.theme.colors.paneBG}
|
|
|
|
|
:hover {
|
|
|
|
|
background-color: ${props => props.theme.colors.paneCard};
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
2019-11-14 09:01:23 +00:00
|
|
|
const HTTPMethod = styled.span<{ method?: string }>`
|
2019-10-21 15:12:45 +00:00
|
|
|
flex: 1;
|
2019-10-25 05:35:20 +00:00
|
|
|
font-size: 12px;
|
2019-10-21 15:12:45 +00:00
|
|
|
color: ${props => {
|
|
|
|
|
switch (props.method) {
|
|
|
|
|
case "GET":
|
|
|
|
|
return "#29CCA3";
|
|
|
|
|
case "POST":
|
|
|
|
|
return "#F7C75B";
|
|
|
|
|
case "PUT":
|
|
|
|
|
return "#30A5E0";
|
|
|
|
|
case "DELETE":
|
|
|
|
|
return "#CE4257";
|
|
|
|
|
default:
|
|
|
|
|
return "#333";
|
|
|
|
|
}
|
|
|
|
|
}};
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
const ActionName = styled.span`
|
|
|
|
|
flex: 3;
|
2019-10-25 05:35:20 +00:00
|
|
|
padding: 0 2px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
const CreateNewButton = styled(BaseButton)`
|
|
|
|
|
&& {
|
|
|
|
|
border: none;
|
|
|
|
|
color: ${props => props.theme.colors.textOnDarkBG};
|
|
|
|
|
height: 32px;
|
2019-11-13 11:34:11 +00:00
|
|
|
text-align: left;
|
|
|
|
|
justify-content: flex-start;
|
2019-10-25 05:35:20 +00:00
|
|
|
&:hover {
|
|
|
|
|
color: ${props => props.theme.colors.paneBG};
|
|
|
|
|
svg {
|
|
|
|
|
path {
|
|
|
|
|
fill: ${props => props.theme.colors.paneBG};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
svg {
|
2019-11-13 11:34:11 +00:00
|
|
|
margin-top: 4px;
|
|
|
|
|
height: 14px;
|
|
|
|
|
width: 14px;
|
2019-10-25 05:35:20 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-10-21 15:12:45 +00:00
|
|
|
`;
|
|
|
|
|
|
2019-11-13 07:34:59 +00:00
|
|
|
const CreateApiWrapper = styled.div`
|
|
|
|
|
display: grid;
|
2019-11-13 11:34:11 +00:00
|
|
|
grid-template-columns: 6fr 1fr;
|
2019-11-13 07:34:59 +00:00
|
|
|
grid-gap: 5px;
|
2019-11-13 11:34:11 +00:00
|
|
|
height: 32px;
|
2019-11-13 07:34:59 +00:00
|
|
|
`;
|
|
|
|
|
|
2019-10-21 15:12:45 +00:00
|
|
|
interface ReduxStateProps {
|
|
|
|
|
actions: ActionDataState;
|
2019-11-13 07:34:59 +00:00
|
|
|
apiPane: ApiPaneReduxState;
|
2019-10-21 15:12:45 +00:00
|
|
|
}
|
|
|
|
|
|
2019-11-13 07:34:59 +00:00
|
|
|
interface ReduxDispatchProps {
|
|
|
|
|
createAction: (name: string) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Props = ReduxStateProps &
|
|
|
|
|
ReduxDispatchProps &
|
|
|
|
|
RouteComponentProps<{ id: string }>;
|
|
|
|
|
type State = {
|
|
|
|
|
isCreating: boolean;
|
|
|
|
|
name: string;
|
2019-11-14 09:01:23 +00:00
|
|
|
search: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const fuseOptions = {
|
|
|
|
|
shouldSort: true,
|
|
|
|
|
threshold: 0.1,
|
|
|
|
|
location: 0,
|
|
|
|
|
minMatchCharLength: 3,
|
|
|
|
|
keys: ["name"],
|
2019-11-13 07:34:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class ApiSidebar extends React.Component<Props, State> {
|
|
|
|
|
constructor(props: Props) {
|
|
|
|
|
super(props);
|
|
|
|
|
this.state = {
|
|
|
|
|
isCreating: false,
|
|
|
|
|
name: "",
|
2019-11-14 09:01:23 +00:00
|
|
|
search: "",
|
2019-11-13 07:34:59 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
componentDidUpdate(prevProps: Readonly<Props>): void {
|
|
|
|
|
if (!prevProps.match.params.id && this.props.match.params.id) {
|
|
|
|
|
this.setState({
|
|
|
|
|
isCreating: false,
|
|
|
|
|
name: "",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-21 15:12:45 +00:00
|
|
|
|
2019-10-25 05:35:20 +00:00
|
|
|
handleCreateNew = () => {
|
|
|
|
|
const { history } = this.props;
|
|
|
|
|
history.push(API_EDITOR_URL);
|
2019-11-13 07:34:59 +00:00
|
|
|
this.setState({
|
|
|
|
|
isCreating: true,
|
|
|
|
|
name: "",
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
saveAction = () => {
|
|
|
|
|
if (this.state.name) {
|
|
|
|
|
this.props.createAction(this.state.name);
|
|
|
|
|
} else {
|
|
|
|
|
this.setState({
|
|
|
|
|
isCreating: false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
handleNameChange = (e: React.ChangeEvent<{ value: string }>) => {
|
|
|
|
|
const value = e.target.value;
|
|
|
|
|
this.setState({
|
|
|
|
|
name: value,
|
|
|
|
|
});
|
2019-10-25 05:35:20 +00:00
|
|
|
};
|
|
|
|
|
|
2019-11-14 09:01:23 +00:00
|
|
|
handleSearchChange = (e: React.ChangeEvent<{ value: string }>) => {
|
|
|
|
|
const value = e.target.value;
|
|
|
|
|
this.setState({
|
|
|
|
|
search: value,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-18 08:16:26 +00:00
|
|
|
render() {
|
2019-11-14 09:01:23 +00:00
|
|
|
const {
|
|
|
|
|
apiPane,
|
|
|
|
|
history,
|
|
|
|
|
match,
|
|
|
|
|
actions: { data },
|
|
|
|
|
} = this.props;
|
|
|
|
|
const { isCreating, search, name } = this.state;
|
2019-10-21 15:12:45 +00:00
|
|
|
const activeActionId = match.params.id;
|
2019-11-14 09:01:23 +00:00
|
|
|
const fuse = new Fuse(data, fuseOptions);
|
|
|
|
|
const actions = search ? fuse.search(search) : data;
|
2019-10-21 15:12:45 +00:00
|
|
|
return (
|
2019-11-13 11:34:11 +00:00
|
|
|
<React.Fragment>
|
|
|
|
|
{apiPane.isFetching ? (
|
|
|
|
|
<LoadingContainer>
|
|
|
|
|
<Spinner size={30} />
|
|
|
|
|
</LoadingContainer>
|
2019-11-13 07:34:59 +00:00
|
|
|
) : (
|
2019-11-13 11:34:11 +00:00
|
|
|
<ApiSidebarWrapper>
|
|
|
|
|
<ApiItemsWrapper>
|
2019-11-14 09:01:23 +00:00
|
|
|
<SearchBar
|
|
|
|
|
icon="search"
|
|
|
|
|
input={{
|
|
|
|
|
value: search,
|
|
|
|
|
onChange: this.handleSearchChange,
|
|
|
|
|
}}
|
|
|
|
|
placeholderMessage="Search"
|
|
|
|
|
/>
|
|
|
|
|
{actions.map(action => (
|
2019-11-13 11:34:11 +00:00
|
|
|
<ApiItem
|
|
|
|
|
key={action.id}
|
|
|
|
|
onClick={() => history.push(API_EDITOR_ID_URL(action.id))}
|
|
|
|
|
isSelected={activeActionId === action.id}
|
|
|
|
|
>
|
2019-11-14 09:01:23 +00:00
|
|
|
{action.actionConfiguration ? (
|
|
|
|
|
<HTTPMethod method={action.actionConfiguration.httpMethod}>
|
|
|
|
|
{action.actionConfiguration.httpMethod}
|
|
|
|
|
</HTTPMethod>
|
|
|
|
|
) : (
|
|
|
|
|
<HTTPMethod />
|
|
|
|
|
)}
|
2019-11-13 11:34:11 +00:00
|
|
|
<ActionName>{action.name}</ActionName>
|
|
|
|
|
</ApiItem>
|
|
|
|
|
))}
|
|
|
|
|
</ApiItemsWrapper>
|
|
|
|
|
{isCreating ? (
|
|
|
|
|
<CreateApiWrapper>
|
|
|
|
|
<BaseTextInput
|
|
|
|
|
placeholderMessage="API name"
|
|
|
|
|
input={{
|
2019-11-14 09:01:23 +00:00
|
|
|
value: name,
|
2019-11-13 11:34:11 +00:00
|
|
|
onChange: this.handleNameChange,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<BaseButton
|
|
|
|
|
icon={TICK}
|
|
|
|
|
styleName="primary"
|
|
|
|
|
text=""
|
|
|
|
|
onClick={this.saveAction}
|
|
|
|
|
filled
|
2019-11-14 09:01:23 +00:00
|
|
|
loading={apiPane.isSaving}
|
2019-11-13 11:34:11 +00:00
|
|
|
/>
|
|
|
|
|
</CreateApiWrapper>
|
|
|
|
|
) : (
|
|
|
|
|
<React.Fragment>
|
|
|
|
|
{!apiPane.isFetching && (
|
|
|
|
|
<CreateNewButton
|
|
|
|
|
text="Create new API"
|
|
|
|
|
icon={FormIcons.ADD_NEW_ICON()}
|
|
|
|
|
onClick={this.handleCreateNew}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</React.Fragment>
|
|
|
|
|
)}
|
|
|
|
|
</ApiSidebarWrapper>
|
2019-11-13 07:34:59 +00:00
|
|
|
)}
|
2019-11-13 11:34:11 +00:00
|
|
|
</React.Fragment>
|
2019-10-21 15:12:45 +00:00
|
|
|
);
|
2019-10-18 08:16:26 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-21 15:12:45 +00:00
|
|
|
const mapStateToProps = (state: AppState): ReduxStateProps => ({
|
|
|
|
|
actions: state.entities.actions,
|
2019-11-13 07:34:59 +00:00
|
|
|
apiPane: state.ui.apiPane,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({
|
2019-11-14 13:35:39 +00:00
|
|
|
createAction: (name: string) => dispatch(createActionRequest({ name })),
|
2019-10-21 15:12:45 +00:00
|
|
|
});
|
|
|
|
|
|
2019-11-13 07:34:59 +00:00
|
|
|
export default connect(
|
|
|
|
|
mapStateToProps,
|
|
|
|
|
mapDispatchToProps,
|
|
|
|
|
)(ApiSidebar);
|