diff --git a/webapp/packages/core-app/package.json b/webapp/packages/core-app/package.json
index 97be7f6399..c5df1bdcd0 100644
--- a/webapp/packages/core-app/package.json
+++ b/webapp/packages/core-app/package.json
@@ -27,6 +27,7 @@
"@cloudbeaver/core-sdk": "^0.1.0",
"@cloudbeaver/core-settings": "^0.1.0",
"@cloudbeaver/core-theming": "^0.1.0",
+ "@cloudbeaver/core-ui": "^0.1.0",
"@cloudbeaver/core-utils": "^0.1.0",
"@cloudbeaver/core-view": "^0.1.0",
"@cloudbeaver/core-executor": "^0.1.0",
diff --git a/webapp/packages/core-app/src/AppScreen/Main.tsx b/webapp/packages/core-app/src/AppScreen/Main.tsx
index 442b76f6a6..c130ff5f1f 100644
--- a/webapp/packages/core-app/src/AppScreen/Main.tsx
+++ b/webapp/packages/core-app/src/AppScreen/Main.tsx
@@ -42,7 +42,7 @@ const mainStyles = composes(
`
);
-export function Main() {
+export const Main: React.FC = function Main() {
return styled(useStyles(mainStyles, splitStyles))(
@@ -56,4 +56,4 @@ export function Main() {
);
-}
+};
diff --git a/webapp/packages/core-app/src/AppScreen/RightArea.tsx b/webapp/packages/core-app/src/AppScreen/RightArea.tsx
index 009c2ad3c9..21205bea17 100644
--- a/webapp/packages/core-app/src/AppScreen/RightArea.tsx
+++ b/webapp/packages/core-app/src/AppScreen/RightArea.tsx
@@ -10,10 +10,11 @@ import { observer } from 'mobx-react-lite';
import styled, { css } from 'reshadow';
import {
- Pane, ResizerControls, Split, splitHorizontalStyles, splitStyles
+ Pane, ResizerControls, SlideBox, SlideElement, slideBoxStyles, Split, splitHorizontalStyles, splitStyles
} from '@cloudbeaver/core-blocks';
-import { useController } from '@cloudbeaver/core-di';
+import { useController, useService } from '@cloudbeaver/core-di';
import { useStyles, composes } from '@cloudbeaver/core-theming';
+import { OptionsPanelService } from '@cloudbeaver/core-ui';
import { NavigationTabsBar } from '../shared/NavigationTabs/NavigationTabsBar';
import { LogViewTab } from '../shared/ToolsPanel/LogViewTab/LogViewTab';
@@ -34,21 +35,36 @@ const styles = composes(
Pane:last-child {
flex: 0 0 30%;
}
+ SlideBox {
+ flex: 1;
+ }
+ SlideElement {
+ display: flex;
+ }
`
);
export const RightArea = observer(function RightArea() {
const controller = useController(LogViewTabController);
+ const optionsPanelService = useService(OptionsPanelService);
+ const OptionsPanel = optionsPanelService.getPanelComponent();
- return styled(useStyles(styles, splitStyles, splitHorizontalStyles))(
-
-
-
-
- {controller.isActive && }
-
-
-
-
+ return styled(useStyles(styles, splitStyles, splitHorizontalStyles, slideBoxStyles))(
+
+
+
+
+
+
+
+
+
+ {controller.isActive && }
+
+
+
+
+
+
);
});
diff --git a/webapp/packages/core-blocks/src/Loader/Loader.tsx b/webapp/packages/core-blocks/src/Loader/Loader.tsx
index a00fc9fbc9..c103dd94b0 100644
--- a/webapp/packages/core-blocks/src/Loader/Loader.tsx
+++ b/webapp/packages/core-blocks/src/Loader/Loader.tsx
@@ -70,7 +70,12 @@ export const Loader: React.FC = observer(function Loader({
if ('loading' in element) {
loading = element.loading;
} else if ('isLoading' in element) {
- loading = element.isLoading() && (overlay || !element.isLoaded());
+ const loaded = element.isLoaded();
+ loading = element.isLoading() || !loaded;
+
+ if (!loaded) {
+ overlay = false;
+ }
}
}
}
diff --git a/webapp/packages/core-blocks/src/Slide/SlideBox.tsx b/webapp/packages/core-blocks/src/Slide/SlideBox.tsx
new file mode 100644
index 0000000000..37d6558b12
--- /dev/null
+++ b/webapp/packages/core-blocks/src/Slide/SlideBox.tsx
@@ -0,0 +1,20 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+interface Props {
+ open?: boolean;
+ className?: string;
+}
+
+export const SlideBox: React.FC = function SlideBox({ children, className }) {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/webapp/packages/core-blocks/src/Slide/SlideBoxStyles.ts b/webapp/packages/core-blocks/src/Slide/SlideBoxStyles.ts
new file mode 100644
index 0000000000..95d7323131
--- /dev/null
+++ b/webapp/packages/core-blocks/src/Slide/SlideBoxStyles.ts
@@ -0,0 +1,29 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import { css } from 'reshadow';
+
+export const slideBoxStyles = css`
+ SlideBox {
+ overflow: hidden;
+ white-space: nowrap;
+
+ & SlideElement {
+ width: 100%;
+ height: 100%;
+ display: inline-block;
+ vertical-align:top;
+ transition: transform cubic-bezier(0.4, 0.0, 0.2, 1) 0.6s;
+ transform: translateX(-100%);
+ }
+
+ &[open] SlideElement {
+ transform: translateX(0%);
+ }
+ }
+`;
diff --git a/webapp/packages/core-blocks/src/Slide/SlideElement.tsx b/webapp/packages/core-blocks/src/Slide/SlideElement.tsx
new file mode 100644
index 0000000000..915ceb8323
--- /dev/null
+++ b/webapp/packages/core-blocks/src/Slide/SlideElement.tsx
@@ -0,0 +1,19 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+interface Props {
+ className?: string;
+}
+
+export const SlideElement: React.FC = function SlideElement({ children, className }) {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/webapp/packages/core-blocks/src/Tabs/Tab/TabDefault.tsx b/webapp/packages/core-blocks/src/Tabs/Tab/TabDefault.tsx
index 7bd38a6365..2964f46d8c 100644
--- a/webapp/packages/core-blocks/src/Tabs/Tab/TabDefault.tsx
+++ b/webapp/packages/core-blocks/src/Tabs/Tab/TabDefault.tsx
@@ -13,7 +13,7 @@ import { Translate } from '@cloudbeaver/core-localization';
import { DynamicStyle, useStyles } from '@cloudbeaver/core-theming';
import { TabContext } from '../TabContext';
-import type { ITabData } from '../TabsContext';
+import type { ITabData } from '../TabsContainer/ITabsContainer';
import { Tab } from './Tab';
import { TabIcon } from './TabIcon';
import type { TabProps } from './TabProps';
diff --git a/webapp/packages/core-blocks/src/Tabs/Tab/TabProps.ts b/webapp/packages/core-blocks/src/Tabs/Tab/TabProps.ts
index 9b84578dc6..a8a137171d 100644
--- a/webapp/packages/core-blocks/src/Tabs/Tab/TabProps.ts
+++ b/webapp/packages/core-blocks/src/Tabs/Tab/TabProps.ts
@@ -10,7 +10,7 @@ import type { PropsWithChildren } from 'react';
import type { DynamicStyle } from '@cloudbeaver/core-theming';
-import type { ITabData } from '../TabsContext';
+import type { ITabData } from '../TabsContainer/ITabsContainer';
export type TabProps = PropsWithChildren<{
tabId: string;
diff --git a/webapp/packages/core-blocks/src/Tabs/Tab/useTab.ts b/webapp/packages/core-blocks/src/Tabs/Tab/useTab.ts
index 5762af9b7c..43a059b9c4 100644
--- a/webapp/packages/core-blocks/src/Tabs/Tab/useTab.ts
+++ b/webapp/packages/core-blocks/src/Tabs/Tab/useTab.ts
@@ -6,12 +6,13 @@
* you may not use this file except in compliance with the License.
*/
-import { useContext, useEffect, useRef } from 'react';
+import { useContext, useEffect } from 'react';
import type { IExecutorHandler } from '@cloudbeaver/core-executor';
import { useObjectRef } from '../../useObjectRef';
-import { ITabData, TabsContext } from '../TabsContext';
+import type { ITabData } from '../TabsContainer/ITabsContainer';
+import { TabsContext } from '../TabsContext';
export function useTab(
tabId: string,
diff --git a/webapp/packages/core-blocks/src/Tabs/TabPanelList.tsx b/webapp/packages/core-blocks/src/Tabs/TabPanelList.tsx
index 9b5c12c24d..b1a625a400 100644
--- a/webapp/packages/core-blocks/src/Tabs/TabPanelList.tsx
+++ b/webapp/packages/core-blocks/src/Tabs/TabPanelList.tsx
@@ -12,7 +12,7 @@ import styled from 'reshadow';
import { DynamicStyle, useStyles } from '@cloudbeaver/core-theming';
import { TabPanel } from './TabPanel';
-import type { ITabInfo } from './TabsContainer';
+import type { ITabInfo } from './TabsContainer/ITabsContainer';
import { TabsContext } from './TabsContext';
interface Props {
diff --git a/webapp/packages/core-blocks/src/Tabs/TabsBox/TabsBox.tsx b/webapp/packages/core-blocks/src/Tabs/TabsBox/TabsBox.tsx
index 94be3e0f37..c899d1f748 100644
--- a/webapp/packages/core-blocks/src/Tabs/TabsBox/TabsBox.tsx
+++ b/webapp/packages/core-blocks/src/Tabs/TabsBox/TabsBox.tsx
@@ -29,7 +29,7 @@ export const TabsBox = forwardRef(function TabsBox
className,
style = [],
}: TabsBoxProps, ref) {
- return styled(useStyles(...style))(
+ return styled(useStyles(style))(
{tabs && (
diff --git a/webapp/packages/core-blocks/src/Tabs/TabsBox/TabsBoxFromArray.tsx b/webapp/packages/core-blocks/src/Tabs/TabsBox/TabsBoxFromArray.tsx
index 98812fcdd1..a96356c634 100644
--- a/webapp/packages/core-blocks/src/Tabs/TabsBox/TabsBoxFromArray.tsx
+++ b/webapp/packages/core-blocks/src/Tabs/TabsBox/TabsBoxFromArray.tsx
@@ -13,7 +13,7 @@ import { useStyles, Style } from '@cloudbeaver/core-theming';
import { Tab } from '../Tab';
import { TabPanel } from '../TabPanel';
-import type { ITabData } from '../TabsContext';
+import type { ITabData } from '../TabsContainer/ITabsContainer';
import { TabsBox } from './TabsBox';
export interface TabElementProps {
diff --git a/webapp/packages/core-blocks/src/Tabs/TabsContainer.ts b/webapp/packages/core-blocks/src/Tabs/TabsContainer/ITabsContainer.ts
similarity index 53%
rename from webapp/packages/core-blocks/src/Tabs/TabsContainer.ts
rename to webapp/packages/core-blocks/src/Tabs/TabsContainer/ITabsContainer.ts
index a4487a3f95..3d76ec62c8 100644
--- a/webapp/packages/core-blocks/src/Tabs/TabsContainer.ts
+++ b/webapp/packages/core-blocks/src/Tabs/TabsContainer/ITabsContainer.ts
@@ -6,12 +6,14 @@
* you may not use this file except in compliance with the License.
*/
-import { computed, observable, makeObservable } from 'mobx';
-
import type { MetadataValueGetter } from '@cloudbeaver/core-utils';
-import type { TabProps } from './Tab/TabProps';
-import type { ITabData } from './TabsContext';
+import type { TabProps } from '../Tab/TabProps';
+
+export interface ITabData> {
+ tabId: string;
+ props: T;
+}
export type TabContainerTabComponent> = React.FC;
export type TabContainerPanelComponent> = React.FC<{ tabId: string } & TProps>;
@@ -42,35 +44,8 @@ export interface ITabInfo<
order: number;
}
-export class TabsContainer, TOptions extends Record = never> {
- readonly tabInfoMap: Map>;
-
- get tabInfoList(): Array> {
- return Array.from(this.tabInfoMap.values())
- .sort((a, b) => a.order - b.order);
- }
-
- constructor() {
- makeObservable(this, {
- tabInfoMap: observable.shallow,
- tabInfoList: computed,
- });
-
- this.tabInfoMap = new Map();
- }
-
- getDisplayed(props?: TProps): Array> {
- return this.tabInfoList.filter(tabInfo => !tabInfo.isHidden?.(tabInfo.key, props));
- }
-
- add(tabInfo: ITabInfoOptions): void {
- if (this.tabInfoMap.has(tabInfo.key)) {
- throw new Error('Tab with same key already exists');
- }
-
- this.tabInfoMap.set(tabInfo.key, {
- ...tabInfo,
- order: tabInfo.order ?? Number.MAX_SAFE_INTEGER,
- });
- }
+export interface ITabsContainer, TOptions extends Record = never> {
+ tabInfoList: Array>;
+ getTabInfo: (tabId: string) => ITabInfo | undefined;
+ getDisplayed: (props?: TProps) => Array>;
}
diff --git a/webapp/packages/core-blocks/src/Tabs/TabsContainer/TabsContainer.ts b/webapp/packages/core-blocks/src/Tabs/TabsContainer/TabsContainer.ts
new file mode 100644
index 0000000000..6869b4f4c8
--- /dev/null
+++ b/webapp/packages/core-blocks/src/Tabs/TabsContainer/TabsContainer.ts
@@ -0,0 +1,49 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import { computed, observable, makeObservable } from 'mobx';
+
+import type { ITabInfo, ITabInfoOptions, ITabsContainer } from './ITabsContainer';
+
+export class TabsContainer, TOptions extends Record = never>
+implements ITabsContainer {
+ readonly tabInfoMap: Map>;
+
+ get tabInfoList(): Array> {
+ return Array.from(this.tabInfoMap.values())
+ .sort((a, b) => a.order - b.order);
+ }
+
+ constructor() {
+ makeObservable(this, {
+ tabInfoMap: observable.shallow,
+ tabInfoList: computed,
+ });
+
+ this.tabInfoMap = new Map();
+ }
+
+ getTabInfo(tabId: string): ITabInfo | undefined {
+ return this.tabInfoMap.get(tabId);
+ }
+
+ getDisplayed(props?: TProps): Array> {
+ return this.tabInfoList.filter(tabInfo => !tabInfo.isHidden?.(tabInfo.key, props));
+ }
+
+ add(tabInfo: ITabInfoOptions): void {
+ if (this.tabInfoMap.has(tabInfo.key)) {
+ throw new Error('Tab with same key already exists');
+ }
+
+ this.tabInfoMap.set(tabInfo.key, {
+ ...tabInfo,
+ order: tabInfo.order ?? Number.MAX_SAFE_INTEGER,
+ });
+ }
+}
diff --git a/webapp/packages/core-blocks/src/Tabs/TabsContext.ts b/webapp/packages/core-blocks/src/Tabs/TabsContext.ts
index 06eea8c5a7..68d64daf8f 100644
--- a/webapp/packages/core-blocks/src/Tabs/TabsContext.ts
+++ b/webapp/packages/core-blocks/src/Tabs/TabsContext.ts
@@ -12,18 +12,13 @@ import type { TabStateReturn } from 'reakit/Tab';
import type { IExecutor } from '@cloudbeaver/core-executor';
import type { MetadataMap } from '@cloudbeaver/core-utils';
-import type { ITabInfo, TabsContainer } from './TabsContainer';
-
-export interface ITabData> {
- tabId: string;
- props: T;
-}
+import type { ITabData, ITabInfo, ITabsContainer } from './TabsContainer/ITabsContainer';
export interface ITabsContext> {
state: TabStateReturn;
tabsState: MetadataMap;
props: T;
- container?: TabsContainer;
+ container?: ITabsContainer;
openExecutor: IExecutor>;
closeExecutor: IExecutor>;
lazy: boolean;
diff --git a/webapp/packages/core-blocks/src/Tabs/TabsState.tsx b/webapp/packages/core-blocks/src/Tabs/TabsState.tsx
index 8d7cde7e98..83661f07ef 100644
--- a/webapp/packages/core-blocks/src/Tabs/TabsState.tsx
+++ b/webapp/packages/core-blocks/src/Tabs/TabsState.tsx
@@ -13,14 +13,14 @@ import { Executor, ExecutorInterrupter, IExecutorHandler } from '@cloudbeaver/co
import { MetadataMap } from '@cloudbeaver/core-utils';
import { useObjectRef } from '../useObjectRef';
-import type { TabsContainer } from './TabsContainer';
-import { TabsContext, ITabsContext, ITabData } from './TabsContext';
+import type { ITabData, ITabsContainer } from './TabsContainer/ITabsContainer';
+import { TabsContext, ITabsContext } from './TabsContext';
type Props> = T & React.PropsWithChildren<{
selectedId?: string;
orientation?: 'horizontal' | 'vertical';
currentTabId?: string | null;
- container?: TabsContainer;
+ container?: ITabsContainer;
localState?: MetadataMap;
lazy?: boolean;
manual?: boolean;
@@ -120,7 +120,7 @@ export function TabsState>({
props: dynamic.props,
}), []);
- const getTabInfo = useCallback((tabId: string) => dynamic.container?.tabInfoMap.get(tabId), []);
+ const getTabInfo = useCallback((tabId: string) => dynamic.container?.getTabInfo(tabId), []);
const value = useMemo>(() => ({
state,
diff --git a/webapp/packages/core-blocks/src/Tabs/index.ts b/webapp/packages/core-blocks/src/Tabs/index.ts
index 5e45c1ccd2..28483dbd6b 100644
--- a/webapp/packages/core-blocks/src/Tabs/index.ts
+++ b/webapp/packages/core-blocks/src/Tabs/index.ts
@@ -4,7 +4,8 @@ export * from './TabList';
export * from './TabPanel';
export * from './TabPanelList';
export * from './TabPanelProps';
-export * from './TabsContainer';
+export * from './TabsContainer/ITabsContainer';
+export * from './TabsContainer/TabsContainer';
export * from './TabsContext';
export * from './TabsState';
export * from './useTabState';
diff --git a/webapp/packages/core-blocks/src/index.ts b/webapp/packages/core-blocks/src/index.ts
index 8adbe9dab2..38fec05355 100644
--- a/webapp/packages/core-blocks/src/index.ts
+++ b/webapp/packages/core-blocks/src/index.ts
@@ -11,6 +11,9 @@ export * from './Placeholder/Placeholder';
export * from './Placeholder/PlaceholderContainer';
export * from './PropertiesTable';
export * from './SanitizedHTML/SanitizedHTML';
+export * from './Slide/SlideBox';
+export * from './Slide/SlideBoxStyles';
+export * from './Slide/SlideElement';
export * from './Split';
export * from './Table';
export * from './Tabs';
diff --git a/webapp/packages/core-bootstrap/package.json b/webapp/packages/core-bootstrap/package.json
index f7729d812c..06d6eb9de3 100644
--- a/webapp/packages/core-bootstrap/package.json
+++ b/webapp/packages/core-bootstrap/package.json
@@ -33,6 +33,7 @@
"@cloudbeaver/core-sdk": "^0.1.0",
"@cloudbeaver/core-settings": "^0.1.0",
"@cloudbeaver/core-theming": "^0.1.0",
+ "@cloudbeaver/core-ui": "^0.1.0",
"@cloudbeaver/core-utils": "^0.1.0",
"@cloudbeaver/core-view": "^0.1.0",
"react": "^16.x.x",
diff --git a/webapp/packages/core-bootstrap/src/manifest.ts b/webapp/packages/core-bootstrap/src/manifest.ts
index 1da8a51691..f3d44ea94b 100644
--- a/webapp/packages/core-bootstrap/src/manifest.ts
+++ b/webapp/packages/core-bootstrap/src/manifest.ts
@@ -92,6 +92,7 @@ import { RouterService, ScreenService } from '@cloudbeaver/core-routing';
import { EnvironmentService, GraphQLService } from '@cloudbeaver/core-sdk';
import { LocalStorageSaveService, SettingsService } from '@cloudbeaver/core-settings';
import { ThemeService } from '@cloudbeaver/core-theming';
+import { OptionsPanelService } from '@cloudbeaver/core-ui';
import { ActiveViewService } from '@cloudbeaver/core-view';
export const coreManifest: PluginManifest = {
@@ -154,6 +155,7 @@ export const coreManifest: PluginManifest = {
EnvironmentService,
ExceptionsCatcherService,
EventsSettingsService,
+ OptionsPanelService,
GraphQLService,
LocalStorageSaveService,
LocalizationService,
diff --git a/webapp/packages/core-connections/src/Administration/ConnectionsResource.ts b/webapp/packages/core-connections/src/Administration/ConnectionsResource.ts
index d98a34c517..f7a9596d8f 100644
--- a/webapp/packages/core-connections/src/Administration/ConnectionsResource.ts
+++ b/webapp/packages/core-connections/src/Administration/ConnectionsResource.ts
@@ -235,6 +235,9 @@ export class ConnectionsResource extends CachedMapResource {
+ await this.performUpdate(config.connectionId!, [], async () => {
+ const { connection } = await this.graphQLService.sdk.updateConnection({
+ config,
+ ...this.getDefaultIncludes(),
+ ...this.getIncludesMap(config.connectionId!),
+ });
+
+ this.updateConnection(connection);
+ });
+ return this.get(config.connectionId!)!;
+ }
+
async close(id: string): Promise {
await this.performUpdate(id, [], async () => {
const { connection } = await this.graphQLService.sdk.closeConnection({
diff --git a/webapp/packages/core-connections/src/index.ts b/webapp/packages/core-connections/src/index.ts
index 564ba14b23..d728d62733 100644
--- a/webapp/packages/core-connections/src/index.ts
+++ b/webapp/packages/core-connections/src/index.ts
@@ -5,6 +5,7 @@ export * from './ConnectionForm/OriginInfo/ConnectionOriginInfoTabService';
export * from './ConnectionForm/ConnectionAccess/ConnectionAccessTabService';
export * from './ConnectionForm/ConnectionFormService';
export * from './ConnectionForm/useConnectionFormState';
+export * from './ConnectionForm/ConnectionForm';
export * from './Administration/Connections/ConnectionsAdministration';
export * from './Administration/Connections/ConnectionsAdministrationNavService';
export * from './Administration/Connections/ConnectionsAdministrationService';
diff --git a/webapp/packages/core-executor/src/Executor.ts b/webapp/packages/core-executor/src/Executor.ts
index 0c8e618239..72feb4182a 100644
--- a/webapp/packages/core-executor/src/Executor.ts
+++ b/webapp/packages/core-executor/src/Executor.ts
@@ -14,7 +14,7 @@ import type { IExecutorHandler } from './IExecutorHandler';
import type { IExecutorHandlersCollection } from './IExecutorHandlersCollection';
import { BlockedExecution, TaskScheduler } from './TaskScheduler/TaskScheduler';
-export class Executor implements IExecutor {
+export class Executor implements IExecutor {
private collection: ExecutorHandlersCollection;
private scheduler: TaskScheduler;
diff --git a/webapp/packages/core-executor/src/IExecutor.ts b/webapp/packages/core-executor/src/IExecutor.ts
index 236678959c..d5068df5b7 100644
--- a/webapp/packages/core-executor/src/IExecutor.ts
+++ b/webapp/packages/core-executor/src/IExecutor.ts
@@ -10,7 +10,7 @@ import type { IExecutionContext, IExecutionContextProvider } from './IExecutionC
import type { IExecutorHandler } from './IExecutorHandler';
import type { IExecutorHandlersCollection } from './IExecutorHandlersCollection';
-export interface IExecutor {
+export interface IExecutor {
before: (executor: IExecutor, map?: (data: T) => TNext) => this;
next: (executor: IExecutor, map?: (data: T) => TNext) => this;
execute: (
diff --git a/webapp/packages/core-sdk/src/queries/connections/updateConnection.gql b/webapp/packages/core-sdk/src/queries/connections/updateConnection.gql
new file mode 100644
index 0000000000..b20a8eaf9e
--- /dev/null
+++ b/webapp/packages/core-sdk/src/queries/connections/updateConnection.gql
@@ -0,0 +1,12 @@
+mutation updateConnection(
+ $config: ConnectionConfig!
+
+ $includeOrigin: Boolean!,
+ $customIncludeOriginDetails: Boolean!,
+ $includeAuthProperties: Boolean!,
+ $customIncludeNetworkHandlerCredentials: Boolean!
+) {
+ connection: updateConnection(config: $config) {
+ ...DatabaseConnection
+ }
+}
diff --git a/webapp/packages/core-sdk/src/sdk.ts b/webapp/packages/core-sdk/src/sdk.ts
index e8eef1572e..beedab8003 100644
--- a/webapp/packages/core-sdk/src/sdk.ts
+++ b/webapp/packages/core-sdk/src/sdk.ts
@@ -279,6 +279,7 @@ export interface Mutation {
testConnection: ConnectionInfo;
testNetworkHandler: NetworkEndpointInfo;
touchSession?: Maybe;
+ updateConnection: ConnectionInfo;
updateResultsData?: Maybe;
updateResultsDataBatch?: Maybe;
}
@@ -389,6 +390,10 @@ export interface MutationTestNetworkHandlerArgs {
config: NetworkHandlerConfigInput;
}
+export interface MutationUpdateConnectionArgs {
+ config: ConnectionConfig;
+}
+
export interface MutationUpdateResultsDataArgs {
connectionId: Scalars['ID'];
contextId: Scalars['ID'];
@@ -1217,6 +1222,16 @@ export type TestNetworkHandlerMutationVariables = Exact<{
export interface TestNetworkHandlerMutation { testNetworkHandler: Pick }
+export type UpdateConnectionMutationVariables = Exact<{
+ config: ConnectionConfig;
+ includeOrigin: Scalars['Boolean'];
+ customIncludeOriginDetails: Scalars['Boolean'];
+ includeAuthProperties: Scalars['Boolean'];
+ customIncludeNetworkHandlerCredentials: Scalars['Boolean'];
+}>;
+
+export interface UpdateConnectionMutation { connection: DatabaseConnectionFragment }
+
export type ExportDataFromContainerQueryVariables = Exact<{
connectionId: Scalars['ID'];
containerNodePath: Scalars['ID'];
@@ -2045,6 +2060,13 @@ export const TestNetworkHandlerDocument = `
}
}
`;
+export const UpdateConnectionDocument = `
+ mutation updateConnection($config: ConnectionConfig!, $includeOrigin: Boolean!, $customIncludeOriginDetails: Boolean!, $includeAuthProperties: Boolean!, $customIncludeNetworkHandlerCredentials: Boolean!) {
+ connection: updateConnection(config: $config) {
+ ...DatabaseConnection
+ }
+}
+ ${DatabaseConnectionFragmentDoc}`;
export const ExportDataFromContainerDocument = `
query exportDataFromContainer($connectionId: ID!, $containerNodePath: ID!, $parameters: DataTransferParameters!) {
taskInfo: dataTransferExportDataFromContainer(
@@ -2603,6 +2625,9 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
testNetworkHandler(variables: TestNetworkHandlerMutationVariables): Promise {
return withWrapper(() => client.request(TestNetworkHandlerDocument, variables));
},
+ updateConnection(variables: UpdateConnectionMutationVariables): Promise {
+ return withWrapper(() => client.request(UpdateConnectionDocument, variables));
+ },
exportDataFromContainer(variables: ExportDataFromContainerQueryVariables): Promise {
return withWrapper(() => client.request(ExportDataFromContainerDocument, variables));
},
diff --git a/webapp/packages/core-ui/.gitignore b/webapp/packages/core-ui/.gitignore
new file mode 100644
index 0000000000..99902db863
--- /dev/null
+++ b/webapp/packages/core-ui/.gitignore
@@ -0,0 +1,21 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/lib
+
+# misc
+.DS_Store
+.env*
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/webapp/packages/core-ui/package.json b/webapp/packages/core-ui/package.json
new file mode 100644
index 0000000000..f9cff19d30
--- /dev/null
+++ b/webapp/packages/core-ui/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "@cloudbeaver/core-ui",
+ "sideEffects": false,
+ "version": "0.1.0",
+ "description": "",
+ "license": "Apache-2.0",
+ "main": "lib/index.js",
+ "module": "lib/index.js",
+ "types": "src/index.ts",
+ "scripts": {
+ "prebuild": "rimraf lib/*",
+ "build": "webpack --config ../../configs/webpack.plugin.config.js",
+ "postbuild": "tsc",
+ "lint": "eslint ./src/ --ext .ts,.tsx",
+ "lint-fix": "eslint ./src/ --ext .ts,.tsx --fix"
+ },
+ "peerDependencies": {
+ "@cloudbeaver/core-di": "^0.1.0",
+ "@cloudbeaver/core-executor": "^0.1.0",
+ "mobx": "^5.x.x"
+ }
+}
\ No newline at end of file
diff --git a/webapp/packages/core-ui/src/Screens/AppScreen/OptionsPanelService.ts b/webapp/packages/core-ui/src/Screens/AppScreen/OptionsPanelService.ts
new file mode 100644
index 0000000000..1c44a29f45
--- /dev/null
+++ b/webapp/packages/core-ui/src/Screens/AppScreen/OptionsPanelService.ts
@@ -0,0 +1,64 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import { makeObservable, observable } from 'mobx';
+
+import { injectable } from '@cloudbeaver/core-di';
+import { Executor, ExecutorInterrupter, IExecutor } from '@cloudbeaver/core-executor';
+
+@injectable()
+export class OptionsPanelService {
+ active: boolean;
+ closeTask: IExecutor;
+
+ panelComponent: (() => React.FC) | null;
+
+ constructor() {
+ makeObservable(this, {
+ active: observable,
+ panelComponent: observable,
+ });
+ this.active = false;
+ this.panelComponent = null;
+ this.closeTask = new Executor();
+ }
+
+ getPanelComponent(): React.FC {
+ if (this.panelComponent === null) {
+ return () => null;
+ }
+ return this.panelComponent();
+ }
+
+ async open(component: () => React.FC): Promise {
+ if (!(await this.close(component))) {
+ return;
+ }
+
+ this.panelComponent = component;
+ this.active = true;
+ }
+
+ async close(component: () => React.FC): Promise {
+ if (component !== this.panelComponent || this.panelComponent === null) {
+ return true;
+ }
+
+ const contexts = await this.closeTask.execute();
+
+ const interrupted = contexts.getContext(ExecutorInterrupter.interruptContext);
+
+ if (interrupted.interrupted) {
+ return false;
+ }
+
+ this.panelComponent = null;
+ this.active = false;
+ return true;
+ }
+}
diff --git a/webapp/packages/core-ui/src/index.ts b/webapp/packages/core-ui/src/index.ts
new file mode 100644
index 0000000000..9618c34418
--- /dev/null
+++ b/webapp/packages/core-ui/src/index.ts
@@ -0,0 +1 @@
+export * from './Screens/AppScreen/OptionsPanelService';
diff --git a/webapp/packages/core-ui/tsconfig.json b/webapp/packages/core-ui/tsconfig.json
new file mode 100644
index 0000000000..8cd6b539e3
--- /dev/null
+++ b/webapp/packages/core-ui/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["src"],
+ "exclude": ["**/node_modules", "**/.*/"],
+}
diff --git a/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/ConnectionForm/ConnectionForm.tsx b/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/ConnectionForm/ConnectionForm.tsx
deleted file mode 100644
index a8ad10beb9..0000000000
--- a/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/ConnectionForm/ConnectionForm.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * CloudBeaver - Cloud Database Manager
- * Copyright (C) 2020-2021 DBeaver Corp and others
- *
- * Licensed under the Apache License, Version 2.0.
- * you may not use this file except in compliance with the License.
- */
-
-import { observer } from 'mobx-react-lite';
-import styled, { css } from 'reshadow';
-
-import { useAdministrationSettings } from '@cloudbeaver/core-administration';
-import {
- InputField,
- useFocus,
- ObjectPropertyInfoForm,
- InputGroup,
- FieldCheckbox,
- SubmittingForm,
- FormBox,
- FormBoxElement,
- FormGroup,
-} from '@cloudbeaver/core-blocks';
-import type { DBDriver } from '@cloudbeaver/core-connections';
-import { useTranslate } from '@cloudbeaver/core-localization';
-import { useStyles } from '@cloudbeaver/core-theming';
-
-import type { IFormController } from './IFormController';
-import { ParametersForm } from './ParametersForm';
-
-interface ConnectionFormProps {
- driver: DBDriver | null;
- controller: IFormController;
-}
-
-const connectionFormStyles = css`
- SubmittingForm {
- flex: 1;
- }
- FormBoxElement {
- flex: 1;
- }
- FormBox {
- padding: 24px 4px 24px;
- }
-`;
-
-export const ConnectionForm = observer(function ConnectionForm({
- driver,
- controller,
-}: ConnectionFormProps) {
- const translate = useTranslate();
- const [focusedRef] = useFocus({ focusFirstChild: true });
- const { credentialsSavingEnabled } = useAdministrationSettings();
-
- return styled(useStyles(connectionFormStyles))(
-
-
-
-
-
- {translate('customConnection_custom_name')}
-
-
- {controller.isUrlConnection ? (
-
-
- {translate('customConnection_url_JDBC')}
-
-
- ) : (
-
- )}
- {controller.authModel && (
- <>
-
- {translate('connections_connection_edit_authentication')}
-
-
- {credentialsSavingEnabled && (
-
-
-
- )}
- >
- )}
-
-
-
- );
-});
diff --git a/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/ConnectionForm/IFormController.ts b/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/ConnectionForm/IFormController.ts
deleted file mode 100644
index 656627252c..0000000000
--- a/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/ConnectionForm/IFormController.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * CloudBeaver - Cloud Database Manager
- * Copyright (C) 2020-2021 DBeaver Corp and others
- *
- * Licensed under the Apache License, Version 2.0.
- * you may not use this file except in compliance with the License.
- */
-
-import { ConnectionConfig, DatabaseAuthModel } from '@cloudbeaver/core-sdk';
-
-export interface IFormController {
- authModel?: DatabaseAuthModel;
- config: ConnectionConfig;
- isUrlConnection: boolean;
- isConnecting: boolean;
- onChange: (value?: unknown, name?: string) => void;
-}
diff --git a/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/ConnectionForm/ParametersForm.tsx b/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/ConnectionForm/ParametersForm.tsx
deleted file mode 100644
index b489aa3548..0000000000
--- a/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/ConnectionForm/ParametersForm.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * CloudBeaver - Cloud Database Manager
- * Copyright (C) 2020-2021 DBeaver Corp and others
- *
- * Licensed under the Apache License, Version 2.0.
- * you may not use this file except in compliance with the License.
- */
-
-import { observer } from 'mobx-react-lite';
-import styled, { css, use } from 'reshadow';
-
-import { InputField } from '@cloudbeaver/core-blocks';
-import { useTranslate } from '@cloudbeaver/core-localization';
-import { useStyles } from '@cloudbeaver/core-theming';
-
-import type { IFormController } from './IFormController';
-
-interface ParametersFormProps {
- controller: IFormController;
- embedded?: boolean;
-}
-
-const parametersFormStyles = css`
- sub-label {
- composes: theme-typography--caption from global;
- line-height: 14px;
- }`;
-
-export const ParametersForm = observer(function ParametersForm({
- controller,
- embedded,
-}: ParametersFormProps) {
- const translate = useTranslate();
-
- return styled(useStyles(parametersFormStyles))(
- <>
- {!embedded && (
-
-
-
- {translate('customConnection_custom_host')}
- {translate('customConnection_custom_obligatory')}
-
-
-
-
- {translate('customConnection_custom_port')}
-
-
-
- )}
-
-
- {translate('customConnection_custom_database')}
-
-
- >
- );
-});
diff --git a/webapp/packages/plugin-connection-template/src/manifest.ts b/webapp/packages/plugin-connection-template/src/manifest.ts
index 462c58f5ff..16227f62e9 100644
--- a/webapp/packages/plugin-connection-template/src/manifest.ts
+++ b/webapp/packages/plugin-connection-template/src/manifest.ts
@@ -14,7 +14,7 @@ import { TemplateConnectionsResource } from './TemplateConnectionsResource';
export const connectionTemplate: PluginManifest = {
info: {
- name: 'Connection template plugin',
+ name: 'Connections plugin',
},
providers: [
diff --git a/webapp/packages/plugin-connections/.gitignore b/webapp/packages/plugin-connections/.gitignore
new file mode 100644
index 0000000000..15bc16c7c3
--- /dev/null
+++ b/webapp/packages/plugin-connections/.gitignore
@@ -0,0 +1,17 @@
+# dependencies
+/node_modules
+
+# testing
+/coverage
+
+# production
+/lib
+
+# misc
+.DS_Store
+.env*
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/webapp/packages/plugin-connections/package.json b/webapp/packages/plugin-connections/package.json
new file mode 100644
index 0000000000..4cf3f337d9
--- /dev/null
+++ b/webapp/packages/plugin-connections/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "@cloudbeaver/plugin-connections",
+ "sideEffects": false,
+ "version": "0.1.0",
+ "description": "",
+ "license": "Apache-2.0",
+ "main": "lib/index.js",
+ "module": "lib/index.js",
+ "types": "src/index.ts",
+ "scripts": {
+ "prebuild": "rimraf lib/*",
+ "build": "webpack --config ../../configs/webpack.plugin.config.js",
+ "postbuild": "tsc",
+ "lint": "eslint ./src/ --ext .ts,.tsx",
+ "lint-fix": "eslint ./src/ --ext .ts,.tsx --fix"
+ },
+ "peerDependencies": {
+ "@cloudbeaver/core-app": "^0.1.0",
+ "@cloudbeaver/core-blocks": "^0.1.0",
+ "@cloudbeaver/core-connections": "^0.1.0",
+ "@cloudbeaver/core-di": "^0.1.0",
+ "@cloudbeaver/core-dialogs": "^0.1.0",
+ "@cloudbeaver/core-events": "^0.1.0",
+ "@cloudbeaver/core-extensions": "^0.1.0",
+ "@cloudbeaver/core-localization": "^0.1.0",
+ "@cloudbeaver/core-plugin": "^0.1.0",
+ "@cloudbeaver/core-product": "^0.1.0",
+ "@cloudbeaver/core-root": "^0.1.0",
+ "@cloudbeaver/core-sdk": "^0.1.0",
+ "@cloudbeaver/core-settings": "^0.1.0",
+ "@cloudbeaver/core-theming": "^0.1.0",
+ "@cloudbeaver/core-ui": "^0.1.0",
+ "@cloudbeaver/core-utils": "^0.1.0",
+ "react": "^16.x.x",
+ "rxjs": "^6.x.x",
+ "mobx": "^5.x.x",
+ "mobx-react-lite": "^3.x.x",
+ "reshadow": "^0.x.x"
+ }
+}
diff --git a/webapp/packages/plugin-connections/src/PublicConnectionForm/PublicConnectionForm.tsx b/webapp/packages/plugin-connections/src/PublicConnectionForm/PublicConnectionForm.tsx
new file mode 100644
index 0000000000..21bed97f06
--- /dev/null
+++ b/webapp/packages/plugin-connections/src/PublicConnectionForm/PublicConnectionForm.tsx
@@ -0,0 +1,63 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import { observable } from 'mobx';
+import { observer } from 'mobx-react-lite';
+import { useCallback, useState } from 'react';
+import styled from 'reshadow';
+import { css } from 'reshadow';
+
+import { Loader, useMapResource } from '@cloudbeaver/core-blocks';
+import { ConnectionInfoResource, IConnectionFormData, IConnectionFormOptions, ConnectionForm } from '@cloudbeaver/core-connections';
+import { useService } from '@cloudbeaver/core-di';
+import { MetadataMap } from '@cloudbeaver/core-utils';
+
+import { PublicConnectionFormService } from './PublicConnectionFormService';
+
+const styles = css`
+ Loader {
+ height: 100%;
+ }
+`;
+
+export const PublicConnectionForm: React.FC = observer(function PublicConnectionForm() {
+ const service = useService(PublicConnectionFormService);
+ const connection = useMapResource(ConnectionInfoResource, {
+ key: service.connectionId,
+ includes: ['includeOrigin', 'customIncludeNetworkHandlerCredentials', 'includeAuthProperties', 'customIncludeNetworkHandlerCredentials'],
+ });
+
+ const [data] = useState(() => ({
+ config: observable({}),
+ get info() {
+ return connection.data;
+ },
+ resource: connection.resource,
+ partsState: new MetadataMap(),
+ }));
+
+ const [options] = useState(() => ({
+ mode: 'edit',
+ type: 'public',
+ }));
+
+ const close = useCallback(() => service.close(), []);
+
+ return styled(styles)(
+
+ {() => (
+
+ )}
+
+ );
+});
diff --git a/webapp/packages/plugin-connections/src/PublicConnectionForm/PublicConnectionFormBootstrap.ts b/webapp/packages/plugin-connections/src/PublicConnectionForm/PublicConnectionFormBootstrap.ts
new file mode 100644
index 0000000000..298a27444a
--- /dev/null
+++ b/webapp/packages/plugin-connections/src/PublicConnectionForm/PublicConnectionFormBootstrap.ts
@@ -0,0 +1,48 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import {
+ NavigationTreeContextMenuService,
+ EObjectFeature,
+ NodeManagerUtils,
+ NavNode
+} from '@cloudbeaver/core-app';
+import { Bootstrap, injectable } from '@cloudbeaver/core-di';
+import { ContextMenuService } from '@cloudbeaver/core-dialogs';
+
+import { PublicConnectionFormService } from './PublicConnectionFormService';
+
+@injectable()
+export class PublicConnectionFormBootstrap extends Bootstrap {
+ constructor(
+ private readonly contextMenuService: ContextMenuService,
+ private readonly publicConnectionFormService: PublicConnectionFormService
+ ) {
+ super();
+ }
+
+ register(): void | Promise {
+ this.contextMenuService.addMenuItem(this.contextMenuService.getRootMenuToken(), {
+ id: 'connection-edit',
+ isPresent(context) {
+ return context.contextType === NavigationTreeContextMenuService.nodeContextType
+ && context.data.objectFeatures.includes(EObjectFeature.dataSource);
+ },
+ title: 'Edit',
+ order: 2,
+ onClick: context => {
+ const node = context.data;
+ const connectionId = NodeManagerUtils.connectionNodeIdToConnectionId(node.id);
+ this.publicConnectionFormService.open(connectionId);
+ },
+ });
+ }
+
+ load(): void | Promise {
+ }
+}
diff --git a/webapp/packages/plugin-connections/src/PublicConnectionForm/PublicConnectionFormService.ts b/webapp/packages/plugin-connections/src/PublicConnectionForm/PublicConnectionFormService.ts
new file mode 100644
index 0000000000..f9de931161
--- /dev/null
+++ b/webapp/packages/plugin-connections/src/PublicConnectionForm/PublicConnectionFormService.ts
@@ -0,0 +1,35 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import { injectable } from '@cloudbeaver/core-di';
+import { OptionsPanelService } from '@cloudbeaver/core-ui';
+
+import { PublicConnectionForm } from './PublicConnectionForm';
+
+const formGetter = () => PublicConnectionForm;
+
+@injectable()
+export class PublicConnectionFormService {
+ connectionId: string | null;
+
+ constructor(
+ private readonly optionsPanelService: OptionsPanelService
+ ) {
+ this.connectionId = null;
+ }
+
+ async open(connectionId: string): Promise {
+ this.connectionId = connectionId;
+ await this.optionsPanelService.open(formGetter);
+ }
+
+ async close(): Promise {
+ this.connectionId = null;
+ await this.optionsPanelService.close(formGetter);
+ }
+}
diff --git a/webapp/packages/plugin-connections/src/index.ts b/webapp/packages/plugin-connections/src/index.ts
new file mode 100644
index 0000000000..25b9fa81b7
--- /dev/null
+++ b/webapp/packages/plugin-connections/src/index.ts
@@ -0,0 +1,3 @@
+import { connectionPlugin } from './manifest';
+
+export default connectionPlugin;
diff --git a/webapp/packages/plugin-connections/src/manifest.ts b/webapp/packages/plugin-connections/src/manifest.ts
new file mode 100644
index 0000000000..4ee6559631
--- /dev/null
+++ b/webapp/packages/plugin-connections/src/manifest.ts
@@ -0,0 +1,23 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2021 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import type { PluginManifest } from '@cloudbeaver/core-di';
+
+import { PublicConnectionFormBootstrap } from './PublicConnectionForm/PublicConnectionFormBootstrap';
+import { PublicConnectionFormService } from './PublicConnectionForm/PublicConnectionFormService';
+
+export const connectionPlugin: PluginManifest = {
+ info: {
+ name: 'Connection template plugin',
+ },
+
+ providers: [
+ PublicConnectionFormBootstrap,
+ PublicConnectionFormService,
+ ],
+};
diff --git a/webapp/packages/plugin-connections/tsconfig.json b/webapp/packages/plugin-connections/tsconfig.json
new file mode 100644
index 0000000000..8cd6b539e3
--- /dev/null
+++ b/webapp/packages/plugin-connections/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["src"],
+ "exclude": ["**/node_modules", "**/.*/"],
+}
diff --git a/webapp/packages/product-default/src/index.ts b/webapp/packages/product-default/src/index.ts
index 3b12fc1be7..752470bb67 100644
--- a/webapp/packages/product-default/src/index.ts
+++ b/webapp/packages/product-default/src/index.ts
@@ -5,6 +5,7 @@ import administration from '@cloudbeaver/plugin-administration';
import authentication from '@cloudbeaver/plugin-authentication';
import connectionCustom from '@cloudbeaver/plugin-connection-custom';
import connectionTemplate from '@cloudbeaver/plugin-connection-template';
+import connectionPlugin from '@cloudbeaver/plugin-connections';
import dataExport from '@cloudbeaver/plugin-data-export';
import spreadsheetNew from '@cloudbeaver/plugin-data-spreadsheet-new';
import dataViewer from '@cloudbeaver/plugin-data-viewer';
@@ -27,6 +28,7 @@ const PLUGINS = [
objectViewer,
sqlEditor,
defaultProductManifest,
+ connectionPlugin,
];
bootstrap(PLUGINS);