From a1a6e54b1eabc114fd320d03b31609f372a2ab6f Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Thu, 1 Jul 2021 19:05:15 +0300 Subject: [PATCH 01/11] Add side menu component --- .../src/components/SideMenu/SideMenu.tsx | 38 ++++++++++++++ .../SideMenu/components/MenuItem.tsx | 51 +++++++++++++++++++ .../src/components/SideMenu/index.tsx | 4 ++ airbyte-webapp/src/components/index.tsx | 1 + 4 files changed, 94 insertions(+) create mode 100644 airbyte-webapp/src/components/SideMenu/SideMenu.tsx create mode 100644 airbyte-webapp/src/components/SideMenu/components/MenuItem.tsx create mode 100644 airbyte-webapp/src/components/SideMenu/index.tsx diff --git a/airbyte-webapp/src/components/SideMenu/SideMenu.tsx b/airbyte-webapp/src/components/SideMenu/SideMenu.tsx new file mode 100644 index 000000000000..5477cb068ff1 --- /dev/null +++ b/airbyte-webapp/src/components/SideMenu/SideMenu.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import styled from "styled-components"; + +import MenuItem from "./components/MenuItem"; + +export type SideMenuItem = { + id: string; + name: string | React.ReactNode; + indicatorCount?: number; +}; + +type SideMenuProps = { + data: SideMenuItem[]; + activeItem?: string; + onSelect: (id: string) => void; +}; + +const Content = styled.nav` + min-width: 147px; +`; + +const SideMenu: React.FC = ({ data, onSelect, activeItem }) => { + return ( + + {data.map((item) => ( + onSelect(item.id)} + /> + ))} + + ); +}; + +export default SideMenu; diff --git a/airbyte-webapp/src/components/SideMenu/components/MenuItem.tsx b/airbyte-webapp/src/components/SideMenu/components/MenuItem.tsx new file mode 100644 index 000000000000..9d081637e16b --- /dev/null +++ b/airbyte-webapp/src/components/SideMenu/components/MenuItem.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import styled from "styled-components"; + +type IProps = { + name: string | React.ReactNode; + isActive?: boolean; + count?: number; + onClick: () => void; +}; + +const Item = styled.div<{ + isActive?: boolean; +}>` + width: 100%; + padding: 6px 8px 7px; + border-radius: 4px; + cursor: pointer; + background: ${({ theme, isActive }) => + isActive ? theme.primaryColor12 : "none"}; + font-style: normal; + font-weight: 500; + font-size: 12px; + line-height: 15px; + color: ${({ theme, isActive }) => + isActive ? theme.primaryColor : theme.greyColor60}; +`; + +const Counter = styled.div` + min-width: 12px; + height: 12px; + padding: 0 3px; + text-align: center; + border-radius: 15px; + background: ${({ theme }) => theme.dangerColor}; + font-size: 8px; + line-height: 12px; + color: ${({ theme }) => theme.whiteColor}; + display: inline-block; + margin-left: 5px; +`; + +const MenuItem: React.FC = ({ name, isActive, count, onClick }) => { + return ( + + {name} + {count ? {count} : null} + + ); +}; + +export default MenuItem; diff --git a/airbyte-webapp/src/components/SideMenu/index.tsx b/airbyte-webapp/src/components/SideMenu/index.tsx new file mode 100644 index 000000000000..203a285deccb --- /dev/null +++ b/airbyte-webapp/src/components/SideMenu/index.tsx @@ -0,0 +1,4 @@ +import SideMenu from "./SideMenu"; + +export default SideMenu; +export { SideMenu }; diff --git a/airbyte-webapp/src/components/index.tsx b/airbyte-webapp/src/components/index.tsx index 8bab89641b7f..a94cf6ba2922 100644 --- a/airbyte-webapp/src/components/index.tsx +++ b/airbyte-webapp/src/components/index.tsx @@ -17,3 +17,4 @@ export * from "./ContentCard"; export * from "./ImageBlock"; export * from "./LabeledRadioButton"; export * from "./Modal"; +export * from "./SideMenu"; From 397a5178990a2924c04ef473654c03315051e0a5 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Thu, 1 Jul 2021 23:48:54 +0300 Subject: [PATCH 02/11] Add side menu to settings page. Remove admin link from sidebar --- airbyte-webapp/src/locales/en.json | 2 + .../src/pages/SettingsPage/SettingsPage.tsx | 69 ++++++++++++++++++- airbyte-webapp/src/pages/routes.tsx | 3 + .../src/views/layout/SideBar/SideBar.tsx | 29 +++----- 4 files changed, 82 insertions(+), 21 deletions(-) diff --git a/airbyte-webapp/src/locales/en.json b/airbyte-webapp/src/locales/en.json index fb7644945e81..d2873b582f64 100644 --- a/airbyte-webapp/src/locales/en.json +++ b/airbyte-webapp/src/locales/en.json @@ -322,6 +322,8 @@ "settings.webhookTestText": "Testing the Webhook will send a “Hello World”. ", "settings.yourWebhook": "Your Webhook URL", "settings.test": "Test", + "settings.notifications": "Notifications", + "settings.metrics": "Metrics", "connector.requestConnectorBlock": "+ Request a new connector", "connector.requestConnector": "Request a new connector", diff --git a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx index c9ab4a27cc22..202d318db8a5 100644 --- a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx @@ -1,19 +1,56 @@ import React, { Suspense } from "react"; import { FormattedMessage } from "react-intl"; import styled from "styled-components"; +import { Redirect, Route, Switch } from "react-router"; import MainPageWithScroll from "components/MainPageWithScroll"; import PageTitle from "components/PageTitle"; import LoadingPage from "components/LoadingPage"; import AccountSettings from "./components/AccountSettings"; import HeadTitle from "components/HeadTitle"; +import SideMenu from "components/SideMenu"; +import { Routes } from "pages/routes"; +import useRouter from "../../components/hooks/useRouterHook"; const Content = styled.div` margin: 0 33px 0 27px; height: 100%; + display: flex; + flex-direction: row; +`; +const MainView = styled.div` + width: 100%; + margin-left: 47px; `; const SettingsPage: React.FC = () => { + const { push, pathname } = useRouter(); + + const menuItems = [ + { + id: `${Routes.Settings}${Routes.Source}`, + name: , + }, + { + id: `${Routes.Settings}${Routes.Destination}`, + name: , + }, + { + id: `${Routes.Settings}${Routes.Configuration}`, + name: , + }, + { + id: `${Routes.Settings}${Routes.Notifications}`, + name: , + }, + { + id: `${Routes.Settings}${Routes.Metrics}`, + name: , + }, + ]; + + const onSelectMenuItem = (newPath: string) => push(newPath); + return ( } @@ -25,9 +62,35 @@ const SettingsPage: React.FC = () => { } > - }> - - + + + + }> + + +
Source
+
+ +
Destination
+
+ +
Configuration
+
+ + + + +
Metrics
+
+ + +
+
+
); diff --git a/airbyte-webapp/src/pages/routes.tsx b/airbyte-webapp/src/pages/routes.tsx index 8e814e140410..80605cf6cff0 100644 --- a/airbyte-webapp/src/pages/routes.tsx +++ b/airbyte-webapp/src/pages/routes.tsx @@ -41,6 +41,9 @@ export enum Routes { DestinationNew = "/new-destination", Admin = "/admin", Settings = "/settings", + Configuration = "/configuration", + Notifications = "/notifications", + Metrics = "/metrics", Root = "/", } diff --git a/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx b/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx index 20587d9ade9e..fcccb7e18f43 100644 --- a/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx +++ b/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx @@ -1,12 +1,7 @@ import React from "react"; import styled from "styled-components"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faLifeRing, - faBook, - faCog, - faTools, -} from "@fortawesome/free-solid-svg-icons"; +import { faLifeRing, faBook, faCog } from "@fortawesome/free-solid-svg-icons"; import { faSlack } from "@fortawesome/free-brands-svg-icons"; import { FormattedMessage } from "react-intl"; import { NavLink } from "react-router-dom"; @@ -94,7 +89,7 @@ const HelpIcon = styled(FontAwesomeIcon)` line-height: 21px; `; -const AdminIcon = styled(FontAwesomeIcon)` +const SettingsIcon = styled(FontAwesomeIcon)` font-size: 16px; line-height: 15px; `; @@ -148,11 +143,17 @@ const SideBar: React.FC = () => {
  • - + + location.pathname.startsWith(Routes.Settings) + } + > {hasNewVersions ? : null} - + - +
  • @@ -184,14 +185,6 @@ const SideBar: React.FC = () => { -
  • - - - - - - -
  • {config.version ? (
  • From 6281612ff084e4dd1d3ad6abc240a1b196d17034 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Thu, 1 Jul 2021 23:52:32 +0300 Subject: [PATCH 03/11] Move NotificationPage --- airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx | 4 ++-- .../NotificationPage/NotificationPage.tsx} | 6 +++--- .../{ => pages/NotificationPage}/components/WebHookForm.tsx | 0 .../src/pages/SettingsPage/pages/NotificationPage/index.tsx | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) rename airbyte-webapp/src/pages/SettingsPage/{components/AccountSettings.tsx => pages/NotificationPage/NotificationPage.tsx} (96%) rename airbyte-webapp/src/pages/SettingsPage/{ => pages/NotificationPage}/components/WebHookForm.tsx (100%) create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/index.tsx diff --git a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx index 202d318db8a5..f111094864ec 100644 --- a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx @@ -6,7 +6,7 @@ import { Redirect, Route, Switch } from "react-router"; import MainPageWithScroll from "components/MainPageWithScroll"; import PageTitle from "components/PageTitle"; import LoadingPage from "components/LoadingPage"; -import AccountSettings from "./components/AccountSettings"; +import NotificationPage from "./pages/NotificationPage"; import HeadTitle from "components/HeadTitle"; import SideMenu from "components/SideMenu"; import { Routes } from "pages/routes"; @@ -81,7 +81,7 @@ const SettingsPage: React.FC = () => {
    Configuration
    - +
    Metrics
    diff --git a/airbyte-webapp/src/pages/SettingsPage/components/AccountSettings.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx similarity index 96% rename from airbyte-webapp/src/pages/SettingsPage/components/AccountSettings.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx index 0a6d6660985c..0b7188363c37 100644 --- a/airbyte-webapp/src/pages/SettingsPage/components/AccountSettings.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx @@ -5,7 +5,7 @@ import styled from "styled-components"; import { ContentCard } from "components"; import { PreferencesForm } from "views/Settings/PreferencesForm"; import useWorkspace from "components/hooks/services/useWorkspaceHook"; -import WebHookForm from "./WebHookForm"; +import WebHookForm from "./components/WebHookForm"; const SettingsCard = styled(ContentCard)` max-width: 638px; @@ -21,7 +21,7 @@ const Content = styled.div` padding: 27px 26px 15px; `; -const AccountSettings: React.FC = () => { +const NotificationPage: React.FC = () => { const { workspace, updatePreferences, @@ -116,4 +116,4 @@ const AccountSettings: React.FC = () => { ); }; -export default AccountSettings; +export default NotificationPage; diff --git a/airbyte-webapp/src/pages/SettingsPage/components/WebHookForm.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx similarity index 100% rename from airbyte-webapp/src/pages/SettingsPage/components/WebHookForm.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/index.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/index.tsx new file mode 100644 index 000000000000..7c47865305fb --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/index.tsx @@ -0,0 +1,3 @@ +import NotificationPage from "./NotificationPage"; + +export default NotificationPage; From 0a2b272a43b7528db725c2d726d95606f3df842c Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Fri, 2 Jul 2021 00:00:14 +0300 Subject: [PATCH 04/11] Move ConfigurationPage --- airbyte-webapp/src/pages/AdminPage/AdminPage.tsx | 4 ---- airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx | 7 ++++--- .../pages/ConfigurationsPage/ConfigurationsPage.tsx} | 8 ++++---- .../components/ImportConfigurationModal.tsx | 0 .../pages/ConfigurationsPage}/components/LogsContent.tsx | 0 .../pages/SettingsPage/pages/ConfigurationsPage/index.tsx | 3 +++ 6 files changed, 11 insertions(+), 11 deletions(-) rename airbyte-webapp/src/pages/{AdminPage/components/ConfigurationView.tsx => SettingsPage/pages/ConfigurationsPage/ConfigurationsPage.tsx} (94%) rename airbyte-webapp/src/pages/{AdminPage => SettingsPage/pages/ConfigurationsPage}/components/ImportConfigurationModal.tsx (100%) rename airbyte-webapp/src/pages/{AdminPage => SettingsPage/pages/ConfigurationsPage}/components/LogsContent.tsx (100%) create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/index.tsx diff --git a/airbyte-webapp/src/pages/AdminPage/AdminPage.tsx b/airbyte-webapp/src/pages/AdminPage/AdminPage.tsx index 9cf6d2fa08d5..78c603493e21 100644 --- a/airbyte-webapp/src/pages/AdminPage/AdminPage.tsx +++ b/airbyte-webapp/src/pages/AdminPage/AdminPage.tsx @@ -10,7 +10,6 @@ import LoadingPage from "components/LoadingPage"; import SourcesView from "./components/SourcesView"; import DestinationsView from "./components/DestinationsView"; import CreateConnector from "./components/CreateConnector"; -import ConfigurationView from "./components/ConfigurationView"; import HeadTitle from "components/HeadTitle"; const Content = styled.div` @@ -47,9 +46,6 @@ const AdminPage: React.FC = () => { if (currentStep === StepsTypes.SOURCES) { return ; } - if (currentStep === StepsTypes.CONFIGURATION) { - return ; - } return ; }; diff --git a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx index f111094864ec..3f46d0d7ef1f 100644 --- a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx @@ -6,11 +6,12 @@ import { Redirect, Route, Switch } from "react-router"; import MainPageWithScroll from "components/MainPageWithScroll"; import PageTitle from "components/PageTitle"; import LoadingPage from "components/LoadingPage"; -import NotificationPage from "./pages/NotificationPage"; import HeadTitle from "components/HeadTitle"; import SideMenu from "components/SideMenu"; import { Routes } from "pages/routes"; -import useRouter from "../../components/hooks/useRouterHook"; +import useRouter from "components/hooks/useRouterHook"; +import NotificationPage from "./pages/NotificationPage"; +import ConfigurationsPage from "./pages/ConfigurationsPage"; const Content = styled.div` margin: 0 33px 0 27px; @@ -78,7 +79,7 @@ const SettingsPage: React.FC = () => {
    Destination
    -
    Configuration
    +
    diff --git a/airbyte-webapp/src/pages/AdminPage/components/ConfigurationView.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/ConfigurationsPage.tsx similarity index 94% rename from airbyte-webapp/src/pages/AdminPage/components/ConfigurationView.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/ConfigurationsPage.tsx index 9198faf57654..f5cb69104fd8 100644 --- a/airbyte-webapp/src/pages/AdminPage/components/ConfigurationView.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/ConfigurationsPage.tsx @@ -8,8 +8,8 @@ import ContentCard from "components/ContentCard"; import config from "config"; import Link from "components/Link"; import DeploymentService from "core/resources/DeploymentService"; -import ImportConfigurationModal from "./ImportConfigurationModal"; -import LogsContent from "./LogsContent"; +import ImportConfigurationModal from "./components/ImportConfigurationModal"; +import LogsContent from "./components/LogsContent"; import HeadTitle from "components/HeadTitle"; const Content = styled.div` @@ -45,7 +45,7 @@ const Warning = styled.div` font-weight: bold; `; -const ConfigurationView: React.FC = () => { +const ConfigurationsPage: React.FC = () => { const [isModalOpen, setIsModalOpen] = useState(false); const [error, setError] = useState(null); @@ -143,4 +143,4 @@ const ConfigurationView: React.FC = () => { ); }; -export default ConfigurationView; +export default ConfigurationsPage; diff --git a/airbyte-webapp/src/pages/AdminPage/components/ImportConfigurationModal.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/components/ImportConfigurationModal.tsx similarity index 100% rename from airbyte-webapp/src/pages/AdminPage/components/ImportConfigurationModal.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/components/ImportConfigurationModal.tsx diff --git a/airbyte-webapp/src/pages/AdminPage/components/LogsContent.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/components/LogsContent.tsx similarity index 100% rename from airbyte-webapp/src/pages/AdminPage/components/LogsContent.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/components/LogsContent.tsx diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/index.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/index.tsx new file mode 100644 index 000000000000..aeb38ed29a66 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/index.tsx @@ -0,0 +1,3 @@ +import ConfigurationsPage from "./ConfigurationsPage"; + +export default ConfigurationsPage; From c142521106c1eda2b73a2d16539144a79b446c3f Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Fri, 2 Jul 2021 01:59:30 +0300 Subject: [PATCH 05/11] Add Sources and Destinations pages to Settings. Delete Admin page --- .../src/pages/AdminPage/AdminPage.tsx | 79 ------- .../AdminPage/components/DestinationsView.tsx | 203 ------------------ airbyte-webapp/src/pages/AdminPage/index.tsx | 3 - .../src/pages/SettingsPage/SettingsPage.tsx | 5 +- .../ConfigurationsPage/ConfigurationsPage.tsx | 7 +- .../pages/ConnectorsPage/DestinationsPage.tsx | 104 +++++++++ .../pages/ConnectorsPage/SourcesPage.tsx | 98 +++++++++ .../components/ConnectorCell.tsx | 0 .../components/ConnectorsView.tsx | 167 ++++++++++++++ .../components/CreateConnector.tsx | 2 +- .../components/CreateConnectorModal.tsx | 0 .../ConnectorsPage}/components/ImageCell.tsx | 0 .../components/PageComponents.tsx | 0 .../components/UpgradeAllButton.tsx | 74 +++++++ .../components/VersionCell.tsx | 0 .../pages/ConnectorsPage/index.tsx | 4 + .../NotificationPage/NotificationPage.tsx | 4 + .../pages/SourcesPage/SourcesPage.tsx} | 14 +- .../SourcesPage/components/ConnectorCell.tsx | 40 ++++ .../SourcesPage/components/ImageCell.tsx | 28 +++ .../SourcesPage/components/PageComponents.tsx | 26 +++ .../components/UpgradeAllButton.tsx | 0 .../SourcesPage/components/VersionCell.tsx | 138 ++++++++++++ .../SettingsPage/pages/SourcesPage/index.tsx | 3 + airbyte-webapp/src/pages/routes.tsx | 22 +- 25 files changed, 713 insertions(+), 308 deletions(-) delete mode 100644 airbyte-webapp/src/pages/AdminPage/AdminPage.tsx delete mode 100644 airbyte-webapp/src/pages/AdminPage/components/DestinationsView.tsx delete mode 100644 airbyte-webapp/src/pages/AdminPage/index.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/SourcesPage.tsx rename airbyte-webapp/src/pages/{AdminPage => SettingsPage/pages/ConnectorsPage}/components/ConnectorCell.tsx (100%) create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ConnectorsView.tsx rename airbyte-webapp/src/pages/{AdminPage => SettingsPage/pages/ConnectorsPage}/components/CreateConnector.tsx (98%) rename airbyte-webapp/src/pages/{AdminPage => SettingsPage/pages/ConnectorsPage}/components/CreateConnectorModal.tsx (100%) rename airbyte-webapp/src/pages/{AdminPage => SettingsPage/pages/ConnectorsPage}/components/ImageCell.tsx (100%) rename airbyte-webapp/src/pages/{AdminPage => SettingsPage/pages/ConnectorsPage}/components/PageComponents.tsx (100%) create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/UpgradeAllButton.tsx rename airbyte-webapp/src/pages/{AdminPage => SettingsPage/pages/ConnectorsPage}/components/VersionCell.tsx (100%) create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/index.tsx rename airbyte-webapp/src/pages/{AdminPage/components/SourcesView.tsx => SettingsPage/pages/SourcesPage/SourcesPage.tsx} (93%) create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/ConnectorCell.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/ImageCell.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/PageComponents.tsx rename airbyte-webapp/src/pages/{AdminPage => SettingsPage/pages/SourcesPage}/components/UpgradeAllButton.tsx (100%) create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/VersionCell.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/index.tsx diff --git a/airbyte-webapp/src/pages/AdminPage/AdminPage.tsx b/airbyte-webapp/src/pages/AdminPage/AdminPage.tsx deleted file mode 100644 index 78c603493e21..000000000000 --- a/airbyte-webapp/src/pages/AdminPage/AdminPage.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, { Suspense, useState } from "react"; -import { FormattedMessage } from "react-intl"; -import styled from "styled-components"; - -import MainPageWithScroll from "components/MainPageWithScroll"; -import PageTitle from "components/PageTitle"; -import StepsMenu from "components/StepsMenu"; -import { StepMenuItem } from "components/StepsMenu/StepsMenu"; -import LoadingPage from "components/LoadingPage"; -import SourcesView from "./components/SourcesView"; -import DestinationsView from "./components/DestinationsView"; -import CreateConnector from "./components/CreateConnector"; -import HeadTitle from "components/HeadTitle"; - -const Content = styled.div` - padding-top: 4px; - margin: 0 33px 0 27px; - height: 100%; -`; - -enum StepsTypes { - SOURCES = "sources", - DESTINATIONS = "destinations", - CONFIGURATION = "configuration", -} - -const AdminPage: React.FC = () => { - const steps: StepMenuItem[] = [ - { - id: StepsTypes.SOURCES, - name: , - }, - { - id: StepsTypes.DESTINATIONS, - name: , - }, - { - id: StepsTypes.CONFIGURATION, - name: , - }, - ]; - const [currentStep, setCurrentStep] = useState(StepsTypes.SOURCES); - const onSelectStep = (id: string) => setCurrentStep(id); - - const renderStep = () => { - if (currentStep === StepsTypes.SOURCES) { - return ; - } - - return ; - }; - - return ( - } - pageTitle={ - } - middleComponent={ - - } - endComponent={} - /> - } - > - - }>{renderStep()} - - - ); -}; - -export default AdminPage; diff --git a/airbyte-webapp/src/pages/AdminPage/components/DestinationsView.tsx b/airbyte-webapp/src/pages/AdminPage/components/DestinationsView.tsx deleted file mode 100644 index ef99bb2dc208..000000000000 --- a/airbyte-webapp/src/pages/AdminPage/components/DestinationsView.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import React, { useCallback, useMemo, useState } from "react"; -import { FormattedMessage, useIntl } from "react-intl"; -import { useFetcher, useResource } from "rest-hooks"; -import { CellProps } from "react-table"; -import { useAsyncFn } from "react-use"; - -import { Block, Title, FormContentTitle } from "./PageComponents"; -import Table from "components/Table"; -import ConnectorCell from "./ConnectorCell"; -import ImageCell from "./ImageCell"; -import VersionCell from "./VersionCell"; -import config from "config"; -import DestinationDefinitionResource from "core/resources/DestinationDefinition"; -import { DestinationResource } from "core/resources/Destination"; -import { DestinationDefinition } from "core/resources/DestinationDefinition"; -import UpgradeAllButton from "./UpgradeAllButton"; -import useConnector from "components/hooks/services/useConnector"; -import HeadTitle from "components/HeadTitle"; - -const DestinationsView: React.FC = () => { - const [successUpdate, setSuccessUpdate] = useState(false); - const formatMessage = useIntl().formatMessage; - const { destinationDefinitions } = useResource( - DestinationDefinitionResource.listShape(), - { - workspaceId: config.ui.workspaceId, - } - ); - const { destinations } = useResource(DestinationResource.listShape(), { - workspaceId: config.ui.workspaceId, - }); - - const [feedbackList, setFeedbackList] = useState>({}); - - const updateDestinationDefinition = useFetcher( - DestinationDefinitionResource.updateShape() - ); - - const { hasNewDestinationVersion } = useConnector(); - - const onUpdateVersion = useCallback( - async ({ id, version }: { id: string; version: string }) => { - try { - await updateDestinationDefinition( - {}, - { - destinationDefinitionId: id, - dockerImageTag: version, - } - ); - setFeedbackList({ ...feedbackList, [id]: "success" }); - } catch (e) { - const messageId = - e.status === 422 ? "form.imageCannotFound" : "form.someError"; - setFeedbackList({ - ...feedbackList, - [id]: formatMessage({ id: messageId }), - }); - } - }, - [feedbackList, formatMessage, updateDestinationDefinition] - ); - - const columns = React.useMemo( - () => [ - { - Header: , - accessor: "name", - customWidth: 25, - Cell: ({ - cell, - row, - }: CellProps<{ - latestDockerImageTag: string; - dockerImageTag: string; - icon?: string; - }>) => ( - - ), - }, - { - Header: , - accessor: "dockerRepository", - customWidth: 36, - Cell: ({ cell, row }: CellProps<{ documentationUrl: string }>) => ( - - ), - }, - { - Header: , - accessor: "dockerImageTag", - customWidth: 10, - }, - { - Header: ( - - - - ), - accessor: "latestDockerImageTag", - collapse: true, - Cell: ({ - cell, - row, - }: CellProps<{ - destinationDefinitionId: string; - dockerImageTag: string; - }>) => ( - - ), - }, - ], - [feedbackList, onUpdateVersion] - ); - - const usedDestinationDefinitions = useMemo(() => { - const destinationDefinitionMap = new Map(); - destinations.forEach((destination) => { - const destinationDefinition = destinationDefinitions.find( - (destinationDefinition) => - destinationDefinition.destinationDefinitionId === - destination.destinationDefinitionId - ); - - if (destinationDefinition) { - destinationDefinitionMap.set( - destinationDefinition.destinationDefinitionId, - destinationDefinition - ); - } - }); - - return Array.from(destinationDefinitionMap.values()); - }, [destinations, destinationDefinitions]); - - const { updateAllDestinationVersions } = useConnector(); - - const [{ loading, error }, onUpdate] = useAsyncFn(async () => { - setSuccessUpdate(false); - await updateAllDestinationVersions(); - setSuccessUpdate(true); - setTimeout(() => { - setSuccessUpdate(false); - }, 2000); - }, [updateAllDestinationVersions]); - - return ( - <> - - {usedDestinationDefinitions.length ? ( - - - <FormattedMessage id="admin.manageDestination" /> - {(hasNewDestinationVersion || successUpdate) && ( - <UpgradeAllButton - isLoading={loading} - hasError={!!error && !loading} - hasSuccess={successUpdate} - onUpdate={onUpdate} - /> - )} - - - - ) : null} - - - - <FormattedMessage id="admin.availableDestinations" /> - {(hasNewDestinationVersion || successUpdate) && - !usedDestinationDefinitions.length && ( - <UpgradeAllButton - isLoading={loading} - hasError={!!error && !loading} - hasSuccess={successUpdate} - onUpdate={onUpdate} - /> - )} - -
    - - - ); -}; - -export default DestinationsView; diff --git a/airbyte-webapp/src/pages/AdminPage/index.tsx b/airbyte-webapp/src/pages/AdminPage/index.tsx deleted file mode 100644 index d2e0bbd197f6..000000000000 --- a/airbyte-webapp/src/pages/AdminPage/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import AdminPage from "./AdminPage"; - -export default AdminPage; diff --git a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx index 3f46d0d7ef1f..98f5c4356aae 100644 --- a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx @@ -12,6 +12,7 @@ import { Routes } from "pages/routes"; import useRouter from "components/hooks/useRouterHook"; import NotificationPage from "./pages/NotificationPage"; import ConfigurationsPage from "./pages/ConfigurationsPage"; +import { DestinationsPage, SourcesPage } from "./pages/ConnectorsPage"; const Content = styled.div` margin: 0 33px 0 27px; @@ -73,10 +74,10 @@ const SettingsPage: React.FC = () => { }> -
    Source
    +
    -
    Destination
    +
    diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/ConfigurationsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/ConfigurationsPage.tsx index f5cb69104fd8..51975e1cf07e 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/ConfigurationsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConfigurationsPage/ConfigurationsPage.tsx @@ -14,7 +14,6 @@ import HeadTitle from "components/HeadTitle"; const Content = styled.div` max-width: 813px; - margin: 4px auto; `; const ControlContent = styled(ContentCard)` @@ -85,9 +84,9 @@ const ConfigurationsPage: React.FC = () => { return ( - }> + }> @@ -109,7 +108,7 @@ const ConfigurationsPage: React.FC = () => { /> - + }> diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx new file mode 100644 index 000000000000..9f07cac1c901 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/DestinationsPage.tsx @@ -0,0 +1,104 @@ +import React, { useCallback, useMemo, useState } from "react"; +import { useIntl } from "react-intl"; +import { useFetcher, useResource } from "rest-hooks"; +import { useAsyncFn } from "react-use"; + +import config from "config"; +import DestinationDefinitionResource from "core/resources/DestinationDefinition"; +import { DestinationResource } from "core/resources/Destination"; +import { DestinationDefinition } from "core/resources/DestinationDefinition"; +import useConnector from "components/hooks/services/useConnector"; +import ConnectorsView from "./components/ConnectorsView"; + +const DestinationsPage: React.FC = () => { + const [isUpdateSuccess, setIsUpdateSuccess] = useState(false); + const formatMessage = useIntl().formatMessage; + const { destinationDefinitions } = useResource( + DestinationDefinitionResource.listShape(), + { + workspaceId: config.ui.workspaceId, + } + ); + const { destinations } = useResource(DestinationResource.listShape(), { + workspaceId: config.ui.workspaceId, + }); + + const [feedbackList, setFeedbackList] = useState>({}); + + const updateDestinationDefinition = useFetcher( + DestinationDefinitionResource.updateShape() + ); + + const { hasNewDestinationVersion } = useConnector(); + + const onUpdateVersion = useCallback( + async ({ id, version }: { id: string; version: string }) => { + try { + await updateDestinationDefinition( + {}, + { + destinationDefinitionId: id, + dockerImageTag: version, + } + ); + setFeedbackList({ ...feedbackList, [id]: "success" }); + } catch (e) { + const messageId = + e.status === 422 ? "form.imageCannotFound" : "form.someError"; + setFeedbackList({ + ...feedbackList, + [id]: formatMessage({ id: messageId }), + }); + } + }, + [feedbackList, formatMessage, updateDestinationDefinition] + ); + + const usedDestinationDefinitions = useMemo(() => { + const destinationDefinitionMap = new Map(); + destinations.forEach((destination) => { + const destinationDefinition = destinationDefinitions.find( + (destinationDefinition) => + destinationDefinition.destinationDefinitionId === + destination.destinationDefinitionId + ); + + if (destinationDefinition) { + destinationDefinitionMap.set( + destinationDefinition.destinationDefinitionId, + destinationDefinition + ); + } + }); + + return Array.from(destinationDefinitionMap.values()); + }, [destinations, destinationDefinitions]); + + const { updateAllDestinationVersions } = useConnector(); + + const [{ loading, error }, onUpdate] = useAsyncFn(async () => { + setIsUpdateSuccess(false); + await updateAllDestinationVersions(); + setIsUpdateSuccess(true); + setTimeout(() => { + setIsUpdateSuccess(false); + }, 2000); + }, [updateAllDestinationVersions]); + + return ( + + ); +}; + +export default DestinationsPage; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/SourcesPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/SourcesPage.tsx new file mode 100644 index 000000000000..81a122a08345 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/SourcesPage.tsx @@ -0,0 +1,98 @@ +import React, { useCallback, useMemo, useState } from "react"; +import { useIntl } from "react-intl"; +import { useFetcher, useResource } from "rest-hooks"; +import { useAsyncFn } from "react-use"; + +import config from "config"; +import SourceDefinitionResource, { + SourceDefinition, +} from "core/resources/SourceDefinition"; +import { SourceResource } from "core/resources/Source"; +import useConnector from "components/hooks/services/useConnector"; +import ConnectorsView from "./components/ConnectorsView"; + +const SourcesPage: React.FC = () => { + const [isUpdateSuccess, setIsUpdateSucces] = useState(false); + const formatMessage = useIntl().formatMessage; + const { sources } = useResource(SourceResource.listShape(), { + workspaceId: config.ui.workspaceId, + }); + const { sourceDefinitions } = useResource( + SourceDefinitionResource.listShape(), + { + workspaceId: config.ui.workspaceId, + } + ); + + const updateSourceDefinition = useFetcher( + SourceDefinitionResource.updateShape() + ); + + const { hasNewSourceVersion, updateAllSourceVersions } = useConnector(); + + const [feedbackList, setFeedbackList] = useState>({}); + const onUpdateVersion = useCallback( + async ({ id, version }: { id: string; version: string }) => { + try { + await updateSourceDefinition( + {}, + { + sourceDefinitionId: id, + dockerImageTag: version, + } + ); + setFeedbackList({ ...feedbackList, [id]: "success" }); + } catch (e) { + const messageId = + e.status === 422 ? "form.imageCannotFound" : "form.someError"; + setFeedbackList({ + ...feedbackList, + [id]: formatMessage({ id: messageId }), + }); + } + }, + [feedbackList, formatMessage, updateSourceDefinition] + ); + + const usedSourcesDefinitions = useMemo(() => { + const sourceDefinitionMap = new Map(); + sources.forEach((source) => { + const sourceDestination = sourceDefinitions.find( + (sourceDefinition) => + sourceDefinition.sourceDefinitionId === source.sourceDefinitionId + ); + + if (sourceDestination) { + sourceDefinitionMap.set(source?.sourceDefinitionId, sourceDestination); + } + }); + + return Array.from(sourceDefinitionMap.values()); + }, [sources, sourceDefinitions]); + + const [{ loading, error }, onUpdate] = useAsyncFn(async () => { + setIsUpdateSucces(false); + await updateAllSourceVersions(); + setIsUpdateSucces(true); + setTimeout(() => { + setIsUpdateSucces(false); + }, 2000); + }, [updateAllSourceVersions]); + + return ( + + ); +}; + +export default SourcesPage; diff --git a/airbyte-webapp/src/pages/AdminPage/components/ConnectorCell.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ConnectorCell.tsx similarity index 100% rename from airbyte-webapp/src/pages/AdminPage/components/ConnectorCell.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ConnectorCell.tsx diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ConnectorsView.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ConnectorsView.tsx new file mode 100644 index 000000000000..bad731ee1aba --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ConnectorsView.tsx @@ -0,0 +1,167 @@ +import React from "react"; +import { FormattedMessage } from "react-intl"; +import { CellProps } from "react-table"; + +import Table from "components/Table"; +import ConnectorCell from "./ConnectorCell"; +import ImageCell from "./ImageCell"; +import VersionCell from "./VersionCell"; +import { Block, FormContentTitle, Title } from "./PageComponents"; +import { SourceDefinition } from "core/resources/SourceDefinition"; +import UpgradeAllButton from "./UpgradeAllButton"; +import CreateConnector from "./CreateConnector"; +import HeadTitle from "components/HeadTitle"; +import { DestinationDefinition } from "core/resources/DestinationDefinition"; + +type ConnectorsViewProps = { + type: "sources" | "destinations"; + isUpdateSuccess: boolean; + hasNewConnectorVersion?: boolean; + onUpdateVersion: ({ id, version }: { id: string; version: string }) => void; + usedConnectorsDefinitions: SourceDefinition[] | DestinationDefinition[]; + connectorsDefinitions: SourceDefinition[] | DestinationDefinition[]; + loading: boolean; + error?: Error; + onUpdate: () => void; + feedbackList: Record; +}; + +const ConnectorsView: React.FC = ({ + type, + onUpdateVersion, + feedbackList, + isUpdateSuccess, + hasNewConnectorVersion, + usedConnectorsDefinitions, + loading, + error, + onUpdate, + connectorsDefinitions, +}) => { + const columns = React.useMemo( + () => [ + { + Header: , + accessor: "name", + customWidth: 25, + Cell: ({ + cell, + row, + }: CellProps<{ + latestDockerImageTag: string; + dockerImageTag: string; + icon?: string; + }>) => ( + + ), + }, + { + Header: , + accessor: "dockerRepository", + customWidth: 36, + Cell: ({ cell, row }: CellProps<{ documentationUrl: string }>) => ( + + ), + }, + { + Header: , + accessor: "dockerImageTag", + customWidth: 10, + }, + { + Header: ( + + + + ), + accessor: "latestDockerImageTag", + collapse: true, + Cell: ({ + cell, + row, + }: CellProps<{ + sourceDefinitionId: string; + dockerImageTag: string; + }>) => ( + + ), + }, + ], + [feedbackList, onUpdateVersion] + ); + + return ( + <> + + {usedConnectorsDefinitions.length ? ( + + + <FormattedMessage + id={ + type === "sources" + ? "admin.manageSource" + : "admin.manageDestination" + } + /> + <div> + <CreateConnector type={type} /> + {(hasNewConnectorVersion || isUpdateSuccess) && ( + <UpgradeAllButton + isLoading={loading} + hasError={!!error && !loading} + hasSuccess={isUpdateSuccess} + onUpdate={onUpdate} + /> + )} + </div> + +
    + + ) : null} + + + + <FormattedMessage + id={ + type === "sources" + ? "admin.availableSource" + : "admin.availableDestinations" + } + /> + {(hasNewConnectorVersion || isUpdateSuccess) && + !usedConnectorsDefinitions.length && ( + <UpgradeAllButton + isLoading={loading} + hasError={!!error && !loading} + hasSuccess={isUpdateSuccess} + onUpdate={onUpdate} + /> + )} + +
    + + + ); +}; + +export default ConnectorsView; diff --git a/airbyte-webapp/src/pages/AdminPage/components/CreateConnector.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx similarity index 98% rename from airbyte-webapp/src/pages/AdminPage/components/CreateConnector.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx index a2deee61b195..6d0dff9cc740 100644 --- a/airbyte-webapp/src/pages/AdminPage/components/CreateConnector.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnector.tsx @@ -7,7 +7,7 @@ import CreateConnectorModal from "./CreateConnectorModal"; import SourceDefinitionResource from "core/resources/SourceDefinition"; import config from "config"; import useRouter from "components/hooks/useRouterHook"; -import { Routes } from "../../routes"; +import { Routes } from "pages/routes"; import DestinationDefinitionResource from "core/resources/DestinationDefinition"; type IProps = { diff --git a/airbyte-webapp/src/pages/AdminPage/components/CreateConnectorModal.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnectorModal.tsx similarity index 100% rename from airbyte-webapp/src/pages/AdminPage/components/CreateConnectorModal.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/CreateConnectorModal.tsx diff --git a/airbyte-webapp/src/pages/AdminPage/components/ImageCell.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ImageCell.tsx similarity index 100% rename from airbyte-webapp/src/pages/AdminPage/components/ImageCell.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ImageCell.tsx diff --git a/airbyte-webapp/src/pages/AdminPage/components/PageComponents.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/PageComponents.tsx similarity index 100% rename from airbyte-webapp/src/pages/AdminPage/components/PageComponents.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/PageComponents.tsx diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/UpgradeAllButton.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/UpgradeAllButton.tsx new file mode 100644 index 000000000000..267315602f5f --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/UpgradeAllButton.tsx @@ -0,0 +1,74 @@ +import React from "react"; +import { FormattedMessage } from "react-intl"; +import styled from "styled-components"; +import { faRedoAlt } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { LoadingButton } from "components"; + +const UpdateButton = styled(LoadingButton)` + margin: -6px 0; + min-width: 120px; +`; + +const TryArrow = styled(FontAwesomeIcon)` + margin: 0 10px -1px 0; + font-size: 14px; +`; + +const UpdateButtonContent = styled.div` + position: relative; + display: inline-block; + margin-left: 5px; +`; + +const ErrorBlock = styled.div` + color: ${({ theme }) => theme.dangerColor}; + font-size: 11px; + position: absolute; + font-weight: normal; + bottom: -17px; + line-height: 11px; + right: 0; + left: -46px; +`; + +type UpdateAllButtonProps = { + onUpdate: () => void; + isLoading: boolean; + hasError: boolean; + hasSuccess: boolean; +}; + +const UpgradeAllButton: React.FC = ({ + onUpdate, + isLoading, + hasError, + hasSuccess, +}) => { + return ( + + {hasError && ( + + + + )} + + {hasSuccess ? ( + + ) : ( + <> + + + + )} + + + ); +}; + +export default UpgradeAllButton; diff --git a/airbyte-webapp/src/pages/AdminPage/components/VersionCell.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/VersionCell.tsx similarity index 100% rename from airbyte-webapp/src/pages/AdminPage/components/VersionCell.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/VersionCell.tsx diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/index.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/index.tsx new file mode 100644 index 000000000000..7ca619a0a450 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/index.tsx @@ -0,0 +1,4 @@ +import SourcesPage from "./SourcesPage"; +import DestinationsPage from "./DestinationsPage"; + +export { SourcesPage, DestinationsPage }; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx index 0b7188363c37..ef100b769dc2 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx @@ -6,6 +6,7 @@ import { ContentCard } from "components"; import { PreferencesForm } from "views/Settings/PreferencesForm"; import useWorkspace from "components/hooks/services/useWorkspaceHook"; import WebHookForm from "./components/WebHookForm"; +import HeadTitle from "../../../../components/HeadTitle"; const SettingsCard = styled(ContentCard)` max-width: 638px; @@ -85,6 +86,9 @@ const NotificationPage: React.FC = () => { return ( <> + }> { +const SourcesPage: React.FC = () => { const [successUpdate, setSuccessUpdate] = useState(false); const formatMessage = useIntl().formatMessage; const { sources } = useResource(SourceResource.listShape(), { @@ -192,4 +192,4 @@ const SourcesView: React.FC = () => { ); }; -export default SourcesView; +export default SourcesPage; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/ConnectorCell.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/ConnectorCell.tsx new file mode 100644 index 000000000000..38867f26734e --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/ConnectorCell.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import styled from "styled-components"; +import Indicator from "components/Indicator"; +import { getIcon } from "utils/imageUtils"; + +type IProps = { + connectorName: string; + img?: string; + hasUpdate?: boolean; +}; + +const Content = styled.div<{ enabled?: boolean }>` + display: flex; + align-items: center; + padding-left: 30px; + position: relative; +`; + +const Image = styled.div` + height: 17px; + width: 17px; + margin-right: 9px; +`; + +const Notification = styled(Indicator)` + position: absolute; + left: 8px; +`; + +const ConnectorCell: React.FC = ({ connectorName, img, hasUpdate }) => { + return ( + + {hasUpdate && } + {getIcon(img)} + {connectorName} + + ); +}; + +export default ConnectorCell; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/ImageCell.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/ImageCell.tsx new file mode 100644 index 000000000000..a9cc0e5be690 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/ImageCell.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import styled from "styled-components"; + +type IProps = { + imageName: string; + link: string; +}; + +const Link = styled.a` + height: 17px; + margin-right: 9px; + color: ${({ theme }) => theme.darkPrimaryColor}; + + &:hover, + &:active { + color: ${({ theme }) => theme.primaryColor}; + } +`; + +const ImageCell: React.FC = ({ imageName, link }) => { + return ( + + {imageName} + + ); +}; + +export default ImageCell; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/PageComponents.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/PageComponents.tsx new file mode 100644 index 000000000000..171a9dc339e8 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/PageComponents.tsx @@ -0,0 +1,26 @@ +import styled from "styled-components"; +import { H5 } from "components"; + +const Title = styled(H5)` + color: ${({ theme }) => theme.darkPrimaryColor}; + margin-bottom: 19px; + display: flex; + justify-content: space-between; + align-items: center; +`; + +const Block = styled.div` + margin-bottom: 56px; +`; + +const FormContent = styled.div` + width: 253px; + margin: -10px 0 -10px 200px; + position: relative; +`; + +const FormContentTitle = styled(FormContent)` + margin: 0 0 0 200px; +`; + +export { Title, Block, FormContent, FormContentTitle }; diff --git a/airbyte-webapp/src/pages/AdminPage/components/UpgradeAllButton.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/UpgradeAllButton.tsx similarity index 100% rename from airbyte-webapp/src/pages/AdminPage/components/UpgradeAllButton.tsx rename to airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/UpgradeAllButton.tsx diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/VersionCell.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/VersionCell.tsx new file mode 100644 index 000000000000..b4401fded2d2 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/components/VersionCell.tsx @@ -0,0 +1,138 @@ +import React from "react"; +import { Formik, Form, FieldProps, Field } from "formik"; +import styled from "styled-components"; +import { FormattedMessage, useIntl } from "react-intl"; + +import { Input, Button, Spinner } from "components"; +import { FormContent } from "./PageComponents"; + +type IProps = { + version: string; + currentVersion: string; + id: string; + onChange: ({ version, id }: { version: string; id: string }) => void; + feedback?: "success" | string; +}; + +const VersionInput = styled(Input)` + max-width: 145px; + margin-right: 19px; +`; + +const InputField = styled.div<{ showNote?: boolean }>` + display: inline-block; + position: relative; + background: ${({ theme }) => theme.whiteColor}; + + &:before { + position: absolute; + display: ${({ showNote }) => (showNote ? "block" : "none")}; + content: attr(data-before); + color: ${({ theme }) => theme.greyColor40}; + top: 10px; + right: 22px; + z-index: 3; + } + &:focus-within:before { + display: none; + } +`; + +const SuccessMessage = styled.div` + color: ${({ theme }) => theme.successColor}; + font-size: 12px; + line-height: 18px; + position: absolute; + text-align: right; + width: 205px; + left: -208px; + height: 100%; + display: flex; + align-items: center; + justify-content: flex-end; + white-space: break-spaces; +`; + +const ErrorMessage = styled(SuccessMessage)` + color: ${({ theme }) => theme.dangerColor}; + font-size: 11px; + line-height: 14px; +`; + +const VersionCell: React.FC = ({ + version, + id, + onChange, + feedback, + currentVersion, +}) => { + const formatMessage = useIntl().formatMessage; + + const renderFeedback = ( + dirty: boolean, + isSubmitting: boolean, + feedback?: string + ) => { + if (isSubmitting) { + return ( + + + + ); + } + + if (feedback && !dirty) { + if (feedback === "success") { + return ( + + + + ); + } else { + return {feedback}; + } + } + + return null; + }; + + return ( + + { + await onChange({ id, version: values.version }); + setSubmitting(false); + }} + > + {({ isSubmitting, dirty }) => ( +
    + {renderFeedback(dirty, isSubmitting, feedback)} + + {({ field }: FieldProps) => ( + + + + )} + + + + )} +
    +
    + ); +}; + +export default VersionCell; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/index.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/index.tsx new file mode 100644 index 000000000000..a903a2946ea5 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/SourcesPage/index.tsx @@ -0,0 +1,3 @@ +import SourcesPage from "./SourcesPage"; + +export default SourcesPage; diff --git a/airbyte-webapp/src/pages/routes.tsx b/airbyte-webapp/src/pages/routes.tsx index 80605cf6cff0..8eb084518618 100644 --- a/airbyte-webapp/src/pages/routes.tsx +++ b/airbyte-webapp/src/pages/routes.tsx @@ -14,7 +14,6 @@ import DestinationPage from "./DestinationPage"; import PreferencesPage from "./PreferencesPage"; import OnboardingPage from "./OnboardingPage"; import ConnectionPage from "./ConnectionPage"; -import AdminPage from "./AdminPage"; import SettingsPage from "./SettingsPage"; import LoadingPage from "components/LoadingPage"; import MainView from "components/MainView"; @@ -39,7 +38,6 @@ export enum Routes { ConnectionNew = "/new-connection", SourceNew = "/new-source", DestinationNew = "/new-destination", - Admin = "/admin", Settings = "/settings", Configuration = "/configuration", Notifications = "/notifications", @@ -81,11 +79,20 @@ const getPageName = (pathname: string) => { if (pathname.match(itemSourcePageRegex)) { return "Source Item Page"; } - if (pathname === Routes.Admin) { - return "Admin Page"; + if (pathname === `${Routes.Settings}${Routes.Source}`) { + return "Settings Sources Connectors Page"; } - if (pathname === Routes.Settings) { - return "Settings Page"; + if (pathname === `${Routes.Settings}${Routes.Destination}`) { + return "Settings Destinations Connectors Page"; + } + if (pathname === `${Routes.Settings}${Routes.Configuration}`) { + return "Settings Configuration Page"; + } + if (pathname === `${Routes.Settings}${Routes.Notifications}`) { + return "Settings Notifications Page"; + } + if (pathname === `${Routes.Settings}${Routes.Metrics}`) { + return "Settings Metrics Page"; } if (pathname === Routes.Connections) { return "Connections Page"; @@ -113,9 +120,6 @@ const MainViewRoutes = () => { - - - From 1c11bd6ae1eea8cd45d622b40975fa654781dff6 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Fri, 2 Jul 2021 13:23:32 +0300 Subject: [PATCH 06/11] Add MetricsPage --- airbyte-webapp/src/locales/en.json | 2 + .../src/pages/SettingsPage/SettingsPage.tsx | 3 +- .../pages/MetricsPage/MetricsPage.tsx | 63 +++++++++ .../MetricsPage/components/MetricsForm.tsx | 128 ++++++++++++++++++ .../SettingsPage/pages/MetricsPage/index.tsx | 3 + 5 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/index.tsx diff --git a/airbyte-webapp/src/locales/en.json b/airbyte-webapp/src/locales/en.json index d2873b582f64..733a907df21a 100644 --- a/airbyte-webapp/src/locales/en.json +++ b/airbyte-webapp/src/locales/en.json @@ -324,6 +324,8 @@ "settings.test": "Test", "settings.notifications": "Notifications", "settings.metrics": "Metrics", + "settings.notificationSettings": "Notification Settings", + "settings.metricsSettings": "Metrics Settings", "connector.requestConnectorBlock": "+ Request a new connector", "connector.requestConnector": "Request a new connector", diff --git a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx index 98f5c4356aae..97d252b4ccab 100644 --- a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx @@ -12,6 +12,7 @@ import { Routes } from "pages/routes"; import useRouter from "components/hooks/useRouterHook"; import NotificationPage from "./pages/NotificationPage"; import ConfigurationsPage from "./pages/ConfigurationsPage"; +import MetricsPage from "./pages/MetricsPage"; import { DestinationsPage, SourcesPage } from "./pages/ConnectorsPage"; const Content = styled.div` @@ -86,7 +87,7 @@ const SettingsPage: React.FC = () => { -
    Metrics
    +
    diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx new file mode 100644 index 000000000000..06980fcc4904 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx @@ -0,0 +1,63 @@ +import React, { useState } from "react"; +import { FormattedMessage } from "react-intl"; +import styled from "styled-components"; + +import { ContentCard } from "components"; +import useWorkspace from "components/hooks/services/useWorkspaceHook"; +import HeadTitle from "components/HeadTitle"; +import MetricsForm from "./components/MetricsForm"; + +const SettingsCard = styled(ContentCard)` + max-width: 638px; + width: 100%; + margin-top: 12px; + + &:first-child { + margin-top: 0; + } +`; + +const Content = styled.div` + padding: 27px 26px 15px; +`; + +const MetricsPage: React.FC = () => { + const { workspace, updatePreferences } = useWorkspace(); + const [errorMessage, setErrorMessage] = useState(null); + const [successMessage, setSuccessMessage] = useState(null); + + const onSubmit = async (data: { anonymousDataCollection: boolean }) => { + setErrorMessage(null); + setSuccessMessage(null); + try { + await updatePreferences({ + anonymousDataCollection: data.anonymousDataCollection, + news: workspace.news, + securityUpdates: workspace.securityUpdates, + }); + setSuccessMessage(); + } catch (e) { + setErrorMessage(); + } + }; + + return ( + <> + + }> + + + + + + ); +}; + +export default MetricsPage; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx new file mode 100644 index 000000000000..542ba53d96dc --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx @@ -0,0 +1,128 @@ +import React from "react"; +import styled from "styled-components"; +import { FormattedMessage } from "react-intl"; +import { Field, FieldProps, Form, Formik } from "formik"; + +import Label from "components/Label"; +import LabeledToggle from "components/LabeledToggle"; +import config from "config"; +import Spinner from "components/Spinner"; + +export type MetricsFormProps = { + onSubmit: (data: { anonymousDataCollection: boolean }) => void; + anonymousDataCollection?: boolean; + successMessage?: React.ReactNode; + errorMessage?: React.ReactNode; +}; + +const FormItem = styled.div` + display: flex; + flex-direction: row; + align-items: center; + min-height: 33px; + margin-bottom: 10px; +`; + +const DocsLink = styled.a` + text-decoration: none; + color: ${({ theme }) => theme.primaryColor}; + cursor: pointer; +`; + +const Subtitle = styled(Label)` + padding-bottom: 9px; +`; + +const Text = styled.div` + font-style: normal; + font-weight: normal; + font-size: 13px; + line-height: 150%; + padding-bottom: 9px; +`; + +const SuccessBlock = styled.div` + margin-left: 10px; + color: ${({ theme }) => theme.successColor}; + font-size: 13px; + line-height: 16px; +`; + +const ErrorBlock = styled(SuccessBlock)` + color: ${({ theme }) => theme.dangerColor}; +`; + +const MetricsForm: React.FC = ({ + onSubmit, + anonymousDataCollection, + successMessage, + errorMessage, +}) => { + const feedbackContent = (isSubmitting: boolean) => { + if (isSubmitting) { + return ( + + + + ); + } + + if (errorMessage) { + return {errorMessage}; + } + + if (successMessage) { + return {successMessage}; + } + + return null; + }; + return ( + { + await onSubmit(values); + }} + > + {({ isSubmitting, handleChange, handleSubmit }) => ( +
    + + + + + ( + + {docs} + + ), + }} + /> + + + + {({ field }: FieldProps) => ( + } + onChange={(event) => { + handleChange(event); + handleSubmit(); + }} + /> + )} + + {feedbackContent(isSubmitting)} + + + )} +
    + ); +}; + +export default MetricsForm; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/index.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/index.tsx new file mode 100644 index 000000000000..63be456e7dd0 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/index.tsx @@ -0,0 +1,3 @@ +import MetricsPage from "./MetricsPage"; + +export default MetricsPage; From 332fba58bcc4a61f55acf194f93884590a4cbd35 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Sat, 3 Jul 2021 00:15:33 +0300 Subject: [PATCH 07/11] Edit Notifications and Metrics pages --- airbyte-webapp/src/locales/en.json | 3 + .../SettingsPage/components/FeedbackBlock.tsx | 49 ++++++++++ .../MetricsPage/components/MetricsForm.tsx | 38 ++------ .../NotificationPage/NotificationPage.tsx | 25 +++-- .../components/NotificationsForm.tsx | 93 +++++++++++++++++++ .../components/WebHookForm.tsx | 4 +- 6 files changed, 164 insertions(+), 48 deletions(-) create mode 100644 airbyte-webapp/src/pages/SettingsPage/components/FeedbackBlock.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/NotificationsForm.tsx diff --git a/airbyte-webapp/src/locales/en.json b/airbyte-webapp/src/locales/en.json index 733a907df21a..a01db77e5f15 100644 --- a/airbyte-webapp/src/locales/en.json +++ b/airbyte-webapp/src/locales/en.json @@ -326,6 +326,9 @@ "settings.metrics": "Metrics", "settings.notificationSettings": "Notification Settings", "settings.metricsSettings": "Metrics Settings", + "settings.emailNotifications": "Email notifications", + "settings.securityUpdates": "Security updates (recommended)", + "settings.newsletter": "Newsletter with feature updates.", "connector.requestConnectorBlock": "+ Request a new connector", "connector.requestConnector": "Request a new connector", diff --git a/airbyte-webapp/src/pages/SettingsPage/components/FeedbackBlock.tsx b/airbyte-webapp/src/pages/SettingsPage/components/FeedbackBlock.tsx new file mode 100644 index 000000000000..aa3448be615a --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/components/FeedbackBlock.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import styled from "styled-components"; + +import Spinner from "components/Spinner"; + +export type FeedbackBlockProps = { + isLoading?: boolean; + successMessage?: React.ReactNode; + errorMessage?: React.ReactNode; +}; + +const SuccessBlock = styled.div` + margin: -10px 10px; + color: ${({ theme }) => theme.successColor}; + font-size: 13px; + line-height: 16px; + display: inline-block; + vertical-align: middle; +`; + +const ErrorBlock = styled(SuccessBlock)` + color: ${({ theme }) => theme.dangerColor}; +`; + +const FeedbackBlock: React.FC = ({ + isLoading, + errorMessage, + successMessage, +}) => { + if (isLoading) { + return ( + + + + ); + } + + if (errorMessage) { + return {errorMessage}; + } + + if (successMessage) { + return {successMessage}; + } + + return null; +}; + +export default FeedbackBlock; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx index 542ba53d96dc..2f3c50d94c71 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx @@ -6,7 +6,7 @@ import { Field, FieldProps, Form, Formik } from "formik"; import Label from "components/Label"; import LabeledToggle from "components/LabeledToggle"; import config from "config"; -import Spinner from "components/Spinner"; +import FeedbackBlock from "../../../components/FeedbackBlock"; export type MetricsFormProps = { onSubmit: (data: { anonymousDataCollection: boolean }) => void; @@ -41,42 +41,12 @@ const Text = styled.div` padding-bottom: 9px; `; -const SuccessBlock = styled.div` - margin-left: 10px; - color: ${({ theme }) => theme.successColor}; - font-size: 13px; - line-height: 16px; -`; - -const ErrorBlock = styled(SuccessBlock)` - color: ${({ theme }) => theme.dangerColor}; -`; - const MetricsForm: React.FC = ({ onSubmit, anonymousDataCollection, successMessage, errorMessage, }) => { - const feedbackContent = (isSubmitting: boolean) => { - if (isSubmitting) { - return ( - - - - ); - } - - if (errorMessage) { - return {errorMessage}; - } - - if (successMessage) { - return {successMessage}; - } - - return null; - }; return ( = ({ /> )} - {feedbackContent(isSubmitting)} + )} diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx index ef100b769dc2..fda771bb94e2 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx @@ -3,10 +3,10 @@ import { FormattedMessage } from "react-intl"; import styled from "styled-components"; import { ContentCard } from "components"; -import { PreferencesForm } from "views/Settings/PreferencesForm"; +import NotificationsForm from "./components/NotificationsForm"; import useWorkspace from "components/hooks/services/useWorkspaceHook"; import WebHookForm from "./components/WebHookForm"; -import HeadTitle from "../../../../components/HeadTitle"; +import HeadTitle from "components/HeadTitle"; const SettingsCard = styled(ContentCard)` max-width: 638px; @@ -41,15 +41,16 @@ const NotificationPage: React.FC = () => { ] = useState(null); const onSubmit = async (data: { - email: string; - anonymousDataCollection: boolean; news: boolean; securityUpdates: boolean; }) => { setErrorMessage(null); setSuccessMessage(null); try { - await updatePreferences(data); + await updatePreferences({ + ...data, + anonymousDataCollection: workspace.anonymousDataCollection, + }); setSuccessMessage(); } catch (e) { setErrorMessage(); @@ -89,7 +90,9 @@ const NotificationPage: React.FC = () => { - }> + } + > { errorMessage={errorWebhookMessage} successMessage={successWebhookMessage} /> - - - }> - - void; + preferencesValues: { + news: boolean; + securityUpdates: boolean; + }; + successMessage?: React.ReactNode; + errorMessage?: React.ReactNode; +}; + +const FormItem = styled.div` + margin-bottom: 10px; +`; + +const Subtitle = styled(Label)` + padding-bottom: 9px; +`; + +const NotificationsForm: React.FC = ({ + onSubmit, + preferencesValues, + successMessage, + errorMessage, +}) => { + return ( + { + await onSubmit(values); + }} + > + {({ isSubmitting, handleChange, handleSubmit }) => ( +
    + + + + + + + {({ field }: FieldProps) => ( + } + onChange={(event) => { + handleChange(event); + handleSubmit(); + }} + /> + )} + + + + + + {({ field }: FieldProps) => ( + } + onChange={(event) => { + handleChange(event); + handleSubmit(); + }} + /> + )} + + + + )} +
    + ); +}; + +export default NotificationsForm; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx index 1707301344da..2b6791ecdd56 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx @@ -17,11 +17,11 @@ const Text = styled.div` const InputRow = styled(Row)` height: auto; - margin-bottom: 28px; + margin-bottom: 40px; `; const Message = styled(Text)` - margin: -19px 0 0; + margin: -40px 0 21px; padding: 0; color: ${({ theme }) => theme.greyColor40}; `; From 82aff4c75ecb7f5a5b2c6c080cedc7aace2a9234 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Sat, 3 Jul 2021 00:53:10 +0300 Subject: [PATCH 08/11] Update feedback for metrics and notification pages --- .../hooks/services/useWorkspaceHook.tsx | 1 + .../components/useWorkspaceEditor.tsx | 53 ++++++++++ .../pages/MetricsPage/MetricsPage.tsx | 34 +++---- .../MetricsPage/components/MetricsForm.tsx | 87 +++++++---------- .../NotificationPage/NotificationPage.tsx | 31 +++--- .../components/NotificationsForm.tsx | 97 ++++++++----------- 6 files changed, 156 insertions(+), 147 deletions(-) create mode 100644 airbyte-webapp/src/pages/SettingsPage/components/useWorkspaceEditor.tsx diff --git a/airbyte-webapp/src/components/hooks/services/useWorkspaceHook.tsx b/airbyte-webapp/src/components/hooks/services/useWorkspaceHook.tsx index 1cc7bc446e2d..31d6227fc7a9 100644 --- a/airbyte-webapp/src/components/hooks/services/useWorkspaceHook.tsx +++ b/airbyte-webapp/src/components/hooks/services/useWorkspaceHook.tsx @@ -78,6 +78,7 @@ const useWorkspace = (): { workspaceId: config.ui.workspaceId, initialSetupComplete: workspace.initialSetupComplete, displaySetupWizard: workspace.displaySetupWizard, + notifications: workspace.notifications, ...data, } ); diff --git a/airbyte-webapp/src/pages/SettingsPage/components/useWorkspaceEditor.tsx b/airbyte-webapp/src/pages/SettingsPage/components/useWorkspaceEditor.tsx new file mode 100644 index 000000000000..f93ee14fc774 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/components/useWorkspaceEditor.tsx @@ -0,0 +1,53 @@ +import React, { useState } from "react"; +import useWorkspace from "../../../components/hooks/services/useWorkspaceHook"; +import { FormattedMessage } from "react-intl"; +import { useAsyncFn } from "react-use"; + +const useWorkspaceEditor = (): { + updateData: (data: { + email?: string; + anonymousDataCollection: boolean; + news: boolean; + securityUpdates: boolean; + }) => Promise; + errorMessage: React.ReactNode; + successMessage: React.ReactNode; + loading?: boolean; +} => { + const { updatePreferences } = useWorkspace(); + const [errorMessage, setErrorMessage] = useState(null); + const [successMessage, setSuccessMessage] = useState(null); + + const [{ loading }, updateData] = useAsyncFn( + async (data: { + news: boolean; + securityUpdates: boolean; + anonymousDataCollection: boolean; + email?: string; + }) => { + setErrorMessage(null); + setSuccessMessage(null); + try { + await updatePreferences({ + email: data.email, + anonymousDataCollection: data.anonymousDataCollection, + news: data.news, + securityUpdates: data.securityUpdates, + }); + setSuccessMessage(); + } catch (e) { + setErrorMessage(); + } + }, + [setErrorMessage, setSuccessMessage] + ); + + return { + updateData, + errorMessage, + successMessage, + loading, + }; +}; + +export default useWorkspaceEditor; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx index 06980fcc4904..e3d38b2bd51e 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/MetricsPage.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; import { FormattedMessage } from "react-intl"; import styled from "styled-components"; @@ -6,6 +6,7 @@ import { ContentCard } from "components"; import useWorkspace from "components/hooks/services/useWorkspaceHook"; import HeadTitle from "components/HeadTitle"; import MetricsForm from "./components/MetricsForm"; +import useWorkspaceEditor from "../../components/useWorkspaceEditor"; const SettingsCard = styled(ContentCard)` max-width: 638px; @@ -22,23 +23,17 @@ const Content = styled.div` `; const MetricsPage: React.FC = () => { - const { workspace, updatePreferences } = useWorkspace(); - const [errorMessage, setErrorMessage] = useState(null); - const [successMessage, setSuccessMessage] = useState(null); - - const onSubmit = async (data: { anonymousDataCollection: boolean }) => { - setErrorMessage(null); - setSuccessMessage(null); - try { - await updatePreferences({ - anonymousDataCollection: data.anonymousDataCollection, - news: workspace.news, - securityUpdates: workspace.securityUpdates, - }); - setSuccessMessage(); - } catch (e) { - setErrorMessage(); - } + const { workspace } = useWorkspace(); + + const { + errorMessage, + successMessage, + loading, + updateData, + } = useWorkspaceEditor(); + + const onChange = async (data: { anonymousDataCollection: boolean }) => { + await updateData({ ...workspace, ...data }); }; return ( @@ -49,10 +44,11 @@ const MetricsPage: React.FC = () => { }> diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx index 2f3c50d94c71..5e1daa08c9af 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/MetricsPage/components/MetricsForm.tsx @@ -1,7 +1,6 @@ import React from "react"; import styled from "styled-components"; import { FormattedMessage } from "react-intl"; -import { Field, FieldProps, Form, Formik } from "formik"; import Label from "components/Label"; import LabeledToggle from "components/LabeledToggle"; @@ -9,10 +8,11 @@ import config from "config"; import FeedbackBlock from "../../../components/FeedbackBlock"; export type MetricsFormProps = { - onSubmit: (data: { anonymousDataCollection: boolean }) => void; + onChange: (data: { anonymousDataCollection: boolean }) => void; anonymousDataCollection?: boolean; successMessage?: React.ReactNode; errorMessage?: React.ReactNode; + isLoading?: boolean; }; const FormItem = styled.div` @@ -42,60 +42,45 @@ const Text = styled.div` `; const MetricsForm: React.FC = ({ - onSubmit, + onChange, anonymousDataCollection, successMessage, errorMessage, + isLoading, }) => { return ( - { - await onSubmit(values); - }} - > - {({ isSubmitting, handleChange, handleSubmit }) => ( -
    - - - - - ( - - {docs} - - ), - }} - /> - - - - {({ field }: FieldProps) => ( - } - onChange={(event) => { - handleChange(event); - handleSubmit(); - }} - /> - )} - - - - - )} -
    + <> + + + + + ( + + {docs} + + ), + }} + /> + + + } + onChange={(event) => { + onChange({ anonymousDataCollection: event.target.checked }); + }} + /> + + + ); }; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx index fda771bb94e2..69dca44e7c8e 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx @@ -7,6 +7,7 @@ import NotificationsForm from "./components/NotificationsForm"; import useWorkspace from "components/hooks/services/useWorkspaceHook"; import WebHookForm from "./components/WebHookForm"; import HeadTitle from "components/HeadTitle"; +import useWorkspaceEditor from "../../components/useWorkspaceEditor"; const SettingsCard = styled(ContentCard)` max-width: 638px; @@ -23,14 +24,13 @@ const Content = styled.div` `; const NotificationPage: React.FC = () => { + const { workspace, updateWebhook, testWebhook } = useWorkspace(); const { - workspace, - updatePreferences, - updateWebhook, - testWebhook, - } = useWorkspace(); - const [errorMessage, setErrorMessage] = useState(null); - const [successMessage, setSuccessMessage] = useState(null); + errorMessage, + successMessage, + loading, + updateData, + } = useWorkspaceEditor(); const [ errorWebhookMessage, setErrorWebhookMessage, @@ -40,21 +40,11 @@ const NotificationPage: React.FC = () => { setSuccessWebhookMessage, ] = useState(null); - const onSubmit = async (data: { + const onChange = async (data: { news: boolean; securityUpdates: boolean; }) => { - setErrorMessage(null); - setSuccessMessage(null); - try { - await updatePreferences({ - ...data, - anonymousDataCollection: workspace.anonymousDataCollection, - }); - setSuccessMessage(); - } catch (e) { - setErrorMessage(); - } + await updateData({ ...workspace, ...data }); }; const onSubmitWebhook = async (data: { webhook: string }) => { @@ -103,9 +93,10 @@ const NotificationPage: React.FC = () => { /> void; + onChange: (data: { news: boolean; securityUpdates: boolean }) => void; preferencesValues: { news: boolean; securityUpdates: boolean; }; successMessage?: React.ReactNode; errorMessage?: React.ReactNode; + isLoading?: boolean; }; const FormItem = styled.div` @@ -26,67 +26,50 @@ const Subtitle = styled(Label)` `; const NotificationsForm: React.FC = ({ - onSubmit, + onChange, preferencesValues, successMessage, errorMessage, + isLoading, }) => { return ( - { - await onSubmit(values); - }} - > - {({ isSubmitting, handleChange, handleSubmit }) => ( -
    - - - - - - - {({ field }: FieldProps) => ( - } - onChange={(event) => { - handleChange(event); - handleSubmit(); - }} - /> - )} - - + <> + + + + + + } + onChange={(event) => { + onChange({ + securityUpdates: event.target.checked, + news: preferencesValues.news, + }); + }} + /> + - - - {({ field }: FieldProps) => ( - } - onChange={(event) => { - handleChange(event); - handleSubmit(); - }} - /> - )} - - - - )} -
    + + } + onChange={(event) => { + onChange({ + news: event.target.checked, + securityUpdates: preferencesValues.securityUpdates, + }); + }} + /> + + ); }; From 21f024026a5de5b97b52228850119fc1f0727b98 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Sat, 3 Jul 2021 01:03:10 +0300 Subject: [PATCH 09/11] Add update icons data to side menu --- .../SideMenu/components/MenuItem.tsx | 2 +- .../hooks/services/useConnector.tsx | 21 +++++++++++++++++++ .../src/pages/SettingsPage/SettingsPage.tsx | 4 ++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/airbyte-webapp/src/components/SideMenu/components/MenuItem.tsx b/airbyte-webapp/src/components/SideMenu/components/MenuItem.tsx index 9d081637e16b..583035d63142 100644 --- a/airbyte-webapp/src/components/SideMenu/components/MenuItem.tsx +++ b/airbyte-webapp/src/components/SideMenu/components/MenuItem.tsx @@ -33,7 +33,7 @@ const Counter = styled.div` border-radius: 15px; background: ${({ theme }) => theme.dangerColor}; font-size: 8px; - line-height: 12px; + line-height: 13px; color: ${({ theme }) => theme.whiteColor}; display: inline-block; margin-left: 5px; diff --git a/airbyte-webapp/src/components/hooks/services/useConnector.tsx b/airbyte-webapp/src/components/hooks/services/useConnector.tsx index 2a77f39c03c5..5b60b4f9aa76 100644 --- a/airbyte-webapp/src/components/hooks/services/useConnector.tsx +++ b/airbyte-webapp/src/components/hooks/services/useConnector.tsx @@ -9,6 +9,8 @@ type ConnectorService = { hasNewVersions: boolean; hasNewSourceVersion: boolean; hasNewDestinationVersion: boolean; + countNewSourceVersion: number; + countNewDestinationVersion: number; updateAllSourceVersions: () => void; updateAllDestinationVersions: () => void; }; @@ -57,6 +59,23 @@ const useConnector = (): ConnectorService => { [hasNewSourceVersion, hasNewDestinationVersion] ); + const countNewSourceVersion = useMemo( + () => + sourceDefinitions.filter( + (source) => source.latestDockerImageTag !== source.dockerImageTag + ).length, + [sourceDefinitions] + ); + + const countNewDestinationVersion = useMemo( + () => + destinationDefinitions.filter( + (destination) => + destination.latestDockerImageTag !== destination.dockerImageTag + ).length, + [destinationDefinitions] + ); + const updateAllSourceVersions = async () => { const updateList = sourceDefinitions.filter( (source) => source.latestDockerImageTag !== source.dockerImageTag @@ -100,6 +119,8 @@ const useConnector = (): ConnectorService => { hasNewDestinationVersion, updateAllSourceVersions, updateAllDestinationVersions, + countNewSourceVersion, + countNewDestinationVersion, }; }; diff --git a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx index 97d252b4ccab..558d0fb7f4f7 100644 --- a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx @@ -3,6 +3,7 @@ import { FormattedMessage } from "react-intl"; import styled from "styled-components"; import { Redirect, Route, Switch } from "react-router"; +import useConnector from "components/hooks/services/useConnector"; import MainPageWithScroll from "components/MainPageWithScroll"; import PageTitle from "components/PageTitle"; import LoadingPage from "components/LoadingPage"; @@ -28,15 +29,18 @@ const MainView = styled.div` const SettingsPage: React.FC = () => { const { push, pathname } = useRouter(); + const { countNewSourceVersion, countNewDestinationVersion } = useConnector(); const menuItems = [ { id: `${Routes.Settings}${Routes.Source}`, name: , + indicatorCount: countNewSourceVersion, }, { id: `${Routes.Settings}${Routes.Destination}`, name: , + indicatorCount: countNewDestinationVersion, }, { id: `${Routes.Settings}${Routes.Configuration}`, From 8c4e6cef2f357549c7f39bdfcaaef0e0c3e46721 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Sat, 3 Jul 2021 01:50:06 +0300 Subject: [PATCH 10/11] Fix small css errors --- .../pages/ConnectorsPage/components/ConnectorCell.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ConnectorCell.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ConnectorCell.tsx index 38867f26734e..d29a20871a18 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ConnectorCell.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/ConnectorsPage/components/ConnectorCell.tsx @@ -14,10 +14,12 @@ const Content = styled.div<{ enabled?: boolean }>` align-items: center; padding-left: 30px; position: relative; + margin: -5px 0; + min-width: 290px; `; const Image = styled.div` - height: 17px; + height: 25px; width: 17px; margin-right: 9px; `; From 3d3cd01d1b3dbcb94f95d826b42060f05fa12190 Mon Sep 17 00:00:00 2001 From: Artem Astapenko Date: Sat, 3 Jul 2021 14:33:28 +0300 Subject: [PATCH 11/11] Add AccountPage --- airbyte-webapp/src/locales/en.json | 1 + .../src/pages/SettingsPage/SettingsPage.tsx | 10 +- .../pages/AccountPage/AccountPage.tsx | 58 +++++++++ .../AccountPage/components/AccountForm.tsx | 112 ++++++++++++++++++ .../SettingsPage/pages/AccountPage/index.tsx | 3 + airbyte-webapp/src/pages/routes.tsx | 1 + .../src/views/layout/SideBar/SideBar.tsx | 2 +- 7 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/AccountPage.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/components/AccountForm.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/index.tsx diff --git a/airbyte-webapp/src/locales/en.json b/airbyte-webapp/src/locales/en.json index a01db77e5f15..e77818b7f765 100644 --- a/airbyte-webapp/src/locales/en.json +++ b/airbyte-webapp/src/locales/en.json @@ -329,6 +329,7 @@ "settings.emailNotifications": "Email notifications", "settings.securityUpdates": "Security updates (recommended)", "settings.newsletter": "Newsletter with feature updates.", + "settings.account": "Account", "connector.requestConnectorBlock": "+ Request a new connector", "connector.requestConnector": "Request a new connector", diff --git a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx index 558d0fb7f4f7..e3a2587bf96b 100644 --- a/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/SettingsPage.tsx @@ -14,6 +14,7 @@ import useRouter from "components/hooks/useRouterHook"; import NotificationPage from "./pages/NotificationPage"; import ConfigurationsPage from "./pages/ConfigurationsPage"; import MetricsPage from "./pages/MetricsPage"; +import AccountPage from "./pages/AccountPage"; import { DestinationsPage, SourcesPage } from "./pages/ConnectorsPage"; const Content = styled.div` @@ -32,6 +33,10 @@ const SettingsPage: React.FC = () => { const { countNewSourceVersion, countNewDestinationVersion } = useConnector(); const menuItems = [ + { + id: `${Routes.Settings}${Routes.Account}`, + name: , + }, { id: `${Routes.Settings}${Routes.Source}`, name: , @@ -78,6 +83,9 @@ const SettingsPage: React.FC = () => { }> + + + @@ -94,7 +102,7 @@ const SettingsPage: React.FC = () => { - + diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/AccountPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/AccountPage.tsx new file mode 100644 index 000000000000..155b88641ba7 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/AccountPage.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import { FormattedMessage } from "react-intl"; +import styled from "styled-components"; + +import { ContentCard } from "components"; +import useWorkspace from "components/hooks/services/useWorkspaceHook"; +import HeadTitle from "components/HeadTitle"; +import AccountForm from "./components/AccountForm"; +import useWorkspaceEditor from "../../components/useWorkspaceEditor"; + +const SettingsCard = styled(ContentCard)` + max-width: 638px; + width: 100%; + margin-top: 12px; + + &:first-child { + margin-top: 0; + } +`; + +const Content = styled.div` + padding: 27px 26px 15px; +`; + +const AccountPage: React.FC = () => { + const { workspace } = useWorkspace(); + + const { + errorMessage, + successMessage, + // loading, + updateData, + } = useWorkspaceEditor(); + + const onSubmit = async (data: { email: string }) => { + await updateData({ ...workspace, ...data }); + }; + + return ( + <> + + }> + + + + + + ); +}; + +export default AccountPage; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/components/AccountForm.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/components/AccountForm.tsx new file mode 100644 index 000000000000..db95d819945a --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/components/AccountForm.tsx @@ -0,0 +1,112 @@ +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; +import styled from "styled-components"; +import { Field, FieldProps, Form, Formik } from "formik"; +import * as yup from "yup"; + +import { LoadingButton } from "components"; +import { Row, Cell } from "components/SimpleTableComponents"; +import LabeledInput from "components/LabeledInput"; + +const InputRow = styled(Row)` + height: auto; + margin-bottom: 40px; +`; + +const ButtonCell = styled(Cell)` + &:last-child { + text-align: left; + } + padding-left: 11px; + height: 9px; +`; + +const EmailForm = styled(Form)` + position: relative; +`; + +const Success = styled.div` + font-size: 13px; + color: ${({ theme }) => theme.successColor}; + position: absolute; + bottom: -19px; +`; + +const Error = styled(Success)` + color: ${({ theme }) => theme.dangerColor}; +`; + +const accountValidationSchema = yup.object().shape({ + email: yup.string().email("form.email.error").required("form.empty.error"), +}); + +type AccountFormProps = { + email: string; + successMessage?: React.ReactNode; + errorMessage?: React.ReactNode; + onSubmit: (data: { email: string }) => void; +}; + +const AccountForm: React.FC = ({ + email, + onSubmit, + successMessage, + errorMessage, +}) => { + const formatMessage = useIntl().formatMessage; + + return ( + + {({ isSubmitting, dirty, values }) => ( + + + + + {({ field, meta }: FieldProps) => ( + + ) : ( + "" + ) + } + label={} + /> + )} + + + + + + + + + {!dirty && + (successMessage ? ( + {successMessage} + ) : errorMessage ? ( + {errorMessage} + ) : null)} + + )} + + ); +}; + +export default AccountForm; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/index.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/index.tsx new file mode 100644 index 000000000000..4bf14c2f6ca4 --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/AccountPage/index.tsx @@ -0,0 +1,3 @@ +import AccountPage from "./AccountPage"; + +export default AccountPage; diff --git a/airbyte-webapp/src/pages/routes.tsx b/airbyte-webapp/src/pages/routes.tsx index 8eb084518618..f1a13fbb07da 100644 --- a/airbyte-webapp/src/pages/routes.tsx +++ b/airbyte-webapp/src/pages/routes.tsx @@ -42,6 +42,7 @@ export enum Routes { Configuration = "/configuration", Notifications = "/notifications", Metrics = "/metrics", + Account = "/account", Root = "/", } diff --git a/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx b/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx index fcccb7e18f43..af0ba816d6bf 100644 --- a/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx +++ b/airbyte-webapp/src/views/layout/SideBar/SideBar.tsx @@ -144,7 +144,7 @@ const SideBar: React.FC = () => {
  • location.pathname.startsWith(Routes.Settings)