diff --git a/airbyte-webapp/src/components/connection/CatalogTree/CatalogSection.tsx b/airbyte-webapp/src/components/connection/CatalogTree/CatalogSection.tsx index 2e1c03fdeba5..5306123c3085 100644 --- a/airbyte-webapp/src/components/connection/CatalogTree/CatalogSection.tsx +++ b/airbyte-webapp/src/components/connection/CatalogTree/CatalogSection.tsx @@ -18,6 +18,8 @@ import { equal, naturalComparatorBy } from "utils/objects"; import { ConnectionFormValues, SUPPORTED_MODES } from "views/Connection/ConnectionForm/formConfig"; import styles from "./CatalogSection.module.scss"; +import { CatalogTreeTableRow } from "./next/CatalogTreeTableRow"; +import { StreamDetailsPanel } from "./next/StreamDetailsPanel"; import { StreamFieldTable } from "./StreamFieldTable"; import { StreamHeader } from "./StreamHeader"; import { flatten, getPathType } from "./utils"; @@ -41,6 +43,8 @@ const CatalogSectionInner: React.FC = ({ errors, changedSelected, }) => { + const isNewStreamsTableEnabled = process.env.REACT_APP_NEW_STREAMS_TABLE ?? false; + const { destDefinition: { supportedDestinationSyncModes }, } = useConnectionFormService(); @@ -135,9 +139,11 @@ const CatalogSectionInner: React.FC = ({ const hasFields = fields?.length > 0; const disabled = mode === "readonly"; + const StreamComponent = isNewStreamsTableEnabled ? CatalogTreeTableRow : StreamHeader; + return (
- = ({ hasError={hasError} disabled={disabled} /> - {isRowExpanded && hasFields && ( -
- -
- )} + ) : ( +
+ +
+ ))}
); }; diff --git a/airbyte-webapp/src/components/connection/CatalogTree/CatalogTree.tsx b/airbyte-webapp/src/components/connection/CatalogTree/CatalogTree.tsx index b00a71c0cf9e..932a78b16d5f 100644 --- a/airbyte-webapp/src/components/connection/CatalogTree/CatalogTree.tsx +++ b/airbyte-webapp/src/components/connection/CatalogTree/CatalogTree.tsx @@ -12,6 +12,9 @@ import { CatalogTreeBody } from "./CatalogTreeBody"; import { CatalogTreeHeader } from "./CatalogTreeHeader"; import { CatalogTreeSearch } from "./CatalogTreeSearch"; import { CatalogTreeSubheader } from "./CatalogTreeSubheader"; +import { BulkEditPanel } from "./next/BulkEditPanel"; +import { CatalogTreeTableHeader } from "./next/CatalogTreeTableHeader"; +import { StreamConnectionHeader } from "./next/StreamConnectionHeader"; interface CatalogTreeProps { streams: SyncSchemaStream[]; @@ -24,6 +27,7 @@ const CatalogTreeComponent: React.FC> onStreamsChanged, isLoading, }) => { + const isNewStreamsTableEnabled = process.env.REACT_APP_NEW_STREAMS_TABLE ?? false; const { mode } = useConnectionFormService(); const [searchString, setSearchString] = useState(""); @@ -53,11 +57,21 @@ const CatalogTreeComponent: React.FC> {mode !== "readonly" && } - - - + {isNewStreamsTableEnabled ? ( + <> + + + + ) : ( + <> + + + + + )} + {isNewStreamsTableEnabled && } ); }; diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/BulkEditPanel.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/BulkEditPanel.tsx new file mode 100644 index 000000000000..7855697333de --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/BulkEditPanel.tsx @@ -0,0 +1,180 @@ +import { faArrowRight } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import intersection from "lodash/intersection"; +import React, { useMemo } from "react"; +import { createPortal } from "react-dom"; +import { FormattedMessage } from "react-intl"; +import styled from "styled-components"; + +import { Cell, Header } from "components"; +import { Button } from "components/ui/Button"; +import { Switch } from "components/ui/Switch"; + +import { SyncSchemaField, SyncSchemaFieldObject, SyncSchemaStream, traverseSchemaToField } from "core/domain/catalog"; +import { DestinationSyncMode, SyncMode } from "core/request/AirbyteClient"; +import { useBulkEditService } from "hooks/services/BulkEdit/BulkEditService"; +import { useConnectionFormService } from "hooks/services/ConnectionForm/ConnectionFormService"; +import { SUPPORTED_MODES } from "views/Connection/ConnectionForm/formConfig"; + +import { pathDisplayName, PathPopout } from "../PathPopout"; +import { HeaderCell } from "../styles"; +import { SyncSettingsDropdown } from "../SyncSettingsDropdown"; +import { flatten, getPathType } from "../utils"; + +const SchemaHeader = styled(Header)` + position: fixed; + bottom: 0; + left: 122px; + z-index: 1000; + width: calc(100% - 152px); + bottom: 0; + height: unset; + background: ${({ theme }) => theme.primaryColor}; + border-radius: 8px 8px 0 0; +`; + +function calculateSharedFields(selectedBatchNodes: SyncSchemaStream[]) { + const primitiveFieldsByStream = selectedBatchNodes.map(({ stream }) => { + const traversedFields = traverseSchemaToField(stream?.jsonSchema, stream?.name); + const flattenedFields = flatten(traversedFields); + + return flattenedFields.filter(SyncSchemaFieldObject.isPrimitive); + }); + + const pathMap = new Map(); + + // calculate intersection of primitive fields across all selected streams + primitiveFieldsByStream.forEach((fields, index) => { + if (index === 0) { + fields.forEach((field) => pathMap.set(pathDisplayName(field.path), field)); + } else { + const fieldMap = new Set(fields.map((f) => pathDisplayName(f.path))); + pathMap.forEach((_, k) => (!fieldMap.has(k) ? pathMap.delete(k) : null)); + } + }); + + return Array.from(pathMap.values()); +} + +export const BulkEditPanel: React.FC = () => { + const { + destDefinition: { supportedDestinationSyncModes }, + } = useConnectionFormService(); + const { selectedBatchNodes, options, onChangeOption, onApply, isActive, onCancel } = useBulkEditService(); + + const availableSyncModes = useMemo( + () => + SUPPORTED_MODES.filter(([syncMode, destinationSyncMode]) => { + const supportableModes = intersection(selectedBatchNodes.flatMap((n) => n.stream?.supportedSyncModes)); + return supportableModes.includes(syncMode) && supportedDestinationSyncModes?.includes(destinationSyncMode); + }).map(([syncMode, destinationSyncMode]) => ({ + value: { syncMode, destinationSyncMode }, + })), + [selectedBatchNodes, supportedDestinationSyncModes] + ); + + const primitiveFields: SyncSchemaField[] = useMemo( + () => calculateSharedFields(selectedBatchNodes), + [selectedBatchNodes] + ); + + if (!isActive) { + return null; + } + + const pkRequired = options.destinationSyncMode === DestinationSyncMode.append_dedup; + const shouldDefinePk = selectedBatchNodes.every((n) => n.stream?.sourceDefinedPrimaryKey?.length === 0) && pkRequired; + const cursorRequired = options.syncMode === SyncMode.incremental; + const shouldDefineCursor = selectedBatchNodes.every((n) => !n.stream?.sourceDefinedCursor) && cursorRequired; + const numStreamsSelected = selectedBatchNodes.length; + + const pkType = getPathType(pkRequired, shouldDefinePk); + const cursorType = getPathType(cursorRequired, shouldDefineCursor); + + const paths = primitiveFields.map((f) => f.path); + + return createPortal( + + +
{numStreamsSelected}
+ +
+ +
+ +
+ onChangeOption({ selected: !options.selected })} /> +
+ +
+ +
+ onChangeOption({ ...value })} + /> +
+ + {cursorType && ( + <> +
+ +
+ onChangeOption({ cursorField: path })} + pathType={cursorType} + paths={paths} + path={options.cursorField} + /> + + )} +
+ + {pkType && ( + <> +
+ +
+ onChangeOption({ primaryKey: path })} + pathType={pkType} + paths={paths} + path={options.primaryKey} + /> + + )} +
+ + + + +
+ +
+ onChangeOption({ ...value })} + /> +
+ + + + +
, + document.body + ); +}; diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableHeader.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableHeader.tsx new file mode 100644 index 000000000000..2d3f11682aab --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableHeader.tsx @@ -0,0 +1,70 @@ +import { FormattedMessage } from "react-intl"; + +import { Cell, Header } from "components/SimpleTableComponents"; +import { CheckBox } from "components/ui/CheckBox"; +import { InfoTooltip, TooltipLearnMoreLink } from "components/ui/Tooltip"; + +import { useBulkEditService } from "hooks/services/BulkEdit/BulkEditService"; +import { useConnectionFormService } from "hooks/services/ConnectionForm/ConnectionFormService"; +import { links } from "utils/links"; + +export const CatalogTreeTableHeader: React.FC = () => { + const { mode } = useConnectionFormService(); + const { onCheckAll, selectedBatchNodeIds, allChecked } = useBulkEditService(); + + return ( +
+ + {mode !== "readonly" && ( + 0 && !allChecked} + checked={allChecked} + /> + )} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableRow.module.scss b/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableRow.module.scss new file mode 100644 index 000000000000..557907278af4 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableRow.module.scss @@ -0,0 +1,48 @@ +@use "scss/colors"; +@use "scss/variables"; + +.icon { + margin-right: 7px; + margin-top: -1px; + + &.plus { + color: colors.$green; + } + + &.minus { + color: colors.$red; + } +} + +.streamHeaderContent { + background: colors.$white; + border-bottom: 1px solid colors.$grey-50; + + &:hover { + background: colors.$grey-30; + cursor: pointer; + } + + &.disabledChange { + background-color: colors.$red-50; + } + + &.enabledChange { + background-color: colors.$green-50; + } + + &.error { + border: 1px solid colors.$red; + } + + &.selected { + background-color: colors.$blue-transparent; + } +} + +.streamRowCheckboxCell { + display: flex; + flex-direction: row; + justify-content: flex-end; + padding-left: 27px; +} diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableRow.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableRow.tsx new file mode 100644 index 000000000000..c651777b389f --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/CatalogTreeTableRow.tsx @@ -0,0 +1,147 @@ +import { faArrowRight, faMinus, faPlus } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import classnames from "classnames"; +import { useMemo } from "react"; +import { FormattedMessage } from "react-intl"; + +import { Cell, Row } from "components/SimpleTableComponents"; +import { CheckBox } from "components/ui/CheckBox"; +import { Switch } from "components/ui/Switch"; + +import { useBulkEditSelect } from "hooks/services/BulkEdit/BulkEditService"; + +import { PathPopout } from "../PathPopout"; +import { StreamHeaderProps } from "../StreamHeader"; +import { HeaderCell } from "../styles"; +import styles from "./CatalogTreeTableRow.module.scss"; + +export const CatalogTreeTableRow: React.FC = ({ + stream, + destName, + destNamespace, + // onSelectSyncMode, + onSelectStream, + // availableSyncModes, + pkType, + onPrimaryKeyChange, + onCursorChange, + primitiveFields, + cursorType, + // isRowExpanded, + fields, + onExpand, + changedSelected, + hasError, + disabled, +}) => { + const { primaryKey, syncMode, cursorField, destinationSyncMode } = stream.config ?? {}; + const isStreamEnabled = stream.config?.selected; + + const { defaultCursorField } = stream.stream ?? {}; + const syncSchema = useMemo( + () => ({ + syncMode, + destinationSyncMode, + }), + [syncMode, destinationSyncMode] + ); + + const [isSelected, selectForBulkEdit] = useBulkEditSelect(stream.id); + + const paths = useMemo(() => primitiveFields.map((field) => field.path), [primitiveFields]); + const fieldCount = fields?.length ?? 0; + const onRowClick = fieldCount > 0 ? () => onExpand() : undefined; + + const iconStyle = classnames(styles.icon, { + [styles.plus]: isStreamEnabled, + [styles.minus]: !isStreamEnabled, + }); + + const streamHeaderContentStyle = classnames(styles.streamHeaderContent, { + [styles.enabledChange]: changedSelected && isStreamEnabled, + [styles.disabledChange]: changedSelected && !isStreamEnabled, + [styles.selected]: isSelected, + [styles.error]: hasError, + }); + + const checkboxCellCustomStyle = classnames(styles.checkboxCell, styles.streamRowCheckboxCell); + + return ( + + {!disabled && ( +
+ {changedSelected && ( +
+ {isStreamEnabled ? ( + + ) : ( + + )} +
+ )} + +
+ )} + + + + {fieldCount} + + {stream.stream?.namespace || } + + {stream.stream?.name} + + {disabled ? ( + + {syncSchema.syncMode} + + ) : ( + // TODO: Replace with Dropdown/Popout + syncSchema.syncMode + )} + + + {cursorType && ( + } + onPathChange={onCursorChange} + /> + )} + + + {pkType && ( + } + onPathChange={onPrimaryKeyChange} + /> + )} + + + + + + {destNamespace} + + + {destName} + + + {disabled ? ( + + {syncSchema.destinationSyncMode} + + ) : ( + // TODO: Replace with Dropdown/Popout + syncSchema.destinationSyncMode + )} + +
+ ); +}; diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamConnectionHeader.module.scss b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamConnectionHeader.module.scss new file mode 100644 index 000000000000..c2a9085a9b8e --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamConnectionHeader.module.scss @@ -0,0 +1,20 @@ +@use "scss/variables"; + +$icon-size: 15px; + +.container { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: variables.$spacing-lg calc(variables.$spacing-lg * 2); +} + +.connector { + display: flex; + flex-direction: row; +} + +.icon { + height: $icon-size; + width: $icon-size; +} diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamConnectionHeader.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamConnectionHeader.tsx new file mode 100644 index 000000000000..cb65e0ca8f92 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamConnectionHeader.tsx @@ -0,0 +1,27 @@ +import { faArrowRight } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { useConnectionEditService } from "hooks/services/ConnectionEdit/ConnectionEditService"; +import { useDestinationDefinition } from "services/connector/DestinationDefinitionService"; +import { useSourceDefinition } from "services/connector/SourceDefinitionService"; +import { getIcon } from "utils/imageUtils"; + +import styles from "./StreamConnectionHeader.module.scss"; + +const renderIcon = (icon?: string): JSX.Element =>
{getIcon(icon)}
; + +export const StreamConnectionHeader: React.FC = () => { + const { + connection: { source, destination }, + } = useConnectionEditService(); + const sourceDefinition = useSourceDefinition(source.sourceDefinitionId); + const destinationDefinition = useDestinationDefinition(destination.destinationDefinitionId); + + return ( +
+
{renderIcon(sourceDefinition.icon)} Source
+ +
{renderIcon(destinationDefinition.icon)} Destination
+
+ ); +}; diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamDetailsPanel.module.scss b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamDetailsPanel.module.scss new file mode 100644 index 000000000000..706de7384422 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamDetailsPanel.module.scss @@ -0,0 +1,19 @@ +@use "scss/colors"; +@use "scss/variables"; +@use "scss/z-indices"; + +.dialog { + z-index: z-indices.$modal; +} + +.container { + position: fixed; + bottom: 0; + left: variables.$width-size-menu; + z-index: 1000; + width: calc(100% - variables.$width-size-menu); + height: calc(100vh - 100px); + background: colors.$white; + border-radius: 15px 15px 0 0; + box-shadow: 0 0 22px rgba(colors.$dark-blue-900, 12%); +} diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamDetailsPanel.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamDetailsPanel.tsx new file mode 100644 index 000000000000..3697534e3332 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamDetailsPanel.tsx @@ -0,0 +1,54 @@ +import { Dialog } from "@headlessui/react"; + +import { Overlay } from "components/ui/Overlay/Overlay"; + +import { AirbyteStream } from "core/request/AirbyteClient"; + +import { StreamConnectionHeader } from "./StreamConnectionHeader"; +import styles from "./StreamDetailsPanel.module.scss"; +import { StreamFieldsTable, StreamFieldsTableProps } from "./StreamFieldsTable"; +import { StreamPanelHeader } from "./StreamPanelHeader"; + +interface StreamDetailsPanelProps extends StreamFieldsTableProps { + disabled?: boolean; + onClose: () => void; + onSelectedChange: () => void; + stream?: AirbyteStream; +} + +export const StreamDetailsPanel: React.FC = ({ + config, + disabled, + onPkSelect, + onCursorSelect, + onClose, + onSelectedChange, + shouldDefinePk, + shouldDefineCursor, + stream, + syncSchemaFields, +}) => { + return ( + + + + + + + + + ); +}; diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamFieldsTable.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamFieldsTable.tsx new file mode 100644 index 000000000000..52dc5610ca25 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamFieldsTable.tsx @@ -0,0 +1,45 @@ +import { SyncSchemaField, SyncSchemaFieldObject } from "core/domain/catalog"; +import { AirbyteStreamConfiguration } from "core/request/AirbyteClient"; + +import { pathDisplayName } from "../PathPopout"; +import { TreeRowWrapper } from "../TreeRowWrapper"; +import { StreamFieldsTableHeader } from "./StreamFieldsTableHeader"; +import { StreamFieldsTableRow } from "./StreamFieldsTableRow"; + +export interface StreamFieldsTableProps { + config?: AirbyteStreamConfiguration; + onCursorSelect: (cursorPath: string[]) => void; + onPkSelect: (pkPath: string[]) => void; + shouldDefinePk: boolean; + shouldDefineCursor: boolean; + syncSchemaFields: SyncSchemaField[]; +} + +export const StreamFieldsTable: React.FC = ({ + config, + onPkSelect, + onCursorSelect, + shouldDefineCursor, + shouldDefinePk, + syncSchemaFields, +}) => { + return ( + <> + + + + {syncSchemaFields.map((field) => ( + + + + ))} + + ); +}; diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamFieldsTableHeader.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamFieldsTableHeader.tsx new file mode 100644 index 000000000000..7db0ce0f52c8 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamFieldsTableHeader.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import { FormattedMessage } from "react-intl"; + +import { HeaderCell, NameContainer } from "../styles"; + +export const StreamFieldsTableHeader: React.FC = React.memo(() => ( + <> + + + + + + + + + + + + + + + + + + + {/* + In the design, but we may be unable to get the destination data type + + + + */} + +)); diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamFieldsTableRow.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamFieldsTableRow.tsx new file mode 100644 index 000000000000..ea584c08da62 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamFieldsTableRow.tsx @@ -0,0 +1,66 @@ +import { faArrowRight } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React from "react"; + +import { Cell } from "components/SimpleTableComponents"; +import { CheckBox } from "components/ui/CheckBox"; +import { RadioButton } from "components/ui/RadioButton"; + +import { SyncSchemaField } from "core/domain/catalog"; +import { AirbyteStreamConfiguration } from "core/request/AirbyteClient"; +import { equal } from "utils/objects"; +import { useTranslateDataType } from "utils/useTranslateDataType"; + +import DataTypeCell from "../DataTypeCell"; +import { pathDisplayName } from "../PathPopout"; +import { NameContainer } from "../styles"; + +interface StreamFieldsTableRowProps { + isPrimaryKeyEnabled: boolean; + isCursorEnabled: boolean; + + onPrimaryKeyChange: (pk: string[]) => void; + onCursorChange: (cs: string[]) => void; + field: SyncSchemaField; + config: AirbyteStreamConfiguration | undefined; +} + +const StreamFieldsTableRowComponent: React.FC = ({ + onPrimaryKeyChange, + onCursorChange, + field, + config, + isCursorEnabled, + isPrimaryKeyEnabled, +}) => { + const dataType = useTranslateDataType(field); + const name = pathDisplayName(field.path); + + const isCursor = equal(config?.cursorField, field.path); + const isPrimaryKey = !!config?.primaryKey?.some((p) => equal(p, field.path)); + + return ( + <> + + {name} + + {dataType} + {isCursorEnabled && onCursorChange(field.path)} />} + + {isPrimaryKeyEnabled && onPrimaryKeyChange(field.path)} />} + + + + + + {field.cleanedName} + + {/* + In the design, but we may be unable to get the destination data type + {dataType} + */} + + ); +}; + +export const StreamFieldsTableRow = React.memo(StreamFieldsTableRowComponent); diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamPanelHeader.module.scss b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamPanelHeader.module.scss new file mode 100644 index 000000000000..557c6a268c23 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamPanelHeader.module.scss @@ -0,0 +1,21 @@ +@use "scss/colors"; +@use "scss/variables"; + +.container { + border-radius: 10px; + margin: variables.$spacing-md variables.$spacing-lg; + background-color: colors.$grey-50; + display: flex; + flex-direction: row; + justify-content: space-between; + + & > :not(button) { + padding: variables.$spacing-md; + } +} + +.properties { + display: flex; + flex-direction: row; + gap: variables.$spacing-xl; +} diff --git a/airbyte-webapp/src/components/connection/CatalogTree/next/StreamPanelHeader.tsx b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamPanelHeader.tsx new file mode 100644 index 000000000000..e40cdb29ba90 --- /dev/null +++ b/airbyte-webapp/src/components/connection/CatalogTree/next/StreamPanelHeader.tsx @@ -0,0 +1,55 @@ +import { FormattedMessage } from "react-intl"; + +import { CrossIcon } from "components/icons/CrossIcon"; +import { Button } from "components/ui/Button"; +import { Switch } from "components/ui/Switch"; + +import { AirbyteStream, AirbyteStreamConfiguration } from "core/request/AirbyteClient"; + +import styles from "./StreamPanelHeader.module.scss"; + +interface StreamPanelHeaderProps { + config?: AirbyteStreamConfiguration; + disabled?: boolean; + onClose: () => void; + onSelectedChange: () => void; + stream?: AirbyteStream; +} + +interface SomethingProps { + messageId: string; + value?: string; +} + +export const StreamProperty: React.FC = ({ messageId, value }) => { + return ( + + + + + : {value} + + ); +}; + +export const StreamPanelHeader: React.FC = ({ + config, + disabled, + onClose, + onSelectedChange, + stream, +}) => { + return ( +
+
+ +
+
+ + + +
+
+ ); +}; diff --git a/airbyte-webapp/src/components/ui/Modal/Modal.module.scss b/airbyte-webapp/src/components/ui/Modal/Modal.module.scss index 290d12289781..566759f29356 100644 --- a/airbyte-webapp/src/components/ui/Modal/Modal.module.scss +++ b/airbyte-webapp/src/components/ui/Modal/Modal.module.scss @@ -8,15 +8,6 @@ } } -.backdrop { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(colors.$black, 0.5); -} - .modalPageContainer { position: absolute; z-index: z-indices.$modal; diff --git a/airbyte-webapp/src/components/ui/Modal/Modal.tsx b/airbyte-webapp/src/components/ui/Modal/Modal.tsx index 857174bb281a..942dadc3ac0b 100644 --- a/airbyte-webapp/src/components/ui/Modal/Modal.tsx +++ b/airbyte-webapp/src/components/ui/Modal/Modal.tsx @@ -3,6 +3,7 @@ import classNames from "classnames"; import React, { useState } from "react"; import { Card } from "../Card"; +import { Overlay } from "../Overlay/Overlay"; import styles from "./Modal.module.scss"; export interface ModalProps { @@ -37,7 +38,7 @@ export const Modal: React.FC> = ({ return ( -
+
{cardless ? ( diff --git a/airbyte-webapp/src/components/ui/Overlay/Overlay.module.scss b/airbyte-webapp/src/components/ui/Overlay/Overlay.module.scss new file mode 100644 index 000000000000..0102f7db41b6 --- /dev/null +++ b/airbyte-webapp/src/components/ui/Overlay/Overlay.module.scss @@ -0,0 +1,10 @@ +@use "scss/colors"; + +.container { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(colors.$black, 0.5); +} diff --git a/airbyte-webapp/src/components/ui/Overlay/Overlay.tsx b/airbyte-webapp/src/components/ui/Overlay/Overlay.tsx new file mode 100644 index 000000000000..1d9bdea7f3b8 --- /dev/null +++ b/airbyte-webapp/src/components/ui/Overlay/Overlay.tsx @@ -0,0 +1,3 @@ +import styles from "./Overlay.module.scss"; + +export const Overlay: React.FC = () =>