From 76a525cf5787b05e75d879aaf7e4c148f6ce0bfc Mon Sep 17 00:00:00 2001 From: neptunian Date: Thu, 5 Dec 2019 12:23:40 -0500 Subject: [PATCH] add first page of add data source --- .../detail => components}/nav_button_back.tsx | 18 ++-- .../plugins/epm/public/hooks/use_links.tsx | 10 ++- .../epm/public/hooks/use_package_install.tsx | 23 ++--- x-pack/legacy/plugins/epm/public/routes.tsx | 16 +++- .../add_data_source/add_data_source_steps.tsx | 79 +++++++++++++++++ .../public/screens/add_data_source/index.tsx | 84 +++++++++++++++++++ .../screens/add_data_source/step_one.tsx | 72 ++++++++++++++++ .../epm/public/screens/detail/header.tsx | 4 +- 8 files changed, 277 insertions(+), 29 deletions(-) rename x-pack/legacy/plugins/epm/public/{screens/detail => components}/nav_button_back.tsx (53%) create mode 100644 x-pack/legacy/plugins/epm/public/screens/add_data_source/add_data_source_steps.tsx create mode 100644 x-pack/legacy/plugins/epm/public/screens/add_data_source/index.tsx create mode 100644 x-pack/legacy/plugins/epm/public/screens/add_data_source/step_one.tsx diff --git a/x-pack/legacy/plugins/epm/public/screens/detail/nav_button_back.tsx b/x-pack/legacy/plugins/epm/public/components/nav_button_back.tsx similarity index 53% rename from x-pack/legacy/plugins/epm/public/screens/detail/nav_button_back.tsx rename to x-pack/legacy/plugins/epm/public/components/nav_button_back.tsx index 42536e820ff7f..7f2d85ccd938c 100644 --- a/x-pack/legacy/plugins/epm/public/screens/detail/nav_button_back.tsx +++ b/x-pack/legacy/plugins/epm/public/components/nav_button_back.tsx @@ -3,23 +3,19 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { EuiButtonEmpty, EuiButtonEmptyProps } from '@elastic/eui'; -import React from 'react'; +import { EuiButtonEmpty } from '@elastic/eui'; import styled from 'styled-components'; -import { useCore, useLinks } from '../../hooks'; +import React from 'react'; +import { useCore } from '../hooks'; -export function NavButtonBack() { - const { toListView } = useLinks(); +export function NavButtonBack({ href, text }: { href: string; text: string }) { const { theme } = useCore(); - const ButtonEmpty = styled(EuiButtonEmpty).attrs({ - href: toListView(), - })` + const ButtonEmpty = styled(EuiButtonEmpty)` margin-right: ${theme.eui.spacerSizes.xl}; `; - return ( - - Browse Packages + + {text} ); } diff --git a/x-pack/legacy/plugins/epm/public/hooks/use_links.tsx b/x-pack/legacy/plugins/epm/public/hooks/use_links.tsx index 44f86e0821e6c..d2436afb3395a 100644 --- a/x-pack/legacy/plugins/epm/public/hooks/use_links.tsx +++ b/x-pack/legacy/plugins/epm/public/hooks/use_links.tsx @@ -5,10 +5,10 @@ */ import { generatePath } from 'react-router-dom'; -import { useCore } from '.'; import { PLUGIN } from '../../common/constants'; import { getFilePath, getInfoPath } from '../../common/routes'; import { patterns } from '../routes'; +import { useCore } from '.'; import { DetailViewPanelName } from '..'; // TODO: get this from server/packages/handlers.ts (move elsewhere?) @@ -53,5 +53,13 @@ export function useLinks() { const params = Object.assign({ pkgkey: `${name}-${version}` }, panel ? { panel } : {}); return appRoot(generatePath(patterns.DETAIL_VIEW, params)); }, + toDetailViewRelative: ({ name, version, panel }: DetailParams) => { + // panel is optional, but `generatePath` won't accept `path: undefined` + // so use this to pass `{ pkgkey }` or `{ pkgkey, panel }` + const params = Object.assign({ pkgkey: `${name}-${version}` }, panel ? { panel } : {}); + return generatePath(patterns.DETAIL_VIEW, params); + }, + toAddDataSourceView: ({ name, version }: { name: string; version: string }) => + appRoot(generatePath(patterns.ADD_DATA_SOURCE_VIEW, { pkgkey: `${name}-${version}` })), }; } diff --git a/x-pack/legacy/plugins/epm/public/hooks/use_package_install.tsx b/x-pack/legacy/plugins/epm/public/hooks/use_package_install.tsx index 24e44d43c2c89..07d44e9de6d23 100644 --- a/x-pack/legacy/plugins/epm/public/hooks/use_package_install.tsx +++ b/x-pack/legacy/plugins/epm/public/hooks/use_package_install.tsx @@ -4,14 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { useCallback, useState, Fragment } from 'react'; import createContainer from 'constate'; -import React, { Fragment, useCallback, useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui'; import { NotificationsStart } from 'src/core/public'; import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; import { PackageInfo } from '../../common/types'; -import { installDatasource, installPackage as fetchInstallPackage, removePackage } from '../data'; +import { installPackage as fetchInstallPackage, removePackage } from '../data'; import { InstallStatus } from '../types'; +import { useLinks } from '.'; interface PackagesInstall { [key: string]: PackageInstallItem; @@ -23,6 +24,7 @@ interface PackageInstallItem { function usePackageInstall({ notifications }: { notifications: NotificationsStart }) { const [packages, setPackage] = useState({}); + const { toAddDataSourceView } = useLinks(); const setPackageInstallStatus = useCallback( ({ name, status }: { name: PackageInfo['name']; status: InstallStatus }) => { @@ -38,18 +40,11 @@ function usePackageInstall({ notifications }: { notifications: NotificationsStar async ({ name, version, title }: Pick) => { setPackageInstallStatus({ name, status: InstallStatus.installing }); const pkgkey = `${name}-${version}`; - const handleRequestInstallDatasource = () => { - installDatasource(pkgkey).then(() => { - notifications.toasts.addSuccess({ - title: `Installed Datasource`, - text: 'Successfully installed Datasource', - }); - }); - }; + try { await fetchInstallPackage(pkgkey); setPackageInstallStatus({ name, status: InstallStatus.installed }); - + const packageDataSourceUrl = toAddDataSourceView({ name, version }); const SuccessMsg = (

Next, create a data source to begin sending data to your Elasticsearch cluster.

@@ -57,7 +52,7 @@ function usePackageInstall({ notifications }: { notifications: NotificationsStar {/* Would like to add a loading indicator here but, afaict, notifications are static. i.e. they won't re-render with new state */} - + Add data source @@ -79,7 +74,7 @@ function usePackageInstall({ notifications }: { notifications: NotificationsStar }); } }, - [notifications.toasts, setPackageInstallStatus] + [notifications.toasts, setPackageInstallStatus, toAddDataSourceView] ); const getPackageInstallStatus = useCallback( diff --git a/x-pack/legacy/plugins/epm/public/routes.tsx b/x-pack/legacy/plugins/epm/public/routes.tsx index 9da7c24d0d766..abf509bab393a 100644 --- a/x-pack/legacy/plugins/epm/public/routes.tsx +++ b/x-pack/legacy/plugins/epm/public/routes.tsx @@ -6,15 +6,17 @@ import React from 'react'; import { Route } from 'react-router-dom'; -import { PLUGIN } from '../common/constants'; import { Detail, DetailProps } from './screens/detail'; +import { AddDataSource, AddDataSourceProps } from './screens/add_data_source'; import { Home } from './screens/home'; +import { PLUGIN } from '../common/constants'; // patterns are used by React Router and are relative to `APP_ROOT` export const patterns = { APP_ROOT: `/app/${PLUGIN.ID}`, LIST_VIEW: '/', DETAIL_VIEW: '/detail/:pkgkey/:panel?', + ADD_DATA_SOURCE_VIEW: '/add-data-source/:pkgkey', }; export const routes = [ @@ -25,6 +27,12 @@ export const routes = [ exact={true} render={(props: DetailMatch) => } />, + } + />, ]; interface DetailMatch { @@ -32,3 +40,9 @@ interface DetailMatch { params: DetailProps; }; } + +interface AddDataSourceMatch { + match: { + params: AddDataSourceProps; + }; +} diff --git a/x-pack/legacy/plugins/epm/public/screens/add_data_source/add_data_source_steps.tsx b/x-pack/legacy/plugins/epm/public/screens/add_data_source/add_data_source_steps.tsx new file mode 100644 index 0000000000000..e48be23d636b4 --- /dev/null +++ b/x-pack/legacy/plugins/epm/public/screens/add_data_source/add_data_source_steps.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useState, Fragment, useCallback } from 'react'; +import styled from 'styled-components'; +import { EuiPanel, EuiSteps, EuiButton, EuiHorizontalRule } from '@elastic/eui'; +import { Redirect } from 'react-router-dom'; +import { StepOneTemplate } from './step_one'; +import { installDatasource } from '../../data'; +import { useCore, useLinks } from '../../hooks'; + +const StyledSteps = styled.div` + .euiStep__titleWrapper { + border-bottom: 1px solid ${props => props.theme.eui.euiBorderColor}; + padding-bottom: ${props => props.theme.eui.paddingSizes.l}; + } + .euiStep__content { + padding-bottom: 0; + } +`; +interface AddDataSourceStepsProps { + pkgName: string; + pkgTitle: string; + pkgVersion: string; +} +const FormNav = styled.div` + text-align: right; +`; +export const AddDataSourceSteps = (props: AddDataSourceStepsProps) => { + const [addDataSourceSuccess, setAddDataSourceSuccess] = useState(false); + const { notifications } = useCore(); + const { toDetailViewRelative } = useLinks(); + const { pkgName, pkgTitle, pkgVersion } = props; + const handleRequestInstallDatasource = useCallback(async () => { + try { + await installDatasource(`${pkgName}-${pkgVersion}`); + setAddDataSourceSuccess(true); + notifications.toasts.addSuccess({ + title: `Added ${pkgTitle} data source`, + }); + return; + } catch (err) { + notifications.toasts.addWarning({ + title: `Failed to add data source to ${pkgTitle}`, + iconType: 'alert', + }); + } + }, [notifications.toasts, pkgName, pkgTitle, pkgVersion]); + const stepOne = [ + { + title: 'Define your data source', + children: , + }, + ]; + + return ( + + {addDataSourceSuccess ? ( + + ) : ( + + + + + + + + Continue + + + + )} + + ); +}; diff --git a/x-pack/legacy/plugins/epm/public/screens/add_data_source/index.tsx b/x-pack/legacy/plugins/epm/public/screens/add_data_source/index.tsx new file mode 100644 index 0000000000000..4ac3cecd4b53e --- /dev/null +++ b/x-pack/legacy/plugins/epm/public/screens/add_data_source/index.tsx @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useState, useEffect } from 'react'; +import { ICON_TYPES, EuiPageHeader, EuiTitle, EuiFlexGroup, EuiIcon, EuiPanel } from '@elastic/eui'; +import styled from 'styled-components'; +import { NavButtonBack } from '../../components/nav_button_back'; +import { useLinks } from '../../hooks'; +import { PackageInfo } from '../../../common/types'; +import { getPackageInfoByKey } from '../../data'; +import { AddDataSourceSteps } from './add_data_source_steps'; + +export interface AddDataSourceProps { + pkgkey: string; +} + +// TODO: change to percentages? +const SIDEBAR_WIDTH = '168px'; + +const PageContainer = styled.div` + margin: 0 auto; + min-width: 1200px; +`; +const PageBody = styled.div` + max-width: 772px; +`; +const NavButtonBackWrapper = styled.div` + padding: ${props => props.theme.eui.paddingSizes.xl} 0 ${props => props.theme.eui.paddingSizes.m} + 0; +`; +const SideBar = styled.div` + margin-right: ${props => props.theme.eui.paddingSizes.xl}; + max-width: ${SIDEBAR_WIDTH}; +`; +const IconPanel = styled(EuiPanel)` + padding: ${props => props.theme.eui.paddingSizes.xl} !important; + text-align: center; + width: ${SIDEBAR_WIDTH}; + height: ${SIDEBAR_WIDTH}; +`; +export function AddDataSource({ pkgkey }: AddDataSourceProps) { + const [info, setInfo] = useState(null); + const { toDetailView } = useLinks(); + + useEffect(() => { + getPackageInfoByKey(pkgkey).then(response => { + setInfo({ ...response }); + }); + }, [pkgkey]); + + // don't have designs for loading/empty states + if (!info) return null; + + const { version, name, title } = info; + const iconType = ICON_TYPES.find(key => key.toLowerCase() === `logo${name}`); + + return ( + + + + + + + + {iconType ? : null} + + + + + +

Add {title} data source

+
+
+ +
+
+
+ ); +} diff --git a/x-pack/legacy/plugins/epm/public/screens/add_data_source/step_one.tsx b/x-pack/legacy/plugins/epm/public/screens/add_data_source/step_one.tsx new file mode 100644 index 0000000000000..f9181cc95781d --- /dev/null +++ b/x-pack/legacy/plugins/epm/public/screens/add_data_source/step_one.tsx @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment } from 'react'; +import { + EuiForm, + EuiDescribedFormGroup, + EuiFormRow, + EuiFieldText, + EuiHorizontalRule, + EuiSwitch, + EuiSpacer, +} from '@elastic/eui'; + +export const StepOneTemplate = () => { + return ( + + + Choose a name} + description={ + + Append a label to your data source name to help distinguish it from your other data + sources. + + } + > + + + + + + Select your inputs} + description={ + Select the data you want to send to your Elastic Search cluster. + } + > + + + true} + /> + + true} + /> + + true} + /> + + + + + + ); +}; diff --git a/x-pack/legacy/plugins/epm/public/screens/detail/header.tsx b/x-pack/legacy/plugins/epm/public/screens/detail/header.tsx index c85b542c1bfbd..68034ab514f33 100644 --- a/x-pack/legacy/plugins/epm/public/screens/detail/header.tsx +++ b/x-pack/legacy/plugins/epm/public/screens/detail/header.tsx @@ -13,7 +13,7 @@ import { Version } from '../../components/version'; import { useBreadcrumbs, useLinks } from '../../hooks'; import { InstallationButton } from './installation_button'; import { CenterColumn, LeftColumn, RightColumn } from './layout'; -import { NavButtonBack } from './nav_button_back'; +import { NavButtonBack } from '../../components/nav_button_back'; const FullWidthNavRow = styled(EuiPage)` /* no left padding so link is against column left edge */ @@ -39,7 +39,7 @@ export function Header(props: HeaderProps) { return ( - + {iconType ? (