diff --git a/webapp/packages/core-connections/src/CONNECTION_CONFIG_SCHEMA.ts b/webapp/packages/core-connections/src/CONNECTION_CONFIG_SCHEMA.ts new file mode 100644 index 0000000000..9549220ddb --- /dev/null +++ b/webapp/packages/core-connections/src/CONNECTION_CONFIG_SCHEMA.ts @@ -0,0 +1,51 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { DriverConfigurationType, NetworkHandlerAuthType } from '@cloudbeaver/core-sdk'; +import { schema } from '@cloudbeaver/core-utils'; + +export const CONNECTION_PROPERTIES_SCHEMA = schema.record(schema.string(), schema.any()); + +export const CONNECTION_NETWORK_HANDLER_SCHEMA = schema.object({ + id: schema.string(), + authType: schema.nativeEnum(NetworkHandlerAuthType).optional(), + enabled: schema.boolean().optional(), + key: schema.string().optional(), + password: schema.string().optional(), + properties: schema.record(schema.string(), schema.any()).optional(), + savePassword: schema.boolean().optional(), + secureProperties: schema.record(schema.string(), schema.any()).optional(), + userName: schema.string().optional(), +}); + +export const CONNECTION_CONFIG_SCHEMA = schema.object({ + authModelId: schema.string().optional(), + configurationType: schema.enum([DriverConfigurationType.Manual, DriverConfigurationType.Url]).optional(), + connectionId: schema.string().optional(), + credentials: schema.record(schema.string(), schema.any()).optional(), + dataSourceId: schema.string().optional(), + databaseName: schema.string().optional(), + description: schema.string().optional(), + driverId: schema.string().optional(), + folder: schema.string().optional(), + host: schema.string().optional(), + mainPropertyValues: schema.record(schema.string(), schema.any()).optional(), + expertSettingsValues: schema.record(schema.string(), schema.any()).optional(), + name: schema.string().optional(), + networkHandlersConfig: schema.array(CONNECTION_NETWORK_HANDLER_SCHEMA).optional(), + port: schema.string().optional(), + properties: CONNECTION_PROPERTIES_SCHEMA.optional(), + providerProperties: schema.record(schema.string(), schema.any()).optional(), + saveCredentials: schema.boolean().optional(), + selectedSecretId: schema.string().optional(), + serverName: schema.string().optional(), + sharedCredentials: schema.boolean().optional(), + url: schema.string().optional(), + userName: schema.string().optional(), + userPassword: schema.string().optional(), +}); diff --git a/webapp/packages/core-connections/src/index.ts b/webapp/packages/core-connections/src/index.ts index cf3a56fe40..de251b4ecd 100644 --- a/webapp/packages/core-connections/src/index.ts +++ b/webapp/packages/core-connections/src/index.ts @@ -64,3 +64,4 @@ export * from './useDBDriver.js'; export * from './USER_NAME_PROPERTY_ID.js'; export * from './parseConnectionKey.js'; export * from './DBDriverExpertSettingsResource.js'; +export * from './CONNECTION_CONFIG_SCHEMA.js'; diff --git a/webapp/packages/plugin-connection-preferences/.gitignore b/webapp/packages/plugin-connection-preferences/.gitignore new file mode 100644 index 0000000000..15bc16c7c3 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/.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-connection-preferences/package.json b/webapp/packages/plugin-connection-preferences/package.json new file mode 100644 index 0000000000..84b7afe89e --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/package.json @@ -0,0 +1,53 @@ +{ + "name": "@cloudbeaver/plugin-connection-preferences", + "type": "module", + "sideEffects": [ + "./lib/module.js", + "./lib/index.js", + "src/**/*.css", + "src/**/*.scss", + "public/**/*" + ], + "version": "0.1.0", + "description": "", + "license": "Apache-2.0", + "exports": { + ".": "./lib/index.js", + "./module": "./lib/module.js" + }, + "scripts": { + "build": "tsc -b", + "clean": "rimraf --glob lib", + "lint": "eslint ./src/ --ext .ts,.tsx", + "validate-dependencies": "core-cli-validate-dependencies" + }, + "dependencies": { + "@cloudbeaver/core-blocks": "workspace:*", + "@cloudbeaver/core-connections": "workspace:*", + "@cloudbeaver/core-data-context": "workspace:*", + "@cloudbeaver/core-di": "workspace:*", + "@cloudbeaver/core-events": "workspace:*", + "@cloudbeaver/core-executor": "workspace:*", + "@cloudbeaver/core-localization": "workspace:*", + "@cloudbeaver/core-navigation-tree": "workspace:*", + "@cloudbeaver/core-projects": "workspace:*", + "@cloudbeaver/core-sdk": "workspace:*", + "@cloudbeaver/core-ui": "workspace:*", + "@cloudbeaver/core-utils": "workspace:*", + "@cloudbeaver/core-view": "workspace:*", + "@dbeaver/js-helpers": "workspace:*", + "mobx": "^6", + "mobx-react-lite": "^4", + "react": "^19", + "react-dom": "^19", + "tslib": "^2" + }, + "devDependencies": { + "@cloudbeaver/core-cli": "workspace:*", + "@cloudbeaver/tsconfig": "workspace:*", + "@types/react": "^19", + "rimraf": "^6", + "typescript": "^5", + "typescript-plugin-css-modules": "^5" + } +} diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesBootstrap.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesBootstrap.ts new file mode 100644 index 0000000000..bf49fc4b47 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesBootstrap.ts @@ -0,0 +1,48 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { DATA_CONTEXT_CONNECTION } from '@cloudbeaver/core-connections'; +import { Bootstrap, injectable } from '@cloudbeaver/core-di'; +import { ActionService, MenuService } from '@cloudbeaver/core-view'; +import { DATA_CONTEXT_NAV_NODE, EObjectFeature } from '@cloudbeaver/core-navigation-tree'; + +import { ACTION_CONNECTION_PREFERENCES } from './actions/ACTION_CONNECTION_PREFERENCES.js'; +import { ConnectionPreferencesPanelService } from './ConnectionPreferencesPanelService.js'; + +@injectable(() => [ActionService, MenuService, ConnectionPreferencesPanelService]) +export class ConnectionPreferencesBootstrap extends Bootstrap { + constructor( + private readonly actionService: ActionService, + private readonly menuService: MenuService, + private readonly connectionPreferencesPanelService: ConnectionPreferencesPanelService, + ) { + super(); + } + + override register(): void { + this.menuService.addCreator({ + root: true, + contexts: [DATA_CONTEXT_CONNECTION, DATA_CONTEXT_NAV_NODE], + isApplicable: context => { + const node = context.get(DATA_CONTEXT_NAV_NODE)!; + return node.objectFeatures.includes(EObjectFeature.dataSource); + }, + getItems: (context, items) => [...items, ACTION_CONNECTION_PREFERENCES], + }); + + this.actionService.addHandler({ + id: 'connection-preferences', + actions: [ACTION_CONNECTION_PREFERENCES], + contexts: [DATA_CONTEXT_CONNECTION], + handler: async context => { + const connectionKey = context.get(DATA_CONTEXT_CONNECTION)!; + await this.connectionPreferencesPanelService.open(connectionKey); + }, + }); + } +} diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesForm.tsx b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesForm.tsx new file mode 100644 index 0000000000..5f38454502 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesForm.tsx @@ -0,0 +1,93 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { Form, Loader, Placeholder, StatusMessage, useForm, useObjectRef } from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; +import { ENotificationType, NotificationService } from '@cloudbeaver/core-events'; +import type { ConnectionConfig } from '@cloudbeaver/core-sdk'; +import { ExecutionContext } from '@cloudbeaver/core-executor'; +import { TabList, TabPanelList, TabsState, type IFormState } from '@cloudbeaver/core-ui'; +import { getFirstException } from '@cloudbeaver/core-utils'; + +import { ConnectionPreferencesFormService } from './ConnectionPreferencesFormService.js'; +import type { ConnectionPreferencesFormState } from './ConnectionPreferencesFormState.js'; +import type { IConnectionPreferencesFormState } from './IConnectionPreferencesFormState.js'; +import { ConnectionPreferencesFormActionsContext, type IConnectionPreferencesFormActionsContext } from './ConnectionPreferencesFormActionsContext.js'; +import { getConnectionPreferencesFormInfoPart } from './ConnectionPreferencesFormInfo/getConnectionPreferencesFormInfoPart.js'; + +export interface ConnectionPreferencesFormProps { + formState: ConnectionPreferencesFormState; + onCancel?: () => void; + onSave?: (config: ConnectionConfig) => void; +} + +export const ConnectionPreferencesForm = observer(function ConnectionPreferencesForm({ formState, onCancel, onSave = () => { } }) { + const connectionPreferencesFormServicee = useService(ConnectionPreferencesFormService); + const notificationService = useService(NotificationService); + + const infoPart = getConnectionPreferencesFormInfoPart(formState); + const exception = getFirstException(formState.exception); + + const form = useForm({ + onSubmit: async event => { + const context = new ExecutionContext>(formState); + + const saved = await formState.save(context); + + if (saved) { + notificationService.notify( + { + title: 'core_connections_connection_update_success', + message: infoPart.state.name, + }, + ENotificationType.Success, + ); + + onSave(infoPart.state); + } + }, + }); + + const actionsContext = useObjectRef(() => ({ + save: () => form.submit(new SubmitEvent('submit')), + onCancel, + })); + + return ( +
+ +
+
+
+
+ +
+ +
+
+ + + + + +
+
+
+ +
+
+
+
+ ); +}); diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormActionsContext.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormActionsContext.ts new file mode 100644 index 0000000000..a796a7b423 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormActionsContext.ts @@ -0,0 +1,16 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { createContext } from 'react'; + +export interface IConnectionPreferencesFormActionsContext { + save: () => Promise; + onCancel?: () => void; +} + +export const ConnectionPreferencesFormActionsContext = createContext(null); diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormBaseActions.tsx b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormBaseActions.tsx new file mode 100644 index 0000000000..be7158d0fb --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormBaseActions.tsx @@ -0,0 +1,38 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { useContext } from 'react'; + +import { Button, type PlaceholderComponent, useTranslate } from '@cloudbeaver/core-blocks'; + +import { ConnectionPreferencesFormActionsContext } from './ConnectionPreferencesFormActionsContext.js'; +import type { IConnectionPreferencesFormProps } from './IConnectionPreferencesFormState.js'; + +export const ConnectionPreferencesFormBaseActions: PlaceholderComponent = observer(function ConnectionPreferencesFormBaseActions({ formState }) { + const actions = useContext(ConnectionPreferencesFormActionsContext); + + if (!actions) { + throw new Error('ConnectionPreferencesFormActionsContext not provided'); + } + + const translate = useTranslate(); + + return ( + <> + {actions.onCancel && ( + + )} + + + ); +}); diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/ConnectionPreferencesFormInfo.tsx b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/ConnectionPreferencesFormInfo.tsx new file mode 100644 index 0000000000..ddf279fb0f --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/ConnectionPreferencesFormInfo.tsx @@ -0,0 +1,58 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { useTab, type TabContainerPanelComponent } from '@cloudbeaver/core-ui'; +import { InputField, Textarea, useAutoLoad, useTranslate, useResource, Group, ColoredContainer, IconOrImage } from '@cloudbeaver/core-blocks'; +import { ProjectInfoResource } from '@cloudbeaver/core-projects'; +import { DBDriverResource } from '@cloudbeaver/core-connections'; + +import type { IConnectionPreferencesFormProps } from '../IConnectionPreferencesFormState.js'; +import { getConnectionPreferencesFormInfoPart } from './getConnectionPreferencesFormInfoPart.js'; + + +export const ConnectionPreferencesFormInfo: TabContainerPanelComponent = observer(function ConnectionPreferencesFormInfo({ formState, tabId }) { + const translate = useTranslate(); + const infoPart = getConnectionPreferencesFormInfoPart(formState); + const tab = useTab(tabId); + + useAutoLoad(ConnectionPreferencesFormInfo, infoPart, tab.selected); + + const projectInfoResource = useResource(ConnectionPreferencesFormInfo, ProjectInfoResource, formState.state.projectId); + const dbDriverResource = useResource(ConnectionPreferencesFormInfo, DBDriverResource, infoPart.state.driverId ?? null); + + return ( + + + {dbDriverResource.data && ( + +
+ {dbDriverResource.data.icon && } + {translate('connections_connection_driver')} +
+
+ )} + + {translate('connections_connection_name')} + + {projectInfoResource.data && ( + + {translate('plugin_projects_project_select_label')} + + )} + + {translate('plugin_connections_connection_form_part_main_folder')} + + +
+
+ ); +}); \ No newline at end of file diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/ConnectionPreferencesFormInfoPart.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/ConnectionPreferencesFormInfoPart.ts new file mode 100644 index 0000000000..31730f682d --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/ConnectionPreferencesFormInfoPart.ts @@ -0,0 +1,46 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { FormPart, type IFormState } from '@cloudbeaver/core-ui'; +import { createConnectionParam, type ConnectionInfoResource } from '@cloudbeaver/core-connections'; + +import type { IConnectionPreferencesFormState } from '../IConnectionPreferencesFormState.js'; +import type { IConnectionPreferencesFormInfoState } from './IConnectionPreferencesFormInfoState.js'; + +function getInitialState(): IConnectionPreferencesFormInfoState { + return {}; +} + +export class ConnectionPreferencesFormInfoPart extends FormPart { + constructor( + formState: IFormState, + private readonly connectionInfoResource: ConnectionInfoResource, + ) { + super(formState, getInitialState()); + } + + protected override async loader(): Promise { + if (this.formState.mode === 'edit' && this.formState.state) { + const key = createConnectionParam(this.formState.state.projectId, this.formState.state.connectionId); + const connection = await this.connectionInfoResource.load(key); + + this.setInitialState({ + driverId: connection.driverId, + name: connection.name, + folder: connection.folder, + description: connection.description, + }); + + return; + } + + this.setInitialState(getInitialState()); + } + + protected override async saveChanges(): Promise {} +} diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/ConnectionPreferencesInfoTabService.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/ConnectionPreferencesInfoTabService.ts new file mode 100644 index 0000000000..e22c62c32a --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/ConnectionPreferencesInfoTabService.ts @@ -0,0 +1,32 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { Bootstrap, injectable } from '@cloudbeaver/core-di'; +import { importLazyComponent } from '@cloudbeaver/core-blocks'; + +import { ConnectionPreferencesFormService } from '../ConnectionPreferencesFormService.js'; + +const ConnectionPreferencesFormInfo = importLazyComponent(() => + import('./ConnectionPreferencesFormInfo.js').then(m => m.ConnectionPreferencesFormInfo), +); + +@injectable(() => [ConnectionPreferencesFormService]) +export class ConnectionPreferencesInfoTabService extends Bootstrap { + constructor(private readonly connectionFormService: ConnectionPreferencesFormService) { + super(); + } + + override register(): void { + this.connectionFormService.parts.add({ + key: 'preferences-info', + name: 'Info', + order: 1, + panel: () => ConnectionPreferencesFormInfo, + }); + } +} diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/IConnectionPreferencesFormInfoState.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/IConnectionPreferencesFormInfoState.ts new file mode 100644 index 0000000000..7d5179e99b --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/IConnectionPreferencesFormInfoState.ts @@ -0,0 +1,19 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { schema } from '@cloudbeaver/core-utils'; +import { CONNECTION_CONFIG_SCHEMA } from '@cloudbeaver/core-connections'; + +export const CONNECTION_PREFERENCES_FORM_INFO_STATE_SCHEMA = CONNECTION_CONFIG_SCHEMA.pick({ + name: true, + folder: true, + description: true, + driverId: true, +}); + +export type IConnectionPreferencesFormInfoState = schema.infer; diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/getConnectionPreferencesFormInfoPart.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/getConnectionPreferencesFormInfoPart.ts new file mode 100644 index 0000000000..2cda2a5b0e --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormInfo/getConnectionPreferencesFormInfoPart.ts @@ -0,0 +1,25 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { createDataContext, DATA_CONTEXT_DI_PROVIDER } from '@cloudbeaver/core-data-context'; +import type { IFormState } from '@cloudbeaver/core-ui'; +import { ConnectionInfoResource } from '@cloudbeaver/core-connections'; + +import { ConnectionPreferencesFormInfoPart } from './ConnectionPreferencesFormInfoPart.js'; +import type { IConnectionPreferencesFormState } from '../IConnectionPreferencesFormState.js'; + +const DATA_CONTEXT_TEAM_FORM_OPTIONS_PART = createDataContext('Connection Preferences Info Part'); + +export function getConnectionPreferencesFormInfoPart(formState: IFormState): ConnectionPreferencesFormInfoPart { + return formState.getPart(DATA_CONTEXT_TEAM_FORM_OPTIONS_PART, context => { + const di = context.get(DATA_CONTEXT_DI_PROVIDER)!; + const connectionInfoResource = di.getService(ConnectionInfoResource); + + return new ConnectionPreferencesFormInfoPart(formState, connectionInfoResource); + }); +} diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormLoader.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormLoader.ts new file mode 100644 index 0000000000..5a8a69c384 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormLoader.ts @@ -0,0 +1,13 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { importLazyComponent } from '@cloudbeaver/core-blocks'; + +export const ConnectionPreferencesFormLoader = importLazyComponent(() => + import('./ConnectionPreferencesForm.js').then(m => m.ConnectionPreferencesForm), +); diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormService.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormService.ts new file mode 100644 index 0000000000..e16447be00 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormService.ts @@ -0,0 +1,31 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { NotificationService } from '@cloudbeaver/core-events'; +import { LocalizationService } from '@cloudbeaver/core-localization'; +import { FormBaseService, type IFormState } from '@cloudbeaver/core-ui'; +import { importLazyComponent } from '@cloudbeaver/core-blocks'; + +import type { IConnectionPreferencesFormProps, IConnectionPreferencesFormState } from './IConnectionPreferencesFormState.js'; + +const ConnectionFormBaseActionsLoader = importLazyComponent(() => + import('./ConnectionPreferencesFormBaseActions.js').then(m => m.ConnectionPreferencesFormBaseActions), +); + +export type ConnectionFormContainerProps = { + formState: IFormState; +}; + +@injectable(() => [LocalizationService, NotificationService]) +export class ConnectionPreferencesFormService extends FormBaseService { + constructor(localizationService: LocalizationService, notificationService: NotificationService) { + super(localizationService, notificationService, 'Connection preferences form'); + this.actionsContainer.add(ConnectionFormBaseActionsLoader); + } +} diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormState.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormState.ts new file mode 100644 index 0000000000..b7820e5bd9 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/ConnectionPreferencesFormState.ts @@ -0,0 +1,19 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { IServiceProvider } from '@cloudbeaver/core-di'; +import { FormState } from '@cloudbeaver/core-ui'; + +import type { IConnectionPreferencesFormState } from './IConnectionPreferencesFormState.js'; +import type { ConnectionPreferencesFormService } from './ConnectionPreferencesFormService.js'; + +export class ConnectionPreferencesFormState extends FormState { + constructor(serviceProvider: IServiceProvider, service: ConnectionPreferencesFormService, config: IConnectionPreferencesFormState) { + super(serviceProvider, service, config); + } +} diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/IConnectionPreferencesFormState.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/IConnectionPreferencesFormState.ts new file mode 100644 index 0000000000..1da7b5d584 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesForm/IConnectionPreferencesFormState.ts @@ -0,0 +1,20 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { schema } from '@cloudbeaver/core-utils'; +import type { IFormProps } from '@cloudbeaver/core-ui'; + +export const CONNECTION_PREFERENCES_FORM_STATE_SCHEMA = schema + .object({ + projectId: schema.string(), + connectionId: schema.string(), + }) + .strict(); + +export type IConnectionPreferencesFormState = schema.infer; +export type IConnectionPreferencesFormProps = IFormProps; diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesPanel.tsx b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesPanel.tsx new file mode 100644 index 0000000000..975200db06 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesPanel.tsx @@ -0,0 +1,30 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { useCallback } from 'react'; + +import { ColoredContainer, Loader } from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; + +import { ConnectionPreferencesPanelService } from './ConnectionPreferencesPanelService.js'; +import { ConnectionPreferencesFormLoader } from './ConnectionPreferencesForm/ConnectionPreferencesFormLoader.js'; + +export const ConnectionPreferencesPanel: React.FC = observer(function ConnectionPreferencesPanel() { + const service = useService(ConnectionPreferencesPanelService); + + const close = useCallback(() => service.close(), [service]); + + return ( + + + {service.formState && } + + + ); +}); diff --git a/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesPanelService.ts b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesPanelService.ts new file mode 100644 index 0000000000..ffb9c81aed --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/ConnectionPreferencesPanelService.ts @@ -0,0 +1,50 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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, IServiceProvider } from '@cloudbeaver/core-di'; +import { FormMode, OptionsPanelService } from '@cloudbeaver/core-ui'; +import { importLazyComponent } from '@cloudbeaver/core-blocks'; +import type { IConnectionInfoParams } from '@cloudbeaver/core-connections'; + +import { ConnectionPreferencesFormState } from './ConnectionPreferencesForm/ConnectionPreferencesFormState.js'; +import { ConnectionPreferencesFormService } from './ConnectionPreferencesForm/ConnectionPreferencesFormService.js'; + +const ConnectionPreferencesPanel = importLazyComponent(() => import('./ConnectionPreferencesPanel.js').then(m => m.ConnectionPreferencesPanel)); + +const formGetter = () => ConnectionPreferencesPanel; + +@injectable(() => [OptionsPanelService, IServiceProvider, ConnectionPreferencesFormService]) +export class ConnectionPreferencesPanelService { + formState: ConnectionPreferencesFormState | null; + + constructor( + private readonly optionsPanelService: OptionsPanelService, + private readonly serviceProvider: IServiceProvider, + private readonly connectionPreferencesFormService: ConnectionPreferencesFormService, + ) { + this.formState = null; + } + + async open(connectionKey: IConnectionInfoParams): Promise { + const opened = await this.optionsPanelService.open(formGetter); + + if (opened) { + this.formState = new ConnectionPreferencesFormState(this.serviceProvider, this.connectionPreferencesFormService, { + projectId: connectionKey.projectId, + connectionId: connectionKey.connectionId, + }).setMode(FormMode.Edit); + } + } + + async close(): Promise { + await this.optionsPanelService.close(); + + this.formState?.dispose(); + this.formState = null; + } +} diff --git a/webapp/packages/plugin-connection-preferences/src/LocaleService.ts b/webapp/packages/plugin-connection-preferences/src/LocaleService.ts new file mode 100644 index 0000000000..5dec550ef8 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/LocaleService.ts @@ -0,0 +1,38 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { Bootstrap, injectable } from '@cloudbeaver/core-di'; +import { LocalizationService } from '@cloudbeaver/core-localization'; + +@injectable(() => [LocalizationService]) +export class LocaleService extends Bootstrap { + constructor(private readonly localizationService: LocalizationService) { + super(); + } + + override register(): void { + this.localizationService.addProvider(this.provider.bind(this)); + } + + private async provider(locale: string) { + switch (locale) { + case 'ru': + return (await import('./locales/ru.js')).default; + case 'it': + return (await import('./locales/it.js')).default; + case 'zh': + return (await import('./locales/zh.js')).default; + case 'fr': + return (await import('./locales/fr.js')).default; + case 'vi': + return (await import('./locales/vi.js')).default; + default: + return (await import('./locales/en.js')).default; + } + } +} diff --git a/webapp/packages/plugin-connection-preferences/src/actions/ACTION_CONNECTION_PREFERENCES.ts b/webapp/packages/plugin-connection-preferences/src/actions/ACTION_CONNECTION_PREFERENCES.ts new file mode 100644 index 0000000000..582f4cff64 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/actions/ACTION_CONNECTION_PREFERENCES.ts @@ -0,0 +1,13 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { createAction } from '@cloudbeaver/core-view'; + +export const ACTION_CONNECTION_PREFERENCES = createAction('connection-preferences', { + label: 'Preferences', +}); diff --git a/webapp/packages/plugin-connection-preferences/src/index.ts b/webapp/packages/plugin-connection-preferences/src/index.ts new file mode 100644 index 0000000000..0a1f485cf1 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/index.ts @@ -0,0 +1,12 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 './module.js'; + +export * from './ConnectionPreferencesForm/ConnectionPreferencesFormService.js'; +export * from './ConnectionPreferencesForm/IConnectionPreferencesFormState.js'; diff --git a/webapp/packages/plugin-connection-preferences/src/locales/en.ts b/webapp/packages/plugin-connection-preferences/src/locales/en.ts new file mode 100644 index 0000000000..074112722f --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/locales/en.ts @@ -0,0 +1,3 @@ +export default [ + ['plugin_connection_preferences', 'Preferences'], +]; diff --git a/webapp/packages/plugin-connection-preferences/src/locales/fr.ts b/webapp/packages/plugin-connection-preferences/src/locales/fr.ts new file mode 100644 index 0000000000..074112722f --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/locales/fr.ts @@ -0,0 +1,3 @@ +export default [ + ['plugin_connection_preferences', 'Preferences'], +]; diff --git a/webapp/packages/plugin-connection-preferences/src/locales/it.ts b/webapp/packages/plugin-connection-preferences/src/locales/it.ts new file mode 100644 index 0000000000..074112722f --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/locales/it.ts @@ -0,0 +1,3 @@ +export default [ + ['plugin_connection_preferences', 'Preferences'], +]; diff --git a/webapp/packages/plugin-connection-preferences/src/locales/ru.ts b/webapp/packages/plugin-connection-preferences/src/locales/ru.ts new file mode 100644 index 0000000000..074112722f --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/locales/ru.ts @@ -0,0 +1,3 @@ +export default [ + ['plugin_connection_preferences', 'Preferences'], +]; diff --git a/webapp/packages/plugin-connection-preferences/src/locales/vi.ts b/webapp/packages/plugin-connection-preferences/src/locales/vi.ts new file mode 100644 index 0000000000..074112722f --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/locales/vi.ts @@ -0,0 +1,3 @@ +export default [ + ['plugin_connection_preferences', 'Preferences'], +]; diff --git a/webapp/packages/plugin-connection-preferences/src/locales/zh.ts b/webapp/packages/plugin-connection-preferences/src/locales/zh.ts new file mode 100644 index 0000000000..074112722f --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/locales/zh.ts @@ -0,0 +1,3 @@ +export default [ + ['plugin_connection_preferences', 'Preferences'], +]; diff --git a/webapp/packages/plugin-connection-preferences/src/module.ts b/webapp/packages/plugin-connection-preferences/src/module.ts new file mode 100644 index 0000000000..05ae884d94 --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/src/module.ts @@ -0,0 +1,30 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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 { Bootstrap, ModuleRegistry, proxy } from '@cloudbeaver/core-di'; + +import { ConnectionPreferencesBootstrap } from './ConnectionPreferencesBootstrap.js'; +import { LocaleService } from './LocaleService.js'; +import { ConnectionPreferencesPanelService } from './ConnectionPreferencesPanelService.js'; +import { ConnectionPreferencesFormService } from './ConnectionPreferencesForm/ConnectionPreferencesFormService.js'; +import { ConnectionPreferencesInfoTabService } from './ConnectionPreferencesForm/ConnectionPreferencesFormInfo/ConnectionPreferencesInfoTabService.js'; + +export default ModuleRegistry.add({ + name: '@cloudbeaver/plugin-connection-preferences', + + configure: serviceCollection => { + serviceCollection + .addSingleton(Bootstrap, LocaleService) + .addSingleton(Bootstrap, proxy(ConnectionPreferencesBootstrap)) + .addSingleton(Bootstrap, proxy(ConnectionPreferencesInfoTabService)) + .addSingleton(ConnectionPreferencesInfoTabService) + .addSingleton(ConnectionPreferencesBootstrap) + .addSingleton(ConnectionPreferencesPanelService) + .addSingleton(ConnectionPreferencesFormService); + }, +}); diff --git a/webapp/packages/plugin-connection-preferences/tsconfig.json b/webapp/packages/plugin-connection-preferences/tsconfig.json new file mode 100644 index 0000000000..827a3cbedb --- /dev/null +++ b/webapp/packages/plugin-connection-preferences/tsconfig.json @@ -0,0 +1,67 @@ +{ + "extends": "@cloudbeaver/tsconfig/tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib", + "tsBuildInfoFile": "lib/tsconfig.tsbuildinfo", + "composite": true + }, + "references": [ + { + "path": "../../common-typescript/@dbeaver/js-helpers" + }, + { + "path": "../core-blocks" + }, + { + "path": "../core-cli" + }, + { + "path": "../core-connections" + }, + { + "path": "../core-data-context" + }, + { + "path": "../core-di" + }, + { + "path": "../core-events" + }, + { + "path": "../core-executor" + }, + { + "path": "../core-localization" + }, + { + "path": "../core-navigation-tree" + }, + { + "path": "../core-projects" + }, + { + "path": "../core-sdk" + }, + { + "path": "../core-ui" + }, + { + "path": "../core-utils" + }, + { + "path": "../core-view" + } + ], + "include": [ + "__custom_mocks__/**/*", + "src/**/*", + "src/**/*.json", + "src/**/*.css", + "src/**/*.scss" + ], + "exclude": [ + "**/node_modules", + "lib/**/*" + ] +} diff --git a/webapp/packages/plugin-set-common/package.json b/webapp/packages/plugin-set-common/package.json index 55960b942f..df2262d75e 100644 --- a/webapp/packages/plugin-set-common/package.json +++ b/webapp/packages/plugin-set-common/package.json @@ -65,6 +65,7 @@ "@cloudbeaver/plugin-browser": "workspace:*", "@cloudbeaver/plugin-codemirror6": "workspace:*", "@cloudbeaver/plugin-connection-custom": "workspace:*", + "@cloudbeaver/plugin-connection-preferences": "workspace:*", "@cloudbeaver/plugin-connection-search": "workspace:*", "@cloudbeaver/plugin-connection-view": "workspace:*", "@cloudbeaver/plugin-connections": "workspace:*", diff --git a/webapp/packages/plugin-set-common/src/index.ts b/webapp/packages/plugin-set-common/src/index.ts index 419481cbf7..b493f11bb3 100644 --- a/webapp/packages/plugin-set-common/src/index.ts +++ b/webapp/packages/plugin-set-common/src/index.ts @@ -110,6 +110,7 @@ import pluginAsyncTaskConfirmation from '@cloudbeaver/plugin-async-task-confirma import pluginSqlAsyncTaskConfirmation from '@cloudbeaver/plugin-sql-async-task-confirmation/module'; import pluginDataViewerConditionalFormatting from '@cloudbeaver/plugin-data-viewer-conditional-formatting/module'; import pluginConnectionView from '@cloudbeaver/plugin-connection-view/module'; +import pluginConnectionPreferences from '@cloudbeaver/plugin-connection-preferences/module'; const core = [ coreRouting, // important, should be first because the router starts in load phase first after all plugins register phase @@ -219,4 +220,5 @@ export const commonSet = [ pluginSqlAsyncTaskConfirmation, pluginDataViewerConditionalFormatting, pluginConnectionView, + pluginConnectionPreferences, ]; diff --git a/webapp/packages/plugin-set-common/tsconfig.json b/webapp/packages/plugin-set-common/tsconfig.json index 4190a27b9c..2c341834f0 100644 --- a/webapp/packages/plugin-set-common/tsconfig.json +++ b/webapp/packages/plugin-set-common/tsconfig.json @@ -139,6 +139,9 @@ { "path": "../plugin-connection-custom" }, + { + "path": "../plugin-connection-preferences" + }, { "path": "../plugin-connection-search" }, diff --git a/webapp/yarn.lock b/webapp/yarn.lock index df3ef50945..5cd957bd6a 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2649,6 +2649,38 @@ __metadata: languageName: unknown linkType: soft +"@cloudbeaver/plugin-connection-preferences@workspace:*, @cloudbeaver/plugin-connection-preferences@workspace:packages/plugin-connection-preferences": + version: 0.0.0-use.local + resolution: "@cloudbeaver/plugin-connection-preferences@workspace:packages/plugin-connection-preferences" + dependencies: + "@cloudbeaver/core-blocks": "workspace:*" + "@cloudbeaver/core-cli": "workspace:*" + "@cloudbeaver/core-connections": "workspace:*" + "@cloudbeaver/core-data-context": "workspace:*" + "@cloudbeaver/core-di": "workspace:*" + "@cloudbeaver/core-events": "workspace:*" + "@cloudbeaver/core-executor": "workspace:*" + "@cloudbeaver/core-localization": "workspace:*" + "@cloudbeaver/core-navigation-tree": "workspace:*" + "@cloudbeaver/core-projects": "workspace:*" + "@cloudbeaver/core-sdk": "workspace:*" + "@cloudbeaver/core-ui": "workspace:*" + "@cloudbeaver/core-utils": "workspace:*" + "@cloudbeaver/core-view": "workspace:*" + "@cloudbeaver/tsconfig": "workspace:*" + "@dbeaver/js-helpers": "workspace:*" + "@types/react": "npm:^19" + mobx: "npm:^6" + mobx-react-lite: "npm:^4" + react: "npm:^19" + react-dom: "npm:^19" + rimraf: "npm:^6" + tslib: "npm:^2" + typescript: "npm:^5" + typescript-plugin-css-modules: "npm:^5" + languageName: unknown + linkType: soft + "@cloudbeaver/plugin-connection-search@workspace:*, @cloudbeaver/plugin-connection-search@workspace:packages/plugin-connection-search": version: 0.0.0-use.local resolution: "@cloudbeaver/plugin-connection-search@workspace:packages/plugin-connection-search" @@ -3873,6 +3905,7 @@ __metadata: "@cloudbeaver/plugin-browser": "workspace:*" "@cloudbeaver/plugin-codemirror6": "workspace:*" "@cloudbeaver/plugin-connection-custom": "workspace:*" + "@cloudbeaver/plugin-connection-preferences": "workspace:*" "@cloudbeaver/plugin-connection-search": "workspace:*" "@cloudbeaver/plugin-connection-view": "workspace:*" "@cloudbeaver/plugin-connections": "workspace:*"