mirror of
https://github.com/getredash/redash.git
synced 2025-12-19 17:37:19 -05:00
* Introduce Link component * Use Link component for external links as well * Remove unused file (I hope it's really not needed) * Use Link component in visualizations library * Simplify Link component implementation * CR1 * Trigger build * CR2
199 lines
6.6 KiB
JavaScript
199 lines
6.6 KiB
JavaScript
import React from "react";
|
|
import PropTypes from "prop-types";
|
|
import { isEmpty, toUpper, includes, get } from "lodash";
|
|
import Button from "antd/lib/button";
|
|
import List from "antd/lib/list";
|
|
import Modal from "antd/lib/modal";
|
|
import Input from "antd/lib/input";
|
|
import Steps from "antd/lib/steps";
|
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
|
import Link from "@/components/Link";
|
|
import { PreviewCard } from "@/components/PreviewCard";
|
|
import EmptyState from "@/components/items-list/components/EmptyState";
|
|
import DynamicForm from "@/components/dynamic-form/DynamicForm";
|
|
import helper from "@/components/dynamic-form/dynamicFormHelper";
|
|
import HelpTrigger, { TYPES as HELP_TRIGGER_TYPES } from "@/components/HelpTrigger";
|
|
|
|
const { Step } = Steps;
|
|
const { Search } = Input;
|
|
|
|
const StepEnum = {
|
|
SELECT_TYPE: 0,
|
|
CONFIGURE_IT: 1,
|
|
DONE: 2,
|
|
};
|
|
|
|
class CreateSourceDialog extends React.Component {
|
|
static propTypes = {
|
|
dialog: DialogPropType.isRequired,
|
|
types: PropTypes.arrayOf(PropTypes.object),
|
|
sourceType: PropTypes.string.isRequired,
|
|
imageFolder: PropTypes.string.isRequired,
|
|
helpTriggerPrefix: PropTypes.string,
|
|
onCreate: PropTypes.func.isRequired,
|
|
};
|
|
|
|
static defaultProps = {
|
|
types: [],
|
|
helpTriggerPrefix: null,
|
|
};
|
|
|
|
state = {
|
|
searchText: "",
|
|
selectedType: null,
|
|
savingSource: false,
|
|
currentStep: StepEnum.SELECT_TYPE,
|
|
};
|
|
|
|
selectType = selectedType => {
|
|
this.setState({ selectedType, currentStep: StepEnum.CONFIGURE_IT });
|
|
};
|
|
|
|
resetType = () => {
|
|
if (this.state.currentStep === StepEnum.CONFIGURE_IT) {
|
|
this.setState({ searchText: "", selectedType: null, currentStep: StepEnum.SELECT_TYPE });
|
|
}
|
|
};
|
|
|
|
createSource = (values, successCallback, errorCallback) => {
|
|
const { selectedType, savingSource } = this.state;
|
|
if (!savingSource) {
|
|
this.setState({ savingSource: true, currentStep: StepEnum.DONE });
|
|
this.props
|
|
.onCreate(selectedType, values)
|
|
.then(data => {
|
|
successCallback("Saved.");
|
|
this.props.dialog.close({ success: true, data });
|
|
})
|
|
.catch(error => {
|
|
this.setState({ savingSource: false, currentStep: StepEnum.CONFIGURE_IT });
|
|
errorCallback(get(error, "response.data.message", "Failed saving."));
|
|
});
|
|
}
|
|
};
|
|
|
|
renderTypeSelector() {
|
|
const { types } = this.props;
|
|
const { searchText } = this.state;
|
|
const filteredTypes = types.filter(
|
|
type => isEmpty(searchText) || includes(type.name.toLowerCase(), searchText.toLowerCase())
|
|
);
|
|
return (
|
|
<div className="m-t-10">
|
|
<Search
|
|
placeholder="Search..."
|
|
onChange={e => this.setState({ searchText: e.target.value })}
|
|
autoFocus
|
|
data-test="SearchSource"
|
|
/>
|
|
<div className="scrollbox p-5 m-t-10" style={{ minHeight: "30vh", maxHeight: "40vh" }}>
|
|
{isEmpty(filteredTypes) ? (
|
|
<EmptyState className="" />
|
|
) : (
|
|
<List size="small" dataSource={filteredTypes} renderItem={item => this.renderItem(item)} />
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
renderForm() {
|
|
const { imageFolder, helpTriggerPrefix } = this.props;
|
|
const { selectedType } = this.state;
|
|
const fields = helper.getFields(selectedType);
|
|
const helpTriggerType = `${helpTriggerPrefix}${toUpper(selectedType.type)}`;
|
|
return (
|
|
<div>
|
|
<div className="d-flex justify-content-center align-items-center">
|
|
<img className="p-5" src={`${imageFolder}/${selectedType.type}.png`} alt={selectedType.name} width="48" />
|
|
<h4 className="m-0">{selectedType.name}</h4>
|
|
</div>
|
|
<div className="text-right">
|
|
{HELP_TRIGGER_TYPES[helpTriggerType] && (
|
|
<HelpTrigger className="f-13" type={helpTriggerType}>
|
|
Setup Instructions <i className="fa fa-question-circle" />
|
|
</HelpTrigger>
|
|
)}
|
|
</div>
|
|
<DynamicForm id="sourceForm" fields={fields} onSubmit={this.createSource} feedbackIcons hideSubmitButton />
|
|
{selectedType.type === "databricks" && (
|
|
<small>
|
|
By using the Databricks Data Source you agree to the Databricks JDBC/ODBC{" "}
|
|
<Link href="https://databricks.com/spark/odbc-driver-download" target="_blank" rel="noopener noreferrer">
|
|
Driver Download Terms and Conditions
|
|
</Link>
|
|
.
|
|
</small>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
renderItem(item) {
|
|
const { imageFolder } = this.props;
|
|
return (
|
|
<List.Item className="p-l-10 p-r-10 clickable" onClick={() => this.selectType(item)}>
|
|
<PreviewCard
|
|
title={item.name}
|
|
imageUrl={`${imageFolder}/${item.type}.png`}
|
|
roundedImage={false}
|
|
data-test="PreviewItem"
|
|
data-test-type={item.type}>
|
|
<i className="fa fa-angle-double-right" />
|
|
</PreviewCard>
|
|
</List.Item>
|
|
);
|
|
}
|
|
|
|
render() {
|
|
const { currentStep, savingSource } = this.state;
|
|
const { dialog, sourceType } = this.props;
|
|
return (
|
|
<Modal
|
|
{...dialog.props}
|
|
title={`Create a New ${sourceType}`}
|
|
footer={
|
|
currentStep === StepEnum.SELECT_TYPE
|
|
? [
|
|
<Button key="cancel" onClick={() => dialog.dismiss()} data-test="CreateSourceCancelButton">
|
|
Cancel
|
|
</Button>,
|
|
<Button key="submit" type="primary" disabled>
|
|
Create
|
|
</Button>,
|
|
]
|
|
: [
|
|
<Button key="previous" onClick={this.resetType}>
|
|
Previous
|
|
</Button>,
|
|
<Button
|
|
key="submit"
|
|
htmlType="submit"
|
|
form="sourceForm"
|
|
type="primary"
|
|
loading={savingSource}
|
|
data-test="CreateSourceSaveButton">
|
|
Create
|
|
</Button>,
|
|
]
|
|
}>
|
|
<div data-test="CreateSourceDialog">
|
|
<Steps className="hidden-xs m-b-10" size="small" current={currentStep} progressDot>
|
|
{currentStep === StepEnum.CONFIGURE_IT ? (
|
|
<Step title={<a>Type Selection</a>} className="clickable" onClick={this.resetType} />
|
|
) : (
|
|
<Step title="Type Selection" />
|
|
)}
|
|
<Step title="Configuration" />
|
|
<Step title="Done" />
|
|
</Steps>
|
|
{currentStep === StepEnum.SELECT_TYPE && this.renderTypeSelector()}
|
|
{currentStep !== StepEnum.SELECT_TYPE && this.renderForm()}
|
|
</div>
|
|
</Modal>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default wrapDialog(CreateSourceDialog);
|