-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SQL Lab - A multi-tab SQL editor #514
Changes from all commits
46be9cf
3c2edd7
ce8a972
171f8a0
861a0f6
9acced4
2b01772
38d8a4d
852e4a1
c2cb78c
b7a6010
b3e81f9
608e68d
3e49bea
88e34a7
58213c2
747b747
14d59ec
02ee3b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,8 @@ dist | |
caravel.egg-info/ | ||
app.db | ||
*.bak | ||
.idea | ||
*.sqllite | ||
|
||
# Node.js, webpack artifacts | ||
*.entry.js | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
node_modules/* | ||
vendor/* | ||
javascripts/dist/* | ||
dist/* | ||
stylesheets/* | ||
spec/* |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
|
||
# TODO | ||
* Figure out how to organize the left panel, integrate Search | ||
* collapse sql beyond 10 lines | ||
* Security per-database (dropdown) | ||
* Get a to work | ||
|
||
## Cosmetic | ||
* Result set font is too big | ||
* lmiit/timer/buttons wrap | ||
* table label is transparent | ||
* SqlEditor buttons | ||
* use react-bootstrap-prompt for query title input | ||
* Make tabs look great | ||
|
||
# PROJECT | ||
* Write Runbook | ||
* Confirm backups | ||
* merge chef branch |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
export const RESET_STATE = 'RESET_STATE'; | ||
export const ADD_QUERY_EDITOR = 'ADD_QUERY_EDITOR'; | ||
export const REMOVE_QUERY_EDITOR = 'REMOVE_QUERY_EDITOR'; | ||
export const ADD_TABLE = 'ADD_TABLE'; | ||
export const REMOVE_TABLE = 'REMOVE_TABLE'; | ||
export const START_QUERY = 'START_QUERY'; | ||
export const STOP_QUERY = 'STOP_QUERY'; | ||
export const END_QUERY = 'END_QUERY'; | ||
export const REMOVE_QUERY = 'REMOVE_QUERY'; | ||
export const EXPAND_TABLE = 'EXPAND_TABLE'; | ||
export const COLLAPSE_TABLE = 'COLLAPSE_TABLE'; | ||
export const QUERY_SUCCESS = 'QUERY_SUCCESS'; | ||
export const QUERY_FAILED = 'QUERY_FAILED'; | ||
export const QUERY_EDITOR_SETDB = 'QUERY_EDITOR_SETDB'; | ||
export const QUERY_EDITOR_SET_SCHEMA = 'QUERY_EDITOR_SET_SCHEMA'; | ||
export const QUERY_EDITOR_SET_TITLE = 'QUERY_EDITOR_SET_TITLE'; | ||
export const QUERY_EDITOR_SET_AUTORUN = 'QUERY_EDITOR_SET_AUTORUN'; | ||
export const QUERY_EDITOR_SET_SQL = 'QUERY_EDITOR_SET_SQL'; | ||
export const SET_WORKSPACE_DB = 'SET_WORKSPACE_DB'; | ||
export const ADD_WORKSPACE_QUERY = 'ADD_WORKSPACE_QUERY'; | ||
export const REMOVE_WORKSPACE_QUERY = 'REMOVE_WORKSPACE_QUERY'; | ||
export const SET_ACTIVE_QUERY_EDITOR = 'SET_ACTIVE_QUERY_EDITOR'; | ||
export const ADD_ALERT = 'ADD_ALERT'; | ||
export const REMOVE_ALERT = 'REMOVE_ALERT'; | ||
export const REFRESH_QUERIES = 'REFRESH_QUERIES'; | ||
|
||
export function resetState() { | ||
return { type: RESET_STATE }; | ||
} | ||
|
||
export function addQueryEditor(queryEditor) { | ||
return { type: ADD_QUERY_EDITOR, queryEditor }; | ||
} | ||
|
||
export function addAlert(alert) { | ||
return { type: ADD_ALERT, alert }; | ||
} | ||
|
||
export function removeAlert(alert) { | ||
return { type: REMOVE_ALERT, alert }; | ||
} | ||
|
||
export function setActiveQueryEditor(queryEditor) { | ||
return { type: SET_ACTIVE_QUERY_EDITOR, queryEditor }; | ||
} | ||
|
||
export function removeQueryEditor(queryEditor) { | ||
return { type: REMOVE_QUERY_EDITOR, queryEditor }; | ||
} | ||
|
||
export function removeQuery(query) { | ||
return { type: REMOVE_QUERY, query }; | ||
} | ||
|
||
export function queryEditorSetDb(queryEditor, dbId) { | ||
return { type: QUERY_EDITOR_SETDB, queryEditor, dbId }; | ||
} | ||
|
||
export function queryEditorSetSchema(queryEditor, schema) { | ||
return { type: QUERY_EDITOR_SET_SCHEMA, queryEditor, schema }; | ||
} | ||
|
||
export function queryEditorSetAutorun(queryEditor, autorun) { | ||
return { type: QUERY_EDITOR_SET_AUTORUN, queryEditor, autorun }; | ||
} | ||
|
||
export function queryEditorSetTitle(queryEditor, title) { | ||
return { type: QUERY_EDITOR_SET_TITLE, queryEditor, title }; | ||
} | ||
|
||
export function queryEditorSetSql(queryEditor, sql) { | ||
return { type: QUERY_EDITOR_SET_SQL, queryEditor, sql }; | ||
} | ||
|
||
export function addTable(table) { | ||
return { type: ADD_TABLE, table }; | ||
} | ||
|
||
export function expandTable(table) { | ||
return { type: EXPAND_TABLE, table }; | ||
} | ||
|
||
export function collapseTable(table) { | ||
return { type: COLLAPSE_TABLE, table }; | ||
} | ||
|
||
export function removeTable(table) { | ||
return { type: REMOVE_TABLE, table }; | ||
} | ||
|
||
export function startQuery(query) { | ||
return { type: START_QUERY, query }; | ||
} | ||
|
||
export function stopQuery(query) { | ||
return { type: STOP_QUERY, query }; | ||
} | ||
|
||
export function querySuccess(query, results) { | ||
return { type: QUERY_SUCCESS, query, results }; | ||
} | ||
|
||
export function queryFailed(query, msg) { | ||
return { type: QUERY_FAILED, query, msg }; | ||
} | ||
|
||
export function addWorkspaceQuery(query) { | ||
return { type: ADD_WORKSPACE_QUERY, query }; | ||
} | ||
|
||
export function removeWorkspaceQuery(query) { | ||
return { type: REMOVE_WORKSPACE_QUERY, query }; | ||
} | ||
export function refreshQueries(alteredQueries) { | ||
return { type: REFRESH_QUERIES, alteredQueries }; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export const STATE_BSSTYLE_MAP = { | ||
failed: 'danger', | ||
pending: 'info', | ||
running: 'warning', | ||
success: 'success', | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import React from 'react'; | ||
import { Alert } from 'react-bootstrap'; | ||
import { connect } from 'react-redux'; | ||
import { bindActionCreators } from 'redux'; | ||
import * as Actions from '../actions'; | ||
|
||
class Alerts extends React.Component { | ||
removeAlert(alert) { | ||
this.props.actions.removeAlert(alert); | ||
} | ||
render() { | ||
const alerts = this.props.alerts.map((alert) => | ||
<Alert | ||
bsStyle={alert.bsStyle} | ||
style={{ width: '500px', textAlign: 'midddle', margin: '10px auto' }} | ||
> | ||
{alert.msg} | ||
<i | ||
className="fa fa-close pull-right" | ||
onClick={this.removeAlert.bind(this, alert)} | ||
style={{ cursor: 'pointer' }} | ||
/> | ||
</Alert> | ||
); | ||
return ( | ||
<div>{alerts}</div> | ||
); | ||
} | ||
} | ||
|
||
Alerts.propTypes = { | ||
alerts: React.PropTypes.array, | ||
actions: React.PropTypes.object, | ||
}; | ||
|
||
function mapDispatchToProps(dispatch) { | ||
return { | ||
actions: bindActionCreators(Actions, dispatch), | ||
}; | ||
} | ||
export default connect(null, mapDispatchToProps)(Alerts); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import React from 'react'; | ||
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'; | ||
|
||
const ButtonWithTooltip = (props) => { | ||
let tooltip = ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this can be |
||
<Tooltip id="tooltip"> | ||
{props.tooltip} | ||
</Tooltip> | ||
); | ||
return ( | ||
<OverlayTrigger | ||
overlay={tooltip} | ||
delayShow={300} | ||
placement={props.placement} | ||
delayHide={150} | ||
> | ||
<Button | ||
onClick={props.onClick} | ||
bsStyle={props.bsStyle} | ||
bsSize={props.bsSize} | ||
disabled={props.disabled} | ||
className={props.className} | ||
> | ||
{props.children} | ||
</Button> | ||
</OverlayTrigger> | ||
); | ||
}; | ||
|
||
ButtonWithTooltip.defaultProps = { | ||
onClick: () => {}, | ||
disabled: false, | ||
placement: 'top', | ||
bsStyle: 'default', | ||
}; | ||
|
||
ButtonWithTooltip.propTypes = { | ||
bsSize: React.PropTypes.string, | ||
bsStyle: React.PropTypes.string, | ||
children: React.PropTypes.element, | ||
className: React.PropTypes.string, | ||
disabled: React.PropTypes.bool, | ||
onClick: React.PropTypes.func, | ||
placement: React.PropTypes.string, | ||
tooltip: React.PropTypes.string, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should make the props that do not have defaults required. tooltip: React.PropTypes.string.isRequired, |
||
}; | ||
|
||
export default ButtonWithTooltip; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import React from 'react'; | ||
import { Alert, Button } from 'react-bootstrap'; | ||
import { connect } from 'react-redux'; | ||
import { bindActionCreators } from 'redux'; | ||
import * as Actions from '../actions'; | ||
import QueryLink from './QueryLink'; | ||
|
||
import 'react-select/dist/react-select.css'; | ||
|
||
const LeftPane = (props) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prefer named functions/function declarations https://github.com/airbnb/javascript#functions--declarations |
||
let queryElements; | ||
if (props.workspaceQueries.length > 0) { | ||
queryElements = props.workspaceQueries.map((q) => <QueryLink query={q} />); | ||
} else { | ||
queryElements = ( | ||
<Alert bsStyle="info"> | ||
Use the save button on the SQL editor to save a query | ||
into this section for future reference. | ||
</Alert> | ||
); | ||
} | ||
return ( | ||
<div> | ||
<div className="panel panel-default LeftPane"> | ||
<div className="panel-heading"> | ||
<div className="panel-title"> | ||
Saved Queries | ||
</div> | ||
</div> | ||
<div className="panel-body"> | ||
{queryElements} | ||
</div> | ||
</div> | ||
<br /><br /> | ||
<Button onClick={props.actions.resetState.bind(this)} bsStyle="danger"> | ||
Reset State | ||
</Button> | ||
</div> | ||
); | ||
}; | ||
|
||
LeftPane.propTypes = { | ||
workspaceQueries: React.PropTypes.array, | ||
actions: React.PropTypes.object, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be required: |
||
}; | ||
|
||
LeftPane.defaultProps = { | ||
workspaceQueries: [], | ||
}; | ||
|
||
function mapStateToProps(state) { | ||
return { | ||
workspaceQueries: state.workspaceQueries, | ||
}; | ||
} | ||
function mapDispatchToProps(dispatch) { | ||
return { | ||
actions: bindActionCreators(Actions, dispatch), | ||
}; | ||
} | ||
|
||
export default connect(mapStateToProps, mapDispatchToProps)(LeftPane); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import React from 'react'; | ||
import { OverlayTrigger, Tooltip } from 'react-bootstrap'; | ||
|
||
|
||
class Link extends React.Component { | ||
render() { | ||
let tooltip = ( | ||
<Tooltip id="tooltip"> | ||
{this.props.tooltip} | ||
</Tooltip> | ||
); | ||
const link = ( | ||
<a | ||
href={this.props.href} | ||
onClick={this.props.onClick} | ||
style={this.props.style} | ||
className={'Link ' + this.props.className} | ||
> | ||
{this.props.children} | ||
</a> | ||
); | ||
if (this.props.tooltip) { | ||
return ( | ||
<OverlayTrigger | ||
overlay={tooltip} | ||
placement={this.props.placement} | ||
delayShow={300} | ||
delayHide={150} | ||
> | ||
{link} | ||
</OverlayTrigger> | ||
); | ||
} | ||
return link; | ||
} | ||
} | ||
Link.propTypes = { | ||
children: React.PropTypes.object, | ||
className: React.PropTypes.string, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. required? |
||
href: React.PropTypes.string, | ||
onClick: React.PropTypes.func, | ||
placement: React.PropTypes.string, | ||
style: React.PropTypes.object, | ||
tooltip: React.PropTypes.string, | ||
}; | ||
Link.defaultProps = { | ||
disabled: false, | ||
href: '#', | ||
tooltip: null, | ||
placement: 'top', | ||
onClick: () => {}, | ||
}; | ||
|
||
export default Link; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer a named function here: