|
| 1 | +// Libraries |
| 2 | +import React, {FC, ChangeEvent, useState} from 'react' |
| 3 | +import {useSelector} from 'react-redux' |
| 4 | +import {useHistory} from 'react-router-dom' |
| 5 | +import {nanoid} from 'nanoid' |
| 6 | + |
| 7 | +// Selectors |
| 8 | +import {getActiveTimeMachine, getSaveableView} from 'src/timeMachine/selectors' |
| 9 | +import {getOrg} from 'src/organizations/selectors' |
| 10 | +import {postNotebook} from 'src/client/notebooksRoutes' |
| 11 | + |
| 12 | +// Components |
| 13 | +import {Form, Input, Button, Grid} from '@influxdata/clockface' |
| 14 | +import {AUTOREFRESH_DEFAULT} from 'src/shared/constants' |
| 15 | + |
| 16 | +// Types |
| 17 | +import { |
| 18 | + Columns, |
| 19 | + InputType, |
| 20 | + ButtonType, |
| 21 | + ComponentColor, |
| 22 | + ComponentStatus, |
| 23 | +} from '@influxdata/clockface' |
| 24 | + |
| 25 | +// Utils |
| 26 | +import {event, normalizeEventName} from 'src/cloud/utils/reporting' |
| 27 | +import {chartTypeName} from 'src/visualization/utils/chartTypeName' |
| 28 | +import { |
| 29 | + PROJECT_NAME, |
| 30 | + DEFAULT_PROJECT_NAME, |
| 31 | + PROJECT_NAME_PLURAL, |
| 32 | +} from 'src/flows' |
| 33 | +import {hydrate, serialize} from 'src/flows/context/flow.list' |
| 34 | +import {getBucketByName} from 'src/buckets/selectors' |
| 35 | +import {AppState} from 'src/types' |
| 36 | + |
| 37 | +interface Props { |
| 38 | + dismiss: () => void |
| 39 | +} |
| 40 | + |
| 41 | +const SaveAsNotebookForm: FC<Props> = ({dismiss}) => { |
| 42 | + const [notebookName, setNotebookName] = useState('') |
| 43 | + const orgID = useSelector(getOrg).id |
| 44 | + const {draftQueries, autoRefresh, timeRange} = useSelector( |
| 45 | + getActiveTimeMachine |
| 46 | + ) |
| 47 | + const {properties} = useSelector(getSaveableView) |
| 48 | + let allUsedBuckets: string[] = [] |
| 49 | + |
| 50 | + draftQueries.forEach(draftQuery => { |
| 51 | + allUsedBuckets = allUsedBuckets.concat(draftQuery.builderConfig.buckets) |
| 52 | + }) |
| 53 | + |
| 54 | + const completeBuckets = useSelector((state: AppState) => { |
| 55 | + const {draftQueries: drafts} = getActiveTimeMachine(state) |
| 56 | + const buckets = drafts.flatMap(draft => draft.builderConfig.buckets) |
| 57 | + return buckets.map(name => getBucketByName(state, name)) |
| 58 | + }) |
| 59 | + |
| 60 | + const history = useHistory() |
| 61 | + |
| 62 | + const handleChangeNotebookName = (event: ChangeEvent<HTMLInputElement>) => { |
| 63 | + setNotebookName(event.target.value) |
| 64 | + } |
| 65 | + |
| 66 | + const handleSubmit = async () => { |
| 67 | + event(`Data Explorer Save as ${PROJECT_NAME} Submitted`) |
| 68 | + |
| 69 | + try { |
| 70 | + event(`data_explorer.save.as_${PROJECT_NAME.toLowerCase()}.success`, { |
| 71 | + which: normalizeEventName(chartTypeName(properties?.type ?? 'xy')), |
| 72 | + }) |
| 73 | + const pipes: any = [] |
| 74 | + |
| 75 | + for (let i = 0; i < draftQueries.length; i++) { |
| 76 | + const draftQuery = draftQueries[i] |
| 77 | + let pipe: any = { |
| 78 | + id: `local_${nanoid()}`, |
| 79 | + visible: !draftQuery.hidden, |
| 80 | + } |
| 81 | + if (draftQuery.editMode === 'builder') { |
| 82 | + const bucket = completeBuckets.splice(0, 1) |
| 83 | + |
| 84 | + pipe.title = `Build a Query ${i + 1}` |
| 85 | + pipe.type = 'queryBuilder' |
| 86 | + pipe = { |
| 87 | + ...pipe, |
| 88 | + ...draftQuery.builderConfig, |
| 89 | + buckets: bucket, |
| 90 | + } |
| 91 | + } else { |
| 92 | + pipe.title = 'Query to Run' |
| 93 | + pipe.queries = draftQuery.builderConfig |
| 94 | + pipe.activeQuery = 0 |
| 95 | + pipe.type = 'rawFluxEditor' |
| 96 | + } |
| 97 | + |
| 98 | + pipes.push(pipe) |
| 99 | + pipes.push({ |
| 100 | + id: `local_${nanoid()}`, |
| 101 | + properties: { |
| 102 | + ...properties, |
| 103 | + builderConfig: draftQuery.builderConfig, |
| 104 | + }, |
| 105 | + title: `Visualize the Result ${i + 1}`, |
| 106 | + type: 'visualization', |
| 107 | + visible: true, |
| 108 | + }) |
| 109 | + } |
| 110 | + |
| 111 | + const flow = hydrate({ |
| 112 | + name: notebookName || DEFAULT_PROJECT_NAME, |
| 113 | + range: timeRange, |
| 114 | + orgID, |
| 115 | + spec: { |
| 116 | + readOnly: false, |
| 117 | + range: timeRange, |
| 118 | + refresh: autoRefresh || AUTOREFRESH_DEFAULT, |
| 119 | + pipes, |
| 120 | + }, |
| 121 | + }) |
| 122 | + |
| 123 | + const response = await postNotebook(serialize(flow)) |
| 124 | + |
| 125 | + if (response.status !== 200) { |
| 126 | + throw new Error(response.data.message) |
| 127 | + } |
| 128 | + |
| 129 | + const {id} = response.data |
| 130 | + |
| 131 | + // redirect to the notebook |
| 132 | + history.push(`/orgs/${orgID}/${PROJECT_NAME_PLURAL.toLowerCase()}/${id}`) |
| 133 | + } catch (error) { |
| 134 | + event(`data_explorer.save.as_${PROJECT_NAME.toLowerCase()}.failure`, { |
| 135 | + which: normalizeEventName(chartTypeName(properties?.type)), |
| 136 | + }) |
| 137 | + console.error(error) |
| 138 | + dismiss() |
| 139 | + } finally { |
| 140 | + setNotebookName('') |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + return ( |
| 145 | + <Grid> |
| 146 | + <Grid.Row> |
| 147 | + <Grid.Column widthXS={Columns.Twelve}> |
| 148 | + <Form.Element label={`${PROJECT_NAME} Name`}> |
| 149 | + <Input |
| 150 | + type={InputType.Text} |
| 151 | + placeholder={`Add optional ${PROJECT_NAME.toLowerCase()} name`} |
| 152 | + name="notebookName" |
| 153 | + value={notebookName} |
| 154 | + onChange={handleChangeNotebookName} |
| 155 | + testID={`save-as-${PROJECT_NAME.toLowerCase()}--name`} |
| 156 | + /> |
| 157 | + </Form.Element> |
| 158 | + </Grid.Column> |
| 159 | + <Grid.Column widthXS={Columns.Twelve}> |
| 160 | + <Form.Footer> |
| 161 | + <Button |
| 162 | + text="Cancel" |
| 163 | + onClick={dismiss} |
| 164 | + titleText="Cancel" |
| 165 | + type={ButtonType.Button} |
| 166 | + color={ComponentColor.Tertiary} |
| 167 | + testID={`save-as-${PROJECT_NAME.toLowerCase()}--cancel`} |
| 168 | + /> |
| 169 | + <Button |
| 170 | + text={`Save as ${PROJECT_NAME}`} |
| 171 | + testID={`save-as-${PROJECT_NAME.toLowerCase()}--submit`} |
| 172 | + color={ComponentColor.Success} |
| 173 | + type={ButtonType.Submit} |
| 174 | + onClick={handleSubmit} |
| 175 | + status={ComponentStatus.Default} |
| 176 | + /> |
| 177 | + </Form.Footer> |
| 178 | + </Grid.Column> |
| 179 | + </Grid.Row> |
| 180 | + </Grid> |
| 181 | + ) |
| 182 | +} |
| 183 | + |
| 184 | +export default SaveAsNotebookForm |
0 commit comments