Full create for single and windowed annotations

pull/2829/head
Luke Morris 2018-02-16 21:37:31 -08:00
parent 03d7f7457d
commit 7a77a0e653
6 changed files with 81 additions and 23 deletions

View File

@ -32,7 +32,7 @@ type annotationResponse struct {
func newAnnotationResponse(src chronograf.Source, a *chronograf.Annotation) annotationResponse {
base := "/chronograf/v1/sources"
return annotationResponse{
res := annotationResponse{
ID: a.ID,
StartTime: a.StartTime.UTC().Format(timeMilliFormat),
EndTime: a.EndTime.UTC().Format(timeMilliFormat),
@ -42,6 +42,12 @@ func newAnnotationResponse(src chronograf.Source, a *chronograf.Annotation) anno
Self: fmt.Sprintf("%s/%d/annotations/%s", base, src.ID, a.ID),
},
}
if a.EndTime.IsZero() {
res.EndTime = ""
}
return res
}
type annotationsResponse struct {
@ -204,9 +210,11 @@ func (ar *newAnnotationRequest) UnmarshalJSON(data []byte) error {
return err
}
ar.EndTime, err = time.Parse(timeMilliFormat, aux.EndTime)
if err != nil {
return err
if aux.EndTime != "" {
ar.EndTime, err = time.Parse(timeMilliFormat, aux.EndTime)
if err != nil {
return err
}
}
return nil

View File

@ -1,3 +1,5 @@
import * as api from 'shared/apis/annotation'
export const editingAnnotation = () => ({
type: 'EDITING_ANNOTATION',
})
@ -53,3 +55,10 @@ export const addAnnotation = annotation => ({
annotation,
},
})
export const addAnnotationAsync = (createUrl, annotation) => async dispatch => {
dispatch(addAnnotation(annotation))
const savedAnnotation = await api.createAnnotation(createUrl, annotation)
dispatch(addAnnotation(savedAnnotation))
dispatch(deleteAnnotation(annotation))
}

View File

@ -18,14 +18,13 @@ export const getAnnotations = (graph, annotations = []) => {
const [xStart, xEnd] = graph.xAxisRange()
return annotations.reduce((acc, a) => {
// Don't render if annotation.time is outside the graph
const time = +a.time
const duration = +a.duration
const endTime = time + duration
const time = +a.startTime
const endTime = +a.endTime
const endAnnotation = {
...a,
id: `${a.id}-end`,
time: `${endTime}`,
duration: '',
startTime: `${endTime}`,
endTime: '',
}
if (time < xStart) {
@ -41,7 +40,7 @@ export const getAnnotations = (graph, annotations = []) => {
}
// If annotation does not have duration, include in array
if (!duration) {
if (!endTime) {
return [...acc, a]
}

View File

@ -0,0 +1,20 @@
import AJAX from 'src/utils/ajax'
const msToRFC = ms => ms && new Date(parseInt(ms, 10)).toISOString()
const rfcToMS = rfc3339 => rfc3339 && JSON.stringify(Date.parse(rfc3339))
const annoToMillisecond = anno => ({
...anno,
startTime: rfcToMS(anno.startTime),
endTime: rfcToMS(anno.endTime),
})
const annoToRFC = anno => ({
...anno,
startTime: msToRFC(anno.startTime),
endTime: msToRFC(anno.endTime),
})
export const createAnnotation = async (url, newAnno) => {
const data = annoToRFC(newAnno)
const response = await AJAX({method: 'POST', url, data})
return annoToMillisecond(response.data)
}

View File

@ -1,9 +1,12 @@
import React, {Component, PropTypes} from 'react'
import classnames from 'classnames'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import uuid from 'node-uuid'
import OnClickOutside from 'shared/components/OnClickOutside'
import * as schema from 'shared/schemas'
import * as actions from 'shared/actions/annotations'
import {
circleFlagStyle,
@ -30,23 +33,27 @@ class NewAnnotation extends Component {
handleMouseUp = () => {
const {
onAddAnnotation,
addAnnotation,
onAddingAnnotationSuccess,
tempAnnotation,
onMouseLeaveTempAnnotation,
dygraph,
} = this.props
const createUrl = this.context.source.links.annotations
// time on mouse down
const startTime = dygraph.toDataXCoord(this.state.trueGraphX)
const startTime = `${dygraph.toDataXCoord(this.state.trueGraphX)}`
if (this.state.mouseAction === 'dragging') {
// time on mouse up
const endTime = Number(tempAnnotation.startTime)
const endTime = tempAnnotation.startTime
onAddAnnotation({
addAnnotation(createUrl, {
...tempAnnotation,
startTime: `${startTime}`,
endTime: `${endTime}`,
startTime,
endTime,
text: 'hi',
type: 'hi',
})
onAddingAnnotationSuccess()
@ -57,14 +64,18 @@ class NewAnnotation extends Component {
})
}
onAddingAnnotationSuccess()
onMouseLeaveTempAnnotation()
onAddAnnotation({
addAnnotation(createUrl, {
...tempAnnotation,
id: uuid.v4(),
startTime: `${startTime}`,
startTime,
endTime: '',
text: 'hi',
type: 'hi',
})
onAddingAnnotationSuccess()
return this.setState({
isMouseOver: false,
mouseAction: null,
@ -171,13 +182,21 @@ class NewAnnotation extends Component {
}
}
const {bool, func, shape} = PropTypes
const {bool, func, shape, string} = PropTypes
NewAnnotation.contextTypes = {
source: shape({
links: shape({
annotations: string,
}),
}),
}
NewAnnotation.propTypes = {
dygraph: shape({}).isRequired,
isTempHovering: bool,
tempAnnotation: schema.annotation.isRequired,
onAddAnnotation: func.isRequired,
addAnnotation: func.isRequired,
onDismissAddingAnnotation: func.isRequired,
onAddingAnnotationSuccess: func.isRequired,
onUpdateAnnotation: func.isRequired,
@ -185,4 +204,8 @@ NewAnnotation.propTypes = {
onMouseLeaveTempAnnotation: func.isRequired,
}
export default OnClickOutside(NewAnnotation)
const mdtp = dispatch => ({
addAnnotation: bindActionCreators(actions.addAnnotationAsync, dispatch),
})
export default connect(null, mdtp)(OnClickOutside(NewAnnotation))

View File

@ -6,7 +6,6 @@ export const annotation = shape({
id: string.isRequired,
startTime: string.isRequired,
endTime: string.isRequired,
name: string.isRequired,
text: string.isRequired,
type: string.isRequired,
})