mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge branch 'devel' into CB-4386-unify-feature-naming
This commit is contained in:
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
|
||||
Bundle-Version: 1.0.42.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.43.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.42-SNAPSHOT</version>
|
||||
<version>1.0.43-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -173,6 +173,11 @@ public class WebPropertyInfo {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isRequired() {
|
||||
return property.isRequired();
|
||||
}
|
||||
|
||||
public boolean hasFeature(@NotNull String feature) {
|
||||
return property.hasFeature(feature);
|
||||
}
|
||||
|
||||
+6
@@ -55,6 +55,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
private final boolean trusted;
|
||||
private final boolean isPrivate;
|
||||
private final String[] requiredFeatures;
|
||||
private final boolean isRequired;
|
||||
|
||||
public WebAuthProviderDescriptor(IConfigurationElement cfg) {
|
||||
super(cfg);
|
||||
@@ -64,6 +65,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
this.configurable = CommonUtils.toBoolean(cfg.getAttribute("configurable"));
|
||||
this.trusted = CommonUtils.toBoolean(cfg.getAttribute("trusted"));
|
||||
this.isPrivate = CommonUtils.toBoolean(cfg.getAttribute("private"));
|
||||
this.isRequired = CommonUtils.toBoolean(cfg.getAttribute("required"));
|
||||
|
||||
for (IConfigurationElement cfgElement : cfg.getChildren("configuration")) {
|
||||
for (IConfigurationElement propGroup : ArrayUtils.safeArray(cfgElement.getChildren(PropertyDescriptor.TAG_PROPERTY_GROUP))) {
|
||||
@@ -124,6 +126,10 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
return isPrivate;
|
||||
}
|
||||
|
||||
public boolean isRequired() {
|
||||
return isRequired;
|
||||
}
|
||||
|
||||
public List<PropertyDescriptor> getConfigurationParameters() {
|
||||
return new ArrayList<>(configurationParameters.values());
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Community Product
|
||||
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
|
||||
Bundle-Version: 23.3.0.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 23.3.1.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>23.3.0-SNAPSHOT</version>
|
||||
<version>23.3.1-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Base JDBC drivers
|
||||
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
|
||||
Bundle-Version: 1.0.87.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.88.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.87-SNAPSHOT</version>
|
||||
<version>1.0.88-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
|
||||
Bundle-Version: 23.3.0.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 23.3.1.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server</artifactId>
|
||||
<version>23.3.0-SNAPSHOT</version>
|
||||
<version>23.3.1-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -36,6 +36,8 @@ type ObjectPropertyInfo {
|
||||
order: Int!
|
||||
# Supported configuration types (for driver properties)
|
||||
supportedConfigurationTypes: [ String! ]
|
||||
# Required parameters
|
||||
required: Boolean! @since(version: "23.3.1")
|
||||
}
|
||||
|
||||
enum ObjectPropertyLength {
|
||||
|
||||
+4
@@ -83,6 +83,10 @@ public class WebAuthProviderInfo {
|
||||
return descriptor.isPrivate();
|
||||
}
|
||||
|
||||
public boolean isRequired() {
|
||||
return descriptor.isRequired();
|
||||
}
|
||||
|
||||
public boolean isSupportProvisioning() {
|
||||
return descriptor.getInstance() instanceof SMProvisioner;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Administration
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true
|
||||
Bundle-Version: 1.0.86.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.87.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.admin</artifactId>
|
||||
<version>1.0.86-SNAPSHOT</version>
|
||||
<version>1.0.87-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Authentication
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true
|
||||
Bundle-Version: 1.0.86.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.87.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.auth</artifactId>
|
||||
<version>1.0.86-SNAPSHOT</version>
|
||||
<version>1.0.87-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -72,6 +72,8 @@ type AuthProviderInfo {
|
||||
credentialProfiles: [AuthProviderCredentialsProfile!]!
|
||||
|
||||
requiredFeatures: [String!]!
|
||||
|
||||
required: Boolean!
|
||||
}
|
||||
|
||||
type AuthInfo {
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Data Transfer
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.data.transfer;singleton:=true
|
||||
Bundle-Version: 1.0.87.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.88.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.data.transfer</artifactId>
|
||||
<version>1.0.87-SNAPSHOT</version>
|
||||
<version>1.0.88-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - File System
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.fs;singleton:=true
|
||||
Bundle-Version: 1.0.4.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.5.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.fs</artifactId>
|
||||
<version>1.0.4-SNAPSHOT</version>
|
||||
<version>1.0.5-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Metadata
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.metadata;singleton:=true
|
||||
Bundle-Version: 1.0.90.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.91.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.metadata</artifactId>
|
||||
<version>1.0.90-SNAPSHOT</version>
|
||||
<version>1.0.91-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Resource manager NIO implementation
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.rm.nio;singleton:=true
|
||||
Bundle-Version: 1.0.4.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.5.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.rm.nio</artifactId>
|
||||
<version>1.0.4-SNAPSHOT</version>
|
||||
<version>1.0.5-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Service - Resource manager
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.rm;singleton:=true
|
||||
Bundle-Version: 1.0.39.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.40.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.rm</artifactId>
|
||||
<version>1.0.39-SNAPSHOT</version>
|
||||
<version>1.0.40-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: Cloudbeaver Web Service - Security
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.security;singleton:=true
|
||||
Bundle-Version: 1.0.42.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.43.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
icon="platform:/plugin/org.jkiss.dbeaver.model/icons/tree/key.png">
|
||||
<credentials>
|
||||
<propertyGroup label="General">
|
||||
<property id="user" label="User name" type="string" description="User name" admin="true" user="true" identifying="true"/>
|
||||
<property id="password" label="User password" type="string" description="User password" encryption="hash" admin="true" user="true"/>
|
||||
<property id="user" label="User name" type="string" description="User name" admin="true" user="true" identifying="true" required="true"/>
|
||||
<property id="password" label="User password" type="string" description="User password" encryption="hash" admin="true" user="true" required="true"/>
|
||||
</propertyGroup>
|
||||
</credentials>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.security</artifactId>
|
||||
<version>1.0.42-SNAPSHOT</version>
|
||||
<version>1.0.43-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: CloudBeaver SLF4j Binding
|
||||
Bundle-SymbolicName: io.cloudbeaver.slf4j;singleton:=true
|
||||
Bundle-Version: 1.0.2.qualifier
|
||||
Bundle-Release-Date: 20231204
|
||||
Bundle-Version: 1.0.3.qualifier
|
||||
Bundle-Release-Date: 20231225
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.slf4j</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.0.3-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.ce.drivers.feature"
|
||||
label="Cloudbeaver CE 3rd Party Drivers"
|
||||
version="1.0.110.qualifier"
|
||||
version="1.0.111.qualifier"
|
||||
provider-name="DBeaver Corp">
|
||||
|
||||
<plugin id="io.cloudbeaver.resources.drivers.base" download-size="0" install-size="0" version="0.0.0"/>
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.ce.drivers.feature</artifactId>
|
||||
<version>1.0.110-SNAPSHOT</version>
|
||||
<version>1.0.111-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.product.ce.feature"
|
||||
label="%featureName"
|
||||
version="23.3.0.qualifier"
|
||||
version="23.3.1.qualifier"
|
||||
provider-name="%providerName"
|
||||
plugin="io.cloudbeaver.product.ce">
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce.feature</artifactId>
|
||||
<version>23.3.0-SNAPSHOT</version>
|
||||
<version>23.3.1-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.server.feature"
|
||||
label="%featureName"
|
||||
version="23.3.0.qualifier"
|
||||
version="23.3.1.qualifier"
|
||||
provider-name="%providerName"
|
||||
plugin="io.cloudbeaver.server">
|
||||
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server.feature</artifactId>
|
||||
<version>23.3.0-SNAPSHOT</version>
|
||||
<version>23.3.1-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
</project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<feature
|
||||
id="io.cloudbeaver.ws.feature"
|
||||
label="%featureName"
|
||||
version="1.0.40.qualifier"
|
||||
version="1.0.41.qualifier"
|
||||
provider-name="%providerName"
|
||||
plugin="io.cloudbeaver.model">
|
||||
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.ws.feature</artifactId>
|
||||
<version>1.0.40-SNAPSHOT</version>
|
||||
<version>1.0.41-SNAPSHOT</version>
|
||||
<packaging>eclipse-feature</packaging>
|
||||
</project>
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
|
||||
<properties>
|
||||
<dbeaver-product>CloudBeaver CE</dbeaver-product>
|
||||
<dbeaver-version>23.3.0</dbeaver-version>
|
||||
<dbeaver-version>23.3.1</dbeaver-version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?pde version="3.5"?>
|
||||
|
||||
<product name="CloudBeaver ce Server" uid="io.cloudbeaver.product" id="io.cloudbeaver.product.ce.product"
|
||||
application="io.cloudbeaver.product.ce.application" version="23.3.0.qualifier" useFeatures="true"
|
||||
application="io.cloudbeaver.product.ce.application" version="23.3.1.qualifier" useFeatures="true"
|
||||
includeLaunchers="false">
|
||||
|
||||
<configIni use="default">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../../</relativePath>
|
||||
</parent>
|
||||
<version>23.3.0-SNAPSHOT</version>
|
||||
<version>23.3.1-SNAPSHOT</version>
|
||||
<artifactId>web-server</artifactId>
|
||||
<packaging>eclipse-repository</packaging>
|
||||
<name>Cloudbeaver Server Product</name>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
import { filterLayoutFakeProps, getLayoutProps } from '../../Containers/filterLayoutFakeProps';
|
||||
import elementsSizeStyles from '../../Containers/shared/ElementsSize.m.css';
|
||||
import { s } from '../../s';
|
||||
import { useS } from '../../useS';
|
||||
import { Field } from '../Field';
|
||||
|
||||
@@ -21,7 +21,7 @@ export interface IChangeData {
|
||||
export interface IFormContext {
|
||||
ref: HTMLFormElement | null;
|
||||
onValidate: SyncExecutor;
|
||||
onSubmit: SyncExecutor;
|
||||
onSubmit: SyncExecutor<SubmitEvent | undefined>;
|
||||
onChange: IExecutor<IChangeData>;
|
||||
parent: IFormContext | null;
|
||||
disableEnterSubmit: boolean;
|
||||
|
||||
@@ -86,6 +86,7 @@ export const Textarea: TextareaType = observer(function Textarea({
|
||||
</FieldLabel>
|
||||
<textarea
|
||||
{...rest}
|
||||
required={required}
|
||||
className={s(styles, { textarea: true })}
|
||||
value={value ?? ''}
|
||||
name={name}
|
||||
|
||||
@@ -22,7 +22,7 @@ interface IOptions {
|
||||
|
||||
export function useForm(options?: IOptions): IFormContext {
|
||||
let parentForm = useContext(FormContext);
|
||||
const [submittingExecutor] = useState(() => new SyncExecutor());
|
||||
const [submittingExecutor] = useState(() => new SyncExecutor<SubmitEvent | undefined>());
|
||||
const [validationExecutor] = useState(() => new SyncExecutor());
|
||||
const [changeExecutor] = useState(() => new Executor<IChangeData>());
|
||||
|
||||
@@ -49,7 +49,7 @@ export function useForm(options?: IOptions): IFormContext {
|
||||
|
||||
useExecutor({
|
||||
executor: submittingExecutor,
|
||||
handlers: [() => options?.onSubmit?.()],
|
||||
handlers: [event => options?.onSubmit?.(event)],
|
||||
});
|
||||
|
||||
const context = useObjectRef<IFormContext>(
|
||||
@@ -93,7 +93,7 @@ export function useForm(options?: IOptions): IFormContext {
|
||||
event?.preventDefault();
|
||||
|
||||
if (this.validate()) {
|
||||
this.onSubmit.execute();
|
||||
this.onSubmit.execute(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+9
@@ -70,6 +70,7 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
|
||||
const controlType = getPropertyControlType(property);
|
||||
const password = property.features.includes('password');
|
||||
const required = property.required && !readOnly;
|
||||
|
||||
const value = getValue(property.value, controlType);
|
||||
const defaultValue = getValue(property.defaultValue, controlType);
|
||||
@@ -79,6 +80,7 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
if (controlType === 'file' && state) {
|
||||
return (
|
||||
<InputFileTextContent
|
||||
required={required}
|
||||
tooltip={property.description}
|
||||
labelTooltip={property.displayName || property.description}
|
||||
name={property.id!}
|
||||
@@ -118,6 +120,7 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
if (state !== undefined) {
|
||||
return (
|
||||
<FieldCheckbox
|
||||
required={required}
|
||||
id={property.id}
|
||||
name={property.id!}
|
||||
state={state}
|
||||
@@ -149,6 +152,7 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
if (state !== undefined) {
|
||||
return (
|
||||
<Combobox
|
||||
required={required}
|
||||
name={property.id!}
|
||||
state={state}
|
||||
items={property.validValues!}
|
||||
@@ -168,6 +172,7 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
|
||||
return (
|
||||
<Combobox
|
||||
required={required}
|
||||
name={property.id!}
|
||||
items={property.validValues!}
|
||||
keySelector={value => value}
|
||||
@@ -188,6 +193,7 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
if (state !== undefined) {
|
||||
return (
|
||||
<Textarea
|
||||
required={required}
|
||||
title={state[property.id!]}
|
||||
labelTooltip={property.description || property.displayName}
|
||||
description={description}
|
||||
@@ -204,6 +210,7 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
|
||||
return (
|
||||
<Textarea
|
||||
required={required}
|
||||
title={value}
|
||||
labelTooltip={property.description || property.displayName}
|
||||
description={description}
|
||||
@@ -221,6 +228,7 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
if (state !== undefined) {
|
||||
return (
|
||||
<InputField
|
||||
required={required}
|
||||
type={password ? 'password' : 'text'}
|
||||
title={password ? property.description || property.displayName : state[property.id!]}
|
||||
labelTooltip={property.description || property.displayName}
|
||||
@@ -243,6 +251,7 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
|
||||
return (
|
||||
<InputField
|
||||
required={required}
|
||||
type={password ? 'password' : 'text'}
|
||||
title={password ? property.description || property.displayName : value}
|
||||
labelTooltip={property.description || property.displayName}
|
||||
|
||||
@@ -22,6 +22,8 @@ interface IUseExecutorOptions<T> {
|
||||
export function useExecutor<T>(options: IUseExecutorOptions<T>): void {
|
||||
const props = useObjectRef(options);
|
||||
const executor = props.executor;
|
||||
const next: IExecutorHandlersCollection<T> | undefined = props.next;
|
||||
const before: IExecutorHandlersCollection<T> | undefined = props.before;
|
||||
|
||||
useEffect(() => {
|
||||
if (!executor) {
|
||||
@@ -30,8 +32,6 @@ export function useExecutor<T>(options: IUseExecutorOptions<T>): void {
|
||||
|
||||
const handlers: Array<IExecutorHandler<T>> = [];
|
||||
const postHandlers: Array<IExecutorHandler<T>> = [];
|
||||
const next: IExecutorHandlersCollection<T> | undefined = props.next;
|
||||
const before: IExecutorHandlersCollection<T> | undefined = props.before;
|
||||
|
||||
if (props.handlers) {
|
||||
for (let i = 0; i < props.handlers.length; i++) {
|
||||
@@ -49,12 +49,12 @@ export function useExecutor<T>(options: IUseExecutorOptions<T>): void {
|
||||
}
|
||||
}
|
||||
|
||||
if (props.next) {
|
||||
executor.next(props.next);
|
||||
if (before) {
|
||||
executor.before(before);
|
||||
}
|
||||
|
||||
if (props.before) {
|
||||
executor.next(props.before);
|
||||
if (next) {
|
||||
executor.next(next);
|
||||
}
|
||||
|
||||
return () => {
|
||||
@@ -74,5 +74,7 @@ export function useExecutor<T>(options: IUseExecutorOptions<T>): void {
|
||||
executor.removeBefore(before);
|
||||
}
|
||||
};
|
||||
}, [executor, props.handlers?.length, props.postHandlers?.length]);
|
||||
}, [
|
||||
executor, props.handlers?.length, props.postHandlers?.length, before, next,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { IAsyncContextLoader, IContextLoader, ISyncContextLoader } from './
|
||||
import type { IExecutorHandler } from './IExecutorHandler';
|
||||
import type { ExecutorDataFilter, ExecutorDataMap, IChainLink, IExecutorHandlersCollection } from './IExecutorHandlersCollection';
|
||||
|
||||
export class ExecutorHandlersCollection<T = unknown, TResult = any | Promise<any>> implements IExecutorHandlersCollection<T, TResult> {
|
||||
export class ExecutorHandlersCollection<T = void, TResult = any | Promise<any>> implements IExecutorHandlersCollection<T, TResult> {
|
||||
handlers: Array<IExecutorHandler<T, TResult>>;
|
||||
postHandlers: Array<IExecutorHandler<T, TResult>>;
|
||||
chain: Array<IChainLink<T, TResult>>;
|
||||
|
||||
@@ -7,6 +7,7 @@ query getAuthModels {
|
||||
requiresLocalConfiguration
|
||||
requiredAuth
|
||||
properties {
|
||||
required
|
||||
id
|
||||
displayName
|
||||
description
|
||||
|
||||
@@ -9,6 +9,7 @@ query getDataTransferProcessors {
|
||||
order
|
||||
icon
|
||||
properties {
|
||||
required
|
||||
id
|
||||
displayName
|
||||
description
|
||||
|
||||
@@ -10,4 +10,5 @@ fragment AuthProviderConfigurationParameters on ObjectPropertyInfo {
|
||||
length
|
||||
features
|
||||
order
|
||||
required
|
||||
}
|
||||
@@ -7,6 +7,7 @@ fragment AuthProviderInfo on AuthProviderInfo {
|
||||
trusted
|
||||
private
|
||||
supportProvisioning
|
||||
required
|
||||
|
||||
#configurationParameters {
|
||||
# id
|
||||
|
||||
@@ -10,4 +10,5 @@ fragment DriverProviderPropertyInfo on ObjectPropertyInfo {
|
||||
features
|
||||
order
|
||||
supportedConfigurationTypes
|
||||
required
|
||||
}
|
||||
@@ -8,4 +8,5 @@ fragment NavNodeProperties on ObjectPropertyInfo {
|
||||
features
|
||||
value
|
||||
order
|
||||
required
|
||||
}
|
||||
@@ -6,6 +6,7 @@ fragment ObjectOriginInfo on ObjectOrigin {
|
||||
|
||||
details @include(if: $customIncludeOriginDetails) {
|
||||
id
|
||||
required
|
||||
displayName
|
||||
description
|
||||
category
|
||||
|
||||
@@ -4,6 +4,7 @@ fragment ObjectPropertyInfo on ObjectPropertyInfo {
|
||||
description
|
||||
hint
|
||||
category
|
||||
required
|
||||
dataType
|
||||
value
|
||||
validValues
|
||||
|
||||
@@ -9,5 +9,6 @@ fragment UserConnectionAuthProperties on ObjectPropertyInfo {
|
||||
defaultValue
|
||||
length
|
||||
features
|
||||
required
|
||||
order
|
||||
}
|
||||
+1
@@ -10,4 +10,5 @@ fragment UserConnectionNetworkHandlerProperties on ObjectPropertyInfo {
|
||||
order
|
||||
length
|
||||
features
|
||||
required
|
||||
}
|
||||
@@ -15,6 +15,7 @@ mutation getSqlExecutionPlanResult(
|
||||
description
|
||||
properties {
|
||||
id
|
||||
required
|
||||
category
|
||||
dataType
|
||||
description
|
||||
|
||||
@@ -6,74 +6,44 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext, useEffect, useMemo, useRef } from 'react';
|
||||
import { TabPanel as BaseTabPanel, TabStateReturn } from 'reakit/Tab';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import { TabPanel as BaseTabPanel } from 'reakit/Tab';
|
||||
|
||||
import { FormContext, getComputed, Loader } from '@cloudbeaver/core-blocks';
|
||||
import { getComputed, Loader } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import { DATA_CONTEXT_TABS_FORM_VALIDATION } from './DATA_CONTEXT_TABS_FORM_VALIDATION';
|
||||
import { TabContext } from './TabContext';
|
||||
import type { TabPanelProps } from './TabPanelProps';
|
||||
import { TabsContext } from './TabsContext';
|
||||
import { useTabsValidation } from './useTabsValidation';
|
||||
|
||||
export const TabPanel: React.FC<TabPanelProps> = observer(function TabPanel({ tabId, children, className, lazy }) {
|
||||
const state = useContext(TabsContext);
|
||||
const formContext = useContext(FormContext);
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const tabContextState = useContext(TabsContext);
|
||||
|
||||
if (!state) {
|
||||
if (!tabContextState) {
|
||||
throw new Error('Tabs context was not provided');
|
||||
}
|
||||
|
||||
const validationTabId = state.context.get(DATA_CONTEXT_TABS_FORM_VALIDATION);
|
||||
const panelRef = useTabsValidation(tabId);
|
||||
const tabContext = useMemo(() => ({ tabId }), [tabId]);
|
||||
const selected = getComputed(() => state.state.selectedId === tabId);
|
||||
const enabled = getComputed(() => (lazy || state.lazy) && !selected);
|
||||
|
||||
useEffect(() => {
|
||||
if (formContext === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
async function trackValidity(event: Event) {
|
||||
if (selected || state?.context.get(DATA_CONTEXT_TABS_FORM_VALIDATION) !== null) {
|
||||
return;
|
||||
}
|
||||
state?.context.set(DATA_CONTEXT_TABS_FORM_VALIDATION, tabId);
|
||||
await state?.open(tabId);
|
||||
}
|
||||
|
||||
panelRef.current?.addEventListener('invalid', trackValidity, true);
|
||||
return () => {
|
||||
panelRef.current?.removeEventListener('invalid', trackValidity, true);
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (selected && validationTabId === tabId) {
|
||||
formContext?.validate();
|
||||
state?.context.set(DATA_CONTEXT_TABS_FORM_VALIDATION, null);
|
||||
}
|
||||
});
|
||||
const selected = getComputed(() => tabContextState.state.selectedId === tabId);
|
||||
const enabled = getComputed(() => (lazy || tabContextState.lazy) && !selected);
|
||||
|
||||
if (enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof children === 'function') {
|
||||
return (
|
||||
<TabContext.Provider value={tabContext}>
|
||||
<BaseTabPanel ref={panelRef} {...state.state} tabId={tabId} className={className}>
|
||||
<Loader suspense>{(children as (state: TabStateReturn) => React.ReactNode)(state.state)}</Loader>
|
||||
</BaseTabPanel>
|
||||
</TabContext.Provider>
|
||||
);
|
||||
function renderChildren() {
|
||||
if (typeof children === 'function') {
|
||||
return children(tabContextState!.state);
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
return (
|
||||
<TabContext.Provider value={tabContext}>
|
||||
<BaseTabPanel ref={panelRef} {...state.state} tabId={tabId} className={className}>
|
||||
<Loader suspense>{children}</Loader>
|
||||
<BaseTabPanel ref={panelRef} {...tabContextState.state} tabId={tabId} className={className}>
|
||||
<Loader suspense>{renderChildren()}</Loader>
|
||||
</BaseTabPanel>
|
||||
</TabContext.Provider>
|
||||
);
|
||||
|
||||
@@ -17,6 +17,7 @@ import { isDefined, isNull, isUndefined, MetadataMap, MetadataValueGetter } from
|
||||
|
||||
import type { ITabData, ITabInfo, ITabsContainer } from './TabsContainer/ITabsContainer';
|
||||
import { ITabsContext, type TabDirection, TabsContext } from './TabsContext';
|
||||
import { TabsValidationProvider } from './TabsValidationProvider';
|
||||
|
||||
type ExtractContainerProps<T> = T extends void ? Record<string, any> : T;
|
||||
|
||||
@@ -292,5 +293,9 @@ export const TabsState = observer(function TabsState<T = Record<string, any>>({
|
||||
);
|
||||
}
|
||||
|
||||
return <TabsContext.Provider value={value}>{children}</TabsContext.Provider>;
|
||||
return (
|
||||
<TabsContext.Provider value={value}>
|
||||
<TabsValidationProvider>{children}</TabsValidationProvider>
|
||||
</TabsContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
+6
-2
@@ -5,6 +5,10 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { createDataContext } from '@cloudbeaver/core-data-context';
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const DATA_CONTEXT_TABS_FORM_VALIDATION = createDataContext<string | null>('Tabs Form Validation', () => null);
|
||||
export interface ITabsValidationContext {
|
||||
invalidate: (tabId: string) => void;
|
||||
}
|
||||
|
||||
export const TabsValidationContext = createContext<ITabsValidationContext | null>(null);
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 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 { useObjectRef } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import { isDefined, isNotNullDefined } from '@cloudbeaver/core-utils';
|
||||
|
||||
import { TabsContext } from './TabsContext';
|
||||
import { TabsValidationContext } from './TabsValidationContext';
|
||||
|
||||
export const TabsValidationProvider = observer(function TabsValidation({ children }: React.PropsWithChildren) {
|
||||
const tabsContext = useContext(TabsContext);
|
||||
const notificationService = useService(NotificationService);
|
||||
|
||||
if (!tabsContext) {
|
||||
throw new Error('TabsState should be defined');
|
||||
}
|
||||
|
||||
const innerState = useObjectRef(
|
||||
() => ({
|
||||
invalidTabs: new Set<string>(),
|
||||
scheduledTabSwitch: -1,
|
||||
invalidate(tabId: string) {
|
||||
this.invalidTabs.add(tabId);
|
||||
this.scheduleTabSwitch();
|
||||
},
|
||||
selectNextInvalidTab() {
|
||||
const next = Array.from(this.invalidTabs)[0] as string | undefined;
|
||||
|
||||
if (!isDefined(next) || (isNotNullDefined(this.tabsContext.state.selectedId) && this.invalidTabs.has(this.tabsContext.state.selectedId))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.tabsContext.open(next).catch(() => {
|
||||
this.notificationService.logError({ title: 'core_ui_form_save_error', message: 'core_ui_switch_tab_error' });
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
this.invalidTabs.clear();
|
||||
},
|
||||
scheduleTabSwitch() {
|
||||
this.clearScheduledTabSwitch();
|
||||
|
||||
this.scheduledTabSwitch = setTimeout(() => {
|
||||
this.scheduledTabSwitch = -1;
|
||||
|
||||
this.selectNextInvalidTab();
|
||||
this.reset();
|
||||
}, 0) as any;
|
||||
},
|
||||
clearScheduledTabSwitch() {
|
||||
if (this.scheduledTabSwitch !== -1) {
|
||||
clearTimeout(this.scheduledTabSwitch);
|
||||
this.scheduledTabSwitch = -1;
|
||||
}
|
||||
},
|
||||
}),
|
||||
{ tabsContext, notificationService },
|
||||
['invalidate'],
|
||||
);
|
||||
|
||||
return <TabsValidationContext.Provider value={innerState}>{children}</TabsValidationContext.Provider>;
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 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 { useContext, useEffect, useRef } from 'react';
|
||||
|
||||
import { FormContext, getComputed, useExecutor, useObjectRef } from '@cloudbeaver/core-blocks';
|
||||
import { ExecutorHandlersCollection } from '@cloudbeaver/core-executor';
|
||||
|
||||
import { TabsContext } from './TabsContext';
|
||||
import { TabsValidationContext } from './TabsValidationContext';
|
||||
|
||||
export function useTabsValidation(tabId: string): React.RefObject<HTMLDivElement> {
|
||||
const tabContextState = useContext(TabsContext);
|
||||
const formContext = useContext(FormContext);
|
||||
|
||||
if (!tabContextState) {
|
||||
throw new Error('Tabs context was not provided');
|
||||
}
|
||||
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const tabsValidationContext = useContext(TabsValidationContext);
|
||||
const selected = getComputed(() => tabContextState.state.selectedId === tabId);
|
||||
|
||||
const tabPropsRef = useObjectRef({ tabId, tabsValidationContext });
|
||||
const validationState = useObjectRef(
|
||||
() => ({
|
||||
beforeValidationCollection: new ExecutorHandlersCollection(),
|
||||
invalidElement: null as HTMLInputElement | null,
|
||||
invalidate(event: Event) {
|
||||
tabPropsRef.tabsValidationContext?.invalidate(tabPropsRef.tabId);
|
||||
|
||||
// we want to store only first element in order in the form
|
||||
if (this.invalidElement === null) {
|
||||
this.invalidElement = event.target as HTMLInputElement | null;
|
||||
}
|
||||
},
|
||||
reportValidity() {
|
||||
if (this.invalidElement) {
|
||||
this.invalidElement.reportValidity();
|
||||
this.reset();
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.invalidElement = null;
|
||||
},
|
||||
}),
|
||||
false,
|
||||
['invalidate'],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const element = panelRef.current;
|
||||
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.addEventListener('invalid', validationState.invalidate, true);
|
||||
return () => {
|
||||
element.removeEventListener('invalid', validationState.invalidate, true);
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (selected) {
|
||||
validationState.reportValidity();
|
||||
}
|
||||
}, [selected]);
|
||||
|
||||
useExecutor({
|
||||
executor: formContext?.onValidate,
|
||||
before: validationState.beforeValidationCollection,
|
||||
});
|
||||
|
||||
useExecutor({
|
||||
executor: validationState.beforeValidationCollection,
|
||||
handlers: [
|
||||
() => {
|
||||
validationState.reset();
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return panelRef;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
export default [
|
||||
['core_ui_empty', 'Empty'],
|
||||
['core_ui_form_save_error', 'Error occurred when saving the form'],
|
||||
['core_ui_switch_tab_error', 'Cannot switch tab during form validation'],
|
||||
];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export default [
|
||||
['core_ui_empty', 'Empty'],
|
||||
['core_ui_form_save_error', 'Error occurred when saving the form'],
|
||||
['core_ui_switch_tab_error', 'Cannot switch tab during form validation'],
|
||||
];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export default [
|
||||
['core_ui_empty', 'Пусто'],
|
||||
['core_ui_form_save_error', 'Ошибка при сохранении формы'],
|
||||
['core_ui_switch_tab_error', 'Невозможно переключить вкладку во время валидации формы'],
|
||||
];
|
||||
|
||||
+36
-27
@@ -9,14 +9,14 @@ import { observer } from 'mobx-react-lite';
|
||||
import { useEffect } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { IconOrImage, Loader, Placeholder, useExecutor, useObjectRef, useStyles, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { Form, IconOrImage, Loader, Placeholder, useExecutor, useForm, useObjectRef, useStyles, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import type { AdminAuthProviderConfiguration } from '@cloudbeaver/core-sdk';
|
||||
import { BASE_TAB_STYLES, TabList, TabPanelList, TabsState, UNDERLINE_TAB_BIG_STYLES, UNDERLINE_TAB_STYLES } from '@cloudbeaver/core-ui';
|
||||
|
||||
import { AuthConfigurationFormService } from './AuthConfigurationFormService';
|
||||
import { authConfigurationContext } from './Contexts/authConfigurationContext';
|
||||
import type { IAuthConfigurationFormState } from './IAuthConfigurationFormProps';
|
||||
import type { IAuthConfigurationFormActions, IAuthConfigurationFormState } from './IAuthConfigurationFormProps';
|
||||
|
||||
const tabsStyles = css`
|
||||
TabList {
|
||||
@@ -100,6 +100,12 @@ export const AuthConfigurationForm = observer<Props>(function AuthConfigurationF
|
||||
const style = useStyles(BASE_TAB_STYLES, tabsStyles, UNDERLINE_TAB_STYLES, UNDERLINE_TAB_BIG_STYLES);
|
||||
const styles = useStyles(style, topBarStyles, formStyles);
|
||||
const service = useService(AuthConfigurationFormService);
|
||||
const form = useForm({
|
||||
onSubmit: async () => {
|
||||
await state.save();
|
||||
},
|
||||
});
|
||||
const actions = useObjectRef<IAuthConfigurationFormActions>({ save: form.submit });
|
||||
|
||||
useExecutor({
|
||||
executor: state.submittingTask,
|
||||
@@ -121,30 +127,33 @@ export const AuthConfigurationForm = observer<Props>(function AuthConfigurationF
|
||||
}, []);
|
||||
|
||||
return styled(styles)(
|
||||
<TabsState container={service.tabsContainer} state={state} onCancel={onCancel}>
|
||||
<box className={className}>
|
||||
<configuration-top-bar>
|
||||
<configuration-top-bar-tabs>
|
||||
<configuration-status-message>
|
||||
{state.statusMessage && (
|
||||
<>
|
||||
<IconOrImage icon="/icons/info_icon.svg" />
|
||||
{translate(state.statusMessage)}
|
||||
</>
|
||||
)}
|
||||
</configuration-status-message>
|
||||
<TabList style={style} disabled={false} />
|
||||
</configuration-top-bar-tabs>
|
||||
<configuration-top-bar-actions>
|
||||
<Loader suspense inline hideMessage hideException>
|
||||
<Placeholder container={service.actionsContainer} state={state} onCancel={onCancel} />
|
||||
</Loader>
|
||||
</configuration-top-bar-actions>
|
||||
</configuration-top-bar>
|
||||
<content-box>
|
||||
<TabPanelList style={style} />
|
||||
</content-box>
|
||||
</box>
|
||||
</TabsState>,
|
||||
<Form context={form}>
|
||||
<TabsState actions={actions} container={service.tabsContainer} state={state} onCancel={onCancel}>
|
||||
<box className={className}>
|
||||
<configuration-top-bar>
|
||||
<configuration-top-bar-tabs>
|
||||
<configuration-status-message>
|
||||
{state.statusMessage && (
|
||||
<>
|
||||
<IconOrImage icon="/icons/info_icon.svg" />
|
||||
{translate(state.statusMessage)}
|
||||
</>
|
||||
)}
|
||||
</configuration-status-message>
|
||||
<TabList style={style} disabled={false} />
|
||||
</configuration-top-bar-tabs>
|
||||
<configuration-top-bar-actions>
|
||||
<Loader suspense inline hideMessage hideException>
|
||||
<Placeholder actions={actions} container={service.actionsContainer} state={state} onCancel={onCancel} />
|
||||
</Loader>
|
||||
</configuration-top-bar-actions>
|
||||
</configuration-top-bar>
|
||||
<content-box>
|
||||
<TabPanelList style={style} />
|
||||
</content-box>
|
||||
</box>
|
||||
</TabsState>
|
||||
</Form>
|
||||
,
|
||||
);
|
||||
});
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ import { Button, PlaceholderComponent, useTranslate } from '@cloudbeaver/core-bl
|
||||
import type { IAuthConfigurationFormProps } from './IAuthConfigurationFormProps';
|
||||
|
||||
export const AuthConfigurationFormBaseActions: PlaceholderComponent<IAuthConfigurationFormProps> = observer(
|
||||
function AuthConfigurationFormBaseActions({ state, onCancel }) {
|
||||
function AuthConfigurationFormBaseActions({ state, onCancel, actions }) {
|
||||
const translate = useTranslate();
|
||||
|
||||
return (
|
||||
@@ -22,7 +22,7 @@ export const AuthConfigurationFormBaseActions: PlaceholderComponent<IAuthConfigu
|
||||
{translate('ui_processing_cancel')}
|
||||
</Button>
|
||||
)}
|
||||
<Button type="button" disabled={state.disabled || state.readonly} mod={['unelevated']} loader onClick={state.save}>
|
||||
<Button type="button" disabled={state.disabled || state.readonly} mod={['unelevated']} loader onClick={actions.save}>
|
||||
{translate(state.mode === 'edit' ? 'ui_processing_save' : 'ui_processing_finish')}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
+5
@@ -30,8 +30,13 @@ export interface IAuthConfigurationFormState {
|
||||
readonly setOptions: (mode: AuthConfigurationFormMode) => this;
|
||||
}
|
||||
|
||||
export interface IAuthConfigurationFormActions {
|
||||
save: () => void;
|
||||
}
|
||||
|
||||
export interface IAuthConfigurationFormProps {
|
||||
state: IAuthConfigurationFormState;
|
||||
actions: IAuthConfigurationFormActions;
|
||||
onCancel?: () => void;
|
||||
}
|
||||
|
||||
|
||||
+117
-122
@@ -14,7 +14,6 @@ import {
|
||||
ColoredContainer,
|
||||
Combobox,
|
||||
FieldCheckbox,
|
||||
Form,
|
||||
Group,
|
||||
GroupTitle,
|
||||
InputField,
|
||||
@@ -45,8 +44,6 @@ const emptyArray: AuthProviderConfigurationParametersFragment[] = [];
|
||||
export const AuthConfigurationOptions: TabContainerPanelComponent<IAuthConfigurationFormProps> = observer(function AuthConfigurationOptions({
|
||||
state,
|
||||
}) {
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
|
||||
const translate = useTranslate();
|
||||
const copy = useClipboard();
|
||||
const style = useStyles(styles);
|
||||
@@ -63,133 +60,131 @@ export const AuthConfigurationOptions: TabContainerPanelComponent<IAuthConfigura
|
||||
}, [state]);
|
||||
|
||||
return styled(style)(
|
||||
<Form ref={formRef}>
|
||||
<ColoredContainer parent gap overflow>
|
||||
<ColoredContainer parent gap overflow>
|
||||
<Group small gap>
|
||||
<Combobox
|
||||
name="providerId"
|
||||
state={state.config}
|
||||
items={providers.resource.configurable}
|
||||
keySelector={provider => provider.id}
|
||||
valueSelector={provider => provider.label}
|
||||
iconSelector={provider => provider.icon}
|
||||
titleSelector={provider => provider.description}
|
||||
placeholder={translate('administration_identity_providers_choose_provider_placeholder')}
|
||||
readOnly={state.readonly || edit}
|
||||
disabled={state.disabled}
|
||||
required
|
||||
tiny
|
||||
fill
|
||||
onSelect={handleProviderSelect}
|
||||
>
|
||||
{translate('administration_identity_providers_provider')}
|
||||
</Combobox>
|
||||
<InputField name="id" state={state.config} readOnly={state.readonly || edit} disabled={state.disabled} required tiny fill>
|
||||
{translate('administration_identity_providers_provider_id')}
|
||||
</InputField>
|
||||
<InputField name="displayName" state={state.config} minLength={1} disabled={state.disabled} readOnly={state.readonly} required tiny fill>
|
||||
{translate('administration_identity_providers_provider_configuration_name')}
|
||||
</InputField>
|
||||
<Textarea name="description" state={state.config} disabled={state.disabled} readOnly={state.readonly}>
|
||||
{translate('administration_identity_providers_provider_configuration_description')}
|
||||
</Textarea>
|
||||
<InputField name="iconURL" state={state.config} disabled={state.disabled} readOnly={state.readonly}>
|
||||
{translate('administration_identity_providers_provider_configuration_icon_url')}
|
||||
</InputField>
|
||||
<FieldCheckbox
|
||||
id={edit ? state.config.id : 'AuthConfigurationDisabled'}
|
||||
name="disabled"
|
||||
state={state.config}
|
||||
disabled={state.disabled}
|
||||
readOnly={state.readonly}
|
||||
>
|
||||
{translate('administration_identity_providers_provider_configuration_disabled')}
|
||||
</FieldCheckbox>
|
||||
</Group>
|
||||
{parameters.isLoaded() && parameters.data && (
|
||||
<>
|
||||
{isUncategorizedExists && (
|
||||
<Group small gap vertical>
|
||||
<GroupTitle>{translate('administration_identity_providers_provider_configuration_parameters')}</GroupTitle>
|
||||
<ObjectPropertyInfoForm
|
||||
state={state.config.parameters}
|
||||
properties={parameters.data}
|
||||
category={null}
|
||||
disabled={state.disabled}
|
||||
readOnly={state.readonly}
|
||||
/>
|
||||
</Group>
|
||||
)}
|
||||
{categories.map(category => (
|
||||
<Group key={category} small gap vertical>
|
||||
<GroupTitle keepSize>{category}</GroupTitle>
|
||||
<ObjectPropertyInfoForm
|
||||
state={state.config.parameters}
|
||||
properties={parameters.data!}
|
||||
category={category}
|
||||
disabled={state.disabled}
|
||||
readOnly={state.readonly}
|
||||
keepSize
|
||||
/>
|
||||
</Group>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{(state.config.metadataLink || state.config.signInLink || state.config.signOutLink || state.config.acsLink) && (
|
||||
<Group small gap>
|
||||
<Combobox
|
||||
name="providerId"
|
||||
<GroupTitle>{translate('administration_identity_providers_provider_configuration_links')}</GroupTitle>
|
||||
<InputField
|
||||
name="signInLink"
|
||||
state={state.config}
|
||||
items={providers.resource.configurable}
|
||||
keySelector={provider => provider.id}
|
||||
valueSelector={provider => provider.label}
|
||||
iconSelector={provider => provider.icon}
|
||||
titleSelector={provider => provider.description}
|
||||
placeholder={translate('administration_identity_providers_choose_provider_placeholder')}
|
||||
readOnly={state.readonly || edit}
|
||||
title={state.config.signInLink}
|
||||
disabled={state.disabled}
|
||||
required
|
||||
tiny
|
||||
fill
|
||||
onSelect={handleProviderSelect}
|
||||
autoHide
|
||||
readOnly
|
||||
onCustomCopy={() => copy(state.config.signInLink!, true)}
|
||||
>
|
||||
{translate('administration_identity_providers_provider')}
|
||||
</Combobox>
|
||||
<InputField name="id" state={state.config} readOnly={state.readonly || edit} disabled={state.disabled} required tiny fill>
|
||||
{translate('administration_identity_providers_provider_id')}
|
||||
Sign in
|
||||
</InputField>
|
||||
<InputField name="displayName" state={state.config} minLength={1} disabled={state.disabled} readOnly={state.readonly} required tiny fill>
|
||||
{translate('administration_identity_providers_provider_configuration_name')}
|
||||
</InputField>
|
||||
<Textarea name="description" state={state.config} disabled={state.disabled} readOnly={state.readonly}>
|
||||
{translate('administration_identity_providers_provider_configuration_description')}
|
||||
</Textarea>
|
||||
<InputField name="iconURL" state={state.config} disabled={state.disabled} readOnly={state.readonly}>
|
||||
{translate('administration_identity_providers_provider_configuration_icon_url')}
|
||||
</InputField>
|
||||
<FieldCheckbox
|
||||
id={edit ? state.config.id : 'AuthConfigurationDisabled'}
|
||||
name="disabled"
|
||||
<InputField
|
||||
name="signOutLink"
|
||||
state={state.config}
|
||||
title={state.config.signOutLink}
|
||||
disabled={state.disabled}
|
||||
readOnly={state.readonly}
|
||||
autoHide
|
||||
readOnly
|
||||
onCustomCopy={() => copy(state.config.signOutLink!, true)}
|
||||
>
|
||||
{translate('administration_identity_providers_provider_configuration_disabled')}
|
||||
</FieldCheckbox>
|
||||
Sign out
|
||||
</InputField>
|
||||
<InputField
|
||||
name="redirectLink"
|
||||
state={state.config}
|
||||
title={state.config.redirectLink}
|
||||
disabled={state.disabled}
|
||||
autoHide
|
||||
readOnly
|
||||
onCustomCopy={() => copy(state.config.redirectLink!, true)}
|
||||
>
|
||||
Redirect
|
||||
</InputField>
|
||||
<InputField
|
||||
name="acsLink"
|
||||
state={state.config}
|
||||
title={state.config.acsLink}
|
||||
disabled={state.disabled}
|
||||
autoHide
|
||||
readOnly
|
||||
onCustomCopy={() => copy(state.config.acsLink!, true)}
|
||||
>
|
||||
ACS
|
||||
</InputField>
|
||||
{state.config.metadataLink && (
|
||||
<Link href={state.config.metadataLink} target="_blank" rel="noopener noreferrer">
|
||||
{translate('administration_identity_providers_provider_configuration_links_metadata')}
|
||||
</Link>
|
||||
)}
|
||||
</Group>
|
||||
{parameters.isLoaded() && parameters.data && (
|
||||
<>
|
||||
{isUncategorizedExists && (
|
||||
<Group small gap vertical>
|
||||
<GroupTitle>{translate('administration_identity_providers_provider_configuration_parameters')}</GroupTitle>
|
||||
<ObjectPropertyInfoForm
|
||||
state={state.config.parameters}
|
||||
properties={parameters.data}
|
||||
category={null}
|
||||
disabled={state.disabled}
|
||||
readOnly={state.readonly}
|
||||
/>
|
||||
</Group>
|
||||
)}
|
||||
{categories.map(category => (
|
||||
<Group key={category} small gap vertical>
|
||||
<GroupTitle keepSize>{category}</GroupTitle>
|
||||
<ObjectPropertyInfoForm
|
||||
state={state.config.parameters}
|
||||
properties={parameters.data!}
|
||||
category={category}
|
||||
disabled={state.disabled}
|
||||
readOnly={state.readonly}
|
||||
keepSize
|
||||
/>
|
||||
</Group>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{(state.config.metadataLink || state.config.signInLink || state.config.signOutLink || state.config.acsLink) && (
|
||||
<Group small gap>
|
||||
<GroupTitle>{translate('administration_identity_providers_provider_configuration_links')}</GroupTitle>
|
||||
<InputField
|
||||
name="signInLink"
|
||||
state={state.config}
|
||||
title={state.config.signInLink}
|
||||
disabled={state.disabled}
|
||||
autoHide
|
||||
readOnly
|
||||
onCustomCopy={() => copy(state.config.signInLink!, true)}
|
||||
>
|
||||
Sign in
|
||||
</InputField>
|
||||
<InputField
|
||||
name="signOutLink"
|
||||
state={state.config}
|
||||
title={state.config.signOutLink}
|
||||
disabled={state.disabled}
|
||||
autoHide
|
||||
readOnly
|
||||
onCustomCopy={() => copy(state.config.signOutLink!, true)}
|
||||
>
|
||||
Sign out
|
||||
</InputField>
|
||||
<InputField
|
||||
name="redirectLink"
|
||||
state={state.config}
|
||||
title={state.config.redirectLink}
|
||||
disabled={state.disabled}
|
||||
autoHide
|
||||
readOnly
|
||||
onCustomCopy={() => copy(state.config.redirectLink!, true)}
|
||||
>
|
||||
Redirect
|
||||
</InputField>
|
||||
<InputField
|
||||
name="acsLink"
|
||||
state={state.config}
|
||||
title={state.config.acsLink}
|
||||
disabled={state.disabled}
|
||||
autoHide
|
||||
readOnly
|
||||
onCustomCopy={() => copy(state.config.acsLink!, true)}
|
||||
>
|
||||
ACS
|
||||
</InputField>
|
||||
{state.config.metadataLink && (
|
||||
<Link href={state.config.metadataLink} target="_blank" rel="noopener noreferrer">
|
||||
{translate('administration_identity_providers_provider_configuration_links_metadata')}
|
||||
</Link>
|
||||
)}
|
||||
</Group>
|
||||
)}
|
||||
</ColoredContainer>
|
||||
</Form>,
|
||||
)}
|
||||
</ColoredContainer>
|
||||
);
|
||||
});
|
||||
|
||||
+16
-20
@@ -6,10 +6,9 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useRef } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { ColoredContainer, Form, Group, InputField, Textarea, useResource, useStyles, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { ColoredContainer, Group, InputField, Textarea, useResource, useStyles, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
import type { TabContainerPanelComponent } from '@cloudbeaver/core-ui';
|
||||
|
||||
@@ -27,27 +26,24 @@ const styles = css`
|
||||
export const TeamOptions: TabContainerPanelComponent<ITeamFormProps> = observer(function TeamOptions({ state }) {
|
||||
const serverConfigResource = useResource(TeamOptions, ServerConfigResource, undefined);
|
||||
const style = useStyles(styles);
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
const translate = useTranslate();
|
||||
const edit = state.mode === 'edit';
|
||||
|
||||
return styled(style)(
|
||||
<Form ref={formRef}>
|
||||
<ColoredContainer parent gap overflow>
|
||||
<Group small gap>
|
||||
<InputField name="teamId" state={state.config} readOnly={state.readonly || edit} disabled={state.disabled} required tiny fill>
|
||||
{translate('administration_teams_team_id')}
|
||||
</InputField>
|
||||
<InputField name="teamName" state={state.config} readOnly={state.readonly} disabled={state.disabled} tiny fill>
|
||||
{translate('administration_teams_team_name')}
|
||||
</InputField>
|
||||
<Textarea name="description" state={state.config} readOnly={state.readonly} disabled={state.disabled} tiny fill>
|
||||
{translate('administration_teams_team_description')}
|
||||
</Textarea>
|
||||
</Group>
|
||||
{!serverConfigResource.resource.distributed && <Permissions state={state} />}
|
||||
<TeamMetaParameters state={state} />
|
||||
</ColoredContainer>
|
||||
</Form>,
|
||||
<ColoredContainer parent gap overflow>
|
||||
<Group small gap>
|
||||
<InputField name="teamId" state={state.config} readOnly={state.readonly || edit} disabled={state.disabled} required tiny fill>
|
||||
{translate('administration_teams_team_id')}
|
||||
</InputField>
|
||||
<InputField name="teamName" state={state.config} readOnly={state.readonly} disabled={state.disabled} tiny fill>
|
||||
{translate('administration_teams_team_name')}
|
||||
</InputField>
|
||||
<Textarea name="description" state={state.config} readOnly={state.readonly} disabled={state.disabled} tiny fill>
|
||||
{translate('administration_teams_team_description')}
|
||||
</Textarea>
|
||||
</Group>
|
||||
{!serverConfigResource.resource.distributed && <Permissions state={state} />}
|
||||
<TeamMetaParameters state={state} />
|
||||
</ColoredContainer>
|
||||
);
|
||||
});
|
||||
|
||||
+23
-4
@@ -10,13 +10,14 @@ import { useEffect } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import type { TeamInfo } from '@cloudbeaver/core-authentication';
|
||||
import { IconOrImage, Loader, Placeholder, useExecutor, useObjectRef, useStyles, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { Form, IconOrImage, Loader, Placeholder, useExecutor, useForm, useObjectRef, useStyles, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { BASE_TAB_STYLES, TabList, TabPanelList, TabsState, UNDERLINE_TAB_BIG_STYLES, UNDERLINE_TAB_STYLES } from '@cloudbeaver/core-ui';
|
||||
|
||||
import { teamContext } from './Contexts/teamContext';
|
||||
import type { ITeamFormState } from './ITeamFormProps';
|
||||
import { TeamFormService } from './TeamFormService';
|
||||
import { ITeamFormActionsContext, TeamFormActionsContext } from './TeamFormActionsContext';
|
||||
|
||||
const tabsStyles = css`
|
||||
TabList {
|
||||
@@ -85,6 +86,14 @@ const formStyles = css`
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
Form {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
@@ -100,6 +109,12 @@ export const TeamForm = observer<Props>(function TeamForm({ state, onCancel, onS
|
||||
const style = [BASE_TAB_STYLES, tabsStyles, UNDERLINE_TAB_STYLES, UNDERLINE_TAB_BIG_STYLES];
|
||||
const styles = useStyles(style, topBarStyles, formStyles);
|
||||
const service = useService(TeamFormService);
|
||||
const form = useForm({
|
||||
onSubmit: state.save,
|
||||
});
|
||||
const actions = useObjectRef<ITeamFormActionsContext>({
|
||||
save: async () => form.submit(),
|
||||
});
|
||||
|
||||
useExecutor({
|
||||
executor: state.submittingTask,
|
||||
@@ -121,7 +136,8 @@ export const TeamForm = observer<Props>(function TeamForm({ state, onCancel, onS
|
||||
}, []);
|
||||
|
||||
return styled(styles)(
|
||||
<TabsState container={service.tabsContainer} localState={state.partsState} state={state} onCancel={onCancel}>
|
||||
<Form context={form}>
|
||||
<TabsState container={service.tabsContainer} localState={state.partsState} state={state} onCancel={onCancel}>
|
||||
<box className={className}>
|
||||
<team-top-bar>
|
||||
<team-top-bar-tabs>
|
||||
@@ -137,7 +153,9 @@ export const TeamForm = observer<Props>(function TeamForm({ state, onCancel, onS
|
||||
</team-top-bar-tabs>
|
||||
<team-top-bar-actions>
|
||||
<Loader suspense inline hideMessage hideException>
|
||||
<Placeholder container={service.actionsContainer} state={state} onCancel={onCancel} />
|
||||
<TeamFormActionsContext.Provider value={actions}>
|
||||
<Placeholder container={service.actionsContainer} state={state} onCancel={onCancel} />
|
||||
</TeamFormActionsContext.Provider>
|
||||
</Loader>
|
||||
</team-top-bar-actions>
|
||||
</team-top-bar>
|
||||
@@ -145,6 +163,7 @@ export const TeamForm = observer<Props>(function TeamForm({ state, onCancel, onS
|
||||
<TabPanelList style={style} />
|
||||
</content-box>
|
||||
</box>
|
||||
</TabsState>,
|
||||
</TabsState>
|
||||
</Form>,
|
||||
);
|
||||
});
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { createContext } from 'react';
|
||||
import type { ITeamFormState } from './ITeamFormProps';
|
||||
|
||||
export interface ITeamFormActionsContext {
|
||||
save: ITeamFormState['save'];
|
||||
}
|
||||
|
||||
export const TeamFormActionsContext = createContext<ITeamFormActionsContext | null>(null);
|
||||
+8
-1
@@ -6,13 +6,20 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { Button, PlaceholderComponent, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import type { ITeamFormProps } from './ITeamFormProps';
|
||||
import { TeamFormActionsContext } from './TeamFormActionsContext';
|
||||
|
||||
export const TeamFormBaseActions: PlaceholderComponent<ITeamFormProps> = observer(function TeamFormBaseActions({ state, onCancel }) {
|
||||
const translate = useTranslate();
|
||||
const actions = useContext(TeamFormActionsContext);
|
||||
|
||||
if (!actions) {
|
||||
throw new Error('TeamFormActionsContext not provided');
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -21,7 +28,7 @@ export const TeamFormBaseActions: PlaceholderComponent<ITeamFormProps> = observe
|
||||
{translate('ui_processing_cancel')}
|
||||
</Button>
|
||||
)}
|
||||
<Button type="button" disabled={state.disabled || state.readonly} mod={['unelevated']} loader onClick={state.save}>
|
||||
<Button type="button" disabled={state.disabled || state.readonly} mod={['unelevated']} loader onClick={actions.save}>
|
||||
{translate(state.mode === 'edit' ? 'ui_processing_save' : 'ui_processing_finish')}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -46,6 +46,7 @@ export const AuthProviderForm = observer<Props>(function AuthProviderForm({ prov
|
||||
parameter.user && (
|
||||
<InputField
|
||||
key={`${provider.id}${configuration?.id ?? ''}${parameter.id}`}
|
||||
required={provider.required}
|
||||
title={parameter.description}
|
||||
type={parameter.encryption === 'none' ? 'text' : 'password'}
|
||||
name={parameter.id}
|
||||
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 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, makeObservable, observable } from 'mobx';
|
||||
|
||||
import { ConnectionsManagerService, DBDriver, DBDriverResource } from '@cloudbeaver/core-connections';
|
||||
import { IInitializableController, injectable } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import { ProjectsService } from '@cloudbeaver/core-projects';
|
||||
import { CachedMapAllKey } from '@cloudbeaver/core-resource';
|
||||
import { isArraysEqual } from '@cloudbeaver/core-utils';
|
||||
import { PublicConnectionFormService } from '@cloudbeaver/plugin-connections';
|
||||
|
||||
@injectable()
|
||||
export class CustomConnectionController implements IInitializableController {
|
||||
isLoading: boolean;
|
||||
onClose!: () => void;
|
||||
|
||||
get drivers(): DBDriver[] {
|
||||
return this.dbDriverResource.enabledDrivers;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly dbDriverResource: DBDriverResource,
|
||||
private readonly notificationService: NotificationService,
|
||||
private readonly projectsService: ProjectsService,
|
||||
private readonly publicConnectionFormService: PublicConnectionFormService,
|
||||
private readonly connectionsManagerService: ConnectionsManagerService,
|
||||
) {
|
||||
this.isLoading = true;
|
||||
|
||||
makeObservable(this, {
|
||||
isLoading: observable,
|
||||
drivers: computed<DBDriver[]>({
|
||||
equals: isArraysEqual,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
init(onClose: () => void): void {
|
||||
this.loadDBDrivers();
|
||||
this.onClose = onClose;
|
||||
}
|
||||
|
||||
onDriverSelect = async (driverId: string) => {
|
||||
await this.projectsService.load();
|
||||
|
||||
const projects = this.connectionsManagerService.createConnectionProjects;
|
||||
|
||||
if (projects.length === 0) {
|
||||
this.notificationService.logError({ title: 'core_projects_no_default_project' });
|
||||
return;
|
||||
}
|
||||
|
||||
const state = await this.publicConnectionFormService.open(
|
||||
projects[0].id,
|
||||
{ driverId },
|
||||
this.drivers.map(driver => driver.id),
|
||||
);
|
||||
|
||||
if (state) {
|
||||
this.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
private async loadDBDrivers() {
|
||||
try {
|
||||
await this.dbDriverResource.load(CachedMapAllKey);
|
||||
} catch (exception: any) {
|
||||
this.notificationService.logException(exception, "Can't load database drivers");
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 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 { useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { useController } from '@cloudbeaver/core-di';
|
||||
import type { DialogComponent } from '@cloudbeaver/core-dialogs';
|
||||
|
||||
import { CustomConnectionController } from './CustomConnectionController';
|
||||
import { DriverSelectorDialog } from './DriverSelectorDialog/DriverSelectorDialog';
|
||||
|
||||
export const CustomConnectionDialog: DialogComponent<null, null> = observer(function CustomConnectionDialog({ rejectDialog }) {
|
||||
const controller = useController(CustomConnectionController, rejectDialog);
|
||||
const translate = useTranslate();
|
||||
|
||||
return (
|
||||
<DriverSelectorDialog
|
||||
title={translate('basicConnection_connectionDialog_newConnection')}
|
||||
drivers={controller.drivers}
|
||||
isLoading={controller.isLoading}
|
||||
onSelect={controller.onDriverSelect}
|
||||
onClose={rejectDialog}
|
||||
/>
|
||||
);
|
||||
});
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 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 { CommonDialogBody, CommonDialogHeader, CommonDialogWrapper, Loader, s, useS } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import type { IDriver } from './Driver';
|
||||
import { DriverSelector } from './DriverSelector';
|
||||
import styles from './DriverSelectorDialog.m.css';
|
||||
|
||||
interface IProps {
|
||||
title: string;
|
||||
drivers: IDriver[];
|
||||
isLoading: boolean;
|
||||
onSelect: (driverId: string) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const DriverSelectorDialog = observer<IProps>(function DriverSelectorDialog({ title, drivers, isLoading, onSelect, onClose }) {
|
||||
const style = useS(styles);
|
||||
|
||||
return (
|
||||
<CommonDialogWrapper size="large" fixedSize>
|
||||
<CommonDialogHeader title={title} onReject={onClose} />
|
||||
<CommonDialogBody noBodyPadding noOverflow>
|
||||
{isLoading && <Loader />}
|
||||
{!isLoading && <DriverSelector className={s(style, { driverSelector: true })} drivers={drivers} onSelect={onSelect} />}
|
||||
</CommonDialogBody>
|
||||
</CommonDialogWrapper>
|
||||
);
|
||||
});
|
||||
@@ -14,8 +14,8 @@ import { ActionService, MenuService } from '@cloudbeaver/core-view';
|
||||
import { MENU_CONNECTIONS } from '@cloudbeaver/plugin-connections';
|
||||
|
||||
import { ACTION_CONNECTION_CUSTOM } from './Actions/ACTION_CONNECTION_CUSTOM';
|
||||
import { CustomConnectionDialog } from './CustomConnection/CustomConnectionDialog';
|
||||
import { CustomConnectionSettingsService } from './CustomConnectionSettingsService';
|
||||
import { DriverSelectorDialog } from './DriverSelector/DriverSelectorDialog';
|
||||
|
||||
@injectable()
|
||||
export class CustomConnectionPluginBootstrap extends Bootstrap {
|
||||
@@ -67,6 +67,6 @@ export class CustomConnectionPluginBootstrap extends Bootstrap {
|
||||
load(): void | Promise<void> {}
|
||||
|
||||
private async openConnectionsDialog() {
|
||||
await this.commonDialogService.open(CustomConnectionDialog, null);
|
||||
await this.commonDialogService.open(DriverSelectorDialog, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 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 { CommonDialogBody, CommonDialogHeader, CommonDialogWrapper, s, useResource, useS, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { DBDriverResource } from '@cloudbeaver/core-connections';
|
||||
import type { DialogComponent } from '@cloudbeaver/core-dialogs';
|
||||
import { ProjectInfoResource } from '@cloudbeaver/core-projects';
|
||||
import { CachedMapAllKey } from '@cloudbeaver/core-resource';
|
||||
|
||||
import { DriverSelector } from './DriverSelector';
|
||||
import styles from './DriverSelectorDialog.m.css';
|
||||
import { useDriverSelectorDialog } from './useDriverSelectorDialog';
|
||||
|
||||
export const DriverSelectorDialog: DialogComponent<null> = observer(function DriverSelectorDialog({ rejectDialog }) {
|
||||
const translate = useTranslate();
|
||||
const style = useS(styles);
|
||||
useResource(DriverSelectorDialog, ProjectInfoResource, CachedMapAllKey, { forceSuspense: true });
|
||||
const dbDriverResource = useResource(DriverSelectorDialog, DBDriverResource, CachedMapAllKey);
|
||||
|
||||
const enabledDrivers = dbDriverResource.resource.enabledDrivers;
|
||||
const dialog = useDriverSelectorDialog(
|
||||
enabledDrivers.map(driver => driver.id),
|
||||
rejectDialog,
|
||||
);
|
||||
|
||||
return (
|
||||
<CommonDialogWrapper size="large" fixedSize>
|
||||
<CommonDialogHeader title={translate('basicConnection_connectionDialog_newConnection')} />
|
||||
<CommonDialogBody noBodyPadding noOverflow>
|
||||
<DriverSelector className={s(style, { driverSelector: true })} drivers={enabledDrivers} onSelect={dialog.select} />
|
||||
</CommonDialogBody>
|
||||
</CommonDialogWrapper>
|
||||
);
|
||||
});
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 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 { action } from 'mobx';
|
||||
|
||||
import { useObservableRef } from '@cloudbeaver/core-blocks';
|
||||
import { ConnectionsManagerService } from '@cloudbeaver/core-connections';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import { PublicConnectionFormService } from '@cloudbeaver/plugin-connections';
|
||||
|
||||
interface State {
|
||||
select(driverId: string): Promise<void>;
|
||||
}
|
||||
|
||||
export function useDriverSelectorDialog(drivers: string[], onSelect?: () => void) {
|
||||
const notificationService = useService(NotificationService);
|
||||
const connectionsManagerService = useService(ConnectionsManagerService);
|
||||
const publicConnectionFormService = useService(PublicConnectionFormService);
|
||||
|
||||
const state: State = useObservableRef(
|
||||
() => ({
|
||||
async select(driverId: string) {
|
||||
const projects = this.connectionsManagerService.createConnectionProjects;
|
||||
|
||||
if (projects.length === 0) {
|
||||
this.notificationService.logError({ title: 'core_projects_no_default_project' });
|
||||
return;
|
||||
}
|
||||
|
||||
const state = await this.publicConnectionFormService.open(projects[0].id, { driverId }, this.drivers);
|
||||
|
||||
if (state) {
|
||||
onSelect?.();
|
||||
}
|
||||
},
|
||||
}),
|
||||
{ select: action.bound },
|
||||
{ drivers, notificationService, connectionsManagerService, publicConnectionFormService },
|
||||
);
|
||||
|
||||
return state;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createContext } from 'react';
|
||||
import type { IConnectionFormState } from './IConnectionFormProps';
|
||||
|
||||
export interface IConnectionFormActionsContext {
|
||||
save: IConnectionFormState['save'];
|
||||
test: IConnectionFormState['test'];
|
||||
}
|
||||
|
||||
export const ConnectionFormActionsContext = createContext<IConnectionFormActionsContext | null>(null);
|
||||
@@ -0,0 +1,7 @@
|
||||
.form {
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { observer } from 'mobx-react-lite';
|
||||
import { useEffect } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { ExceptionMessage, Loader, Placeholder, StatusMessage, useExecutor, useObjectRef, useStyles } from '@cloudbeaver/core-blocks';
|
||||
import { ExceptionMessage, Form, Loader, Placeholder, StatusMessage, useExecutor, useForm, useObjectRef, useStyles } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { ENotificationType } from '@cloudbeaver/core-events';
|
||||
import type { ConnectionConfig } from '@cloudbeaver/core-sdk';
|
||||
@@ -18,6 +18,8 @@ import { BASE_TAB_STYLES, TabList, TabPanelList, TabsState, UNDERLINE_TAB_BIG_ST
|
||||
import { ConnectionFormService } from './ConnectionFormService';
|
||||
import { connectionConfigContext } from './Contexts/connectionConfigContext';
|
||||
import type { IConnectionFormState } from './IConnectionFormProps';
|
||||
import connectionFormStyles from './ConnectionForm.m.css';
|
||||
import { ConnectionFormActionsContext, IConnectionFormActionsContext } from './ConnectFormActionsContext';
|
||||
|
||||
const tabsStyles = css`
|
||||
TabList {
|
||||
@@ -106,6 +108,21 @@ export const ConnectionForm = observer<ConnectionFormProps>(function ConnectionF
|
||||
const style = [BASE_TAB_STYLES, tabsStyles, UNDERLINE_TAB_STYLES, UNDERLINE_TAB_BIG_STYLES];
|
||||
const styles = useStyles(style, topBarStyles, formStyles);
|
||||
const service = useService(ConnectionFormService);
|
||||
|
||||
const form = useForm({
|
||||
onSubmit: event => {
|
||||
if (event?.type === 'test') {
|
||||
state.test();
|
||||
} else {
|
||||
state.save();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const actionsContext = useObjectRef<IConnectionFormActionsContext>(() => ({
|
||||
save: async () => form.submit(new SubmitEvent('submit')),
|
||||
test: async () => form.submit(new SubmitEvent('test')),
|
||||
}));
|
||||
|
||||
useExecutor({
|
||||
executor: state.submittingTask,
|
||||
@@ -139,25 +156,29 @@ export const ConnectionForm = observer<ConnectionFormProps>(function ConnectionF
|
||||
}
|
||||
|
||||
return styled(styles)(
|
||||
<TabsState container={service.tabsContainer} localState={state.partsState} state={state} onCancel={onCancel}>
|
||||
<box className={className}>
|
||||
<connection-top-bar>
|
||||
<connection-top-bar-tabs>
|
||||
<connection-status-message>
|
||||
<StatusMessage type={ENotificationType.Info} message={state.statusMessage} />
|
||||
</connection-status-message>
|
||||
<TabList style={style} disabled={state.disabled} />
|
||||
</connection-top-bar-tabs>
|
||||
<connection-top-bar-actions>
|
||||
<Loader suspense inline hideMessage hideException>
|
||||
<Placeholder container={service.actionsContainer} state={state} onCancel={onCancel} />
|
||||
</Loader>
|
||||
</connection-top-bar-actions>
|
||||
</connection-top-bar>
|
||||
<content-box>
|
||||
<TabPanelList style={style} />
|
||||
</content-box>
|
||||
</box>
|
||||
</TabsState>,
|
||||
<Form context={form} className={connectionFormStyles.form}>
|
||||
<TabsState container={service.tabsContainer} localState={state.partsState} state={state} onCancel={onCancel}>
|
||||
<box className={className}>
|
||||
<connection-top-bar>
|
||||
<connection-top-bar-tabs>
|
||||
<connection-status-message>
|
||||
<StatusMessage type={ENotificationType.Info} message={state.statusMessage} />
|
||||
</connection-status-message>
|
||||
<TabList style={style} disabled={state.disabled} />
|
||||
</connection-top-bar-tabs>
|
||||
<connection-top-bar-actions>
|
||||
<Loader suspense inline hideMessage hideException>
|
||||
<ConnectionFormActionsContext.Provider value={actionsContext}>
|
||||
<Placeholder container={service.actionsContainer} state={state} onCancel={onCancel} />
|
||||
</ConnectionFormActionsContext.Provider>
|
||||
</Loader>
|
||||
</connection-top-bar-actions>
|
||||
</connection-top-bar>
|
||||
<content-box>
|
||||
<TabPanelList style={style} />
|
||||
</content-box>
|
||||
</box>
|
||||
</TabsState>
|
||||
</Form>,
|
||||
);
|
||||
});
|
||||
|
||||
+10
-2
@@ -6,18 +6,26 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { AUTH_PROVIDER_LOCAL_ID } from '@cloudbeaver/core-authentication';
|
||||
import { Button, getComputed, PlaceholderComponent, useResource, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { DatabaseAuthModelsResource, DBDriverResource } from '@cloudbeaver/core-connections';
|
||||
import { useAuthenticationAction } from '@cloudbeaver/core-ui';
|
||||
|
||||
import { ConnectionFormActionsContext } from './ConnectFormActionsContext';
|
||||
import type { IConnectionFormProps } from './IConnectionFormProps';
|
||||
|
||||
export const ConnectionFormBaseActions: PlaceholderComponent<IConnectionFormProps> = observer(function ConnectionFormBaseActions({
|
||||
state,
|
||||
onCancel,
|
||||
}) {
|
||||
const actions = useContext(ConnectionFormActionsContext);
|
||||
|
||||
if (!actions) {
|
||||
throw new Error('ConnectionFormActionsContext not provided');
|
||||
}
|
||||
|
||||
const translate = useTranslate();
|
||||
const driverMap = useResource(ConnectionFormBaseActions, DBDriverResource, state.config.driverId || null);
|
||||
|
||||
@@ -40,10 +48,10 @@ export const ConnectionFormBaseActions: PlaceholderComponent<IConnectionFormProp
|
||||
{translate('ui_processing_cancel')}
|
||||
</Button>
|
||||
)}
|
||||
<Button title={translate('connections_connection_test_tooltip')} type="button" disabled={state.disabled || !authorized} mod={['outlined']} loader onClick={state.test}>
|
||||
<Button type="button" disabled={state.disabled || !authorized} mod={['outlined']} loader onClick={actions.test}>
|
||||
{translate('connections_connection_test')}
|
||||
</Button>
|
||||
<Button type="button" disabled={state.disabled || state.readonly} mod={['unelevated']} loader onClick={state.save}>
|
||||
<Button type="button" disabled={state.disabled || state.readonly} mod={['unelevated']} loader onClick={actions.save}>
|
||||
{translate(state.mode === 'edit' ? 'ui_processing_save' : 'ui_processing_finish')}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -95,7 +95,7 @@ export const SSH: TabContainerPanelComponent<Props> = observer(function SSH({ st
|
||||
<Form>
|
||||
<ColoredContainer parent>
|
||||
<Group form gap keepSize large>
|
||||
<Switch name="enabled" state={handlerState} mod={['primary']} disabled={disabled || readonly}>
|
||||
<Switch id="ssh-enable-switch" name="enabled" state={handlerState} mod={['primary']} disabled={disabled || readonly}>
|
||||
{translate('connections_network_handler_ssh_tunnel_enable')}
|
||||
</Switch>
|
||||
<Combobox
|
||||
|
||||
@@ -45,7 +45,7 @@ export const SSHKeyUploader = observer<Props>(function SSHKeyUploader({ state, s
|
||||
disabled={disabled}
|
||||
readOnly={readonly}
|
||||
description={saved ? translate('ui_processing_saved') : undefined}
|
||||
required={state.savePassword}
|
||||
required={state.savePassword && !saved}
|
||||
medium
|
||||
>
|
||||
{translate('connections_network_handler_ssh_tunnel_private_key')}
|
||||
|
||||
@@ -8,16 +8,15 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
import { SSH_TUNNEL_ID } from '@cloudbeaver/core-connections';
|
||||
import { TabContainerPanelComponent, useTab } from '@cloudbeaver/core-ui';
|
||||
import type { TabContainerPanelComponent } from '@cloudbeaver/core-ui';
|
||||
|
||||
import type { IConnectionFormProps } from '../IConnectionFormProps';
|
||||
import { SSH } from './SSH';
|
||||
|
||||
export const SSHPanel: TabContainerPanelComponent<IConnectionFormProps> = observer(function SSHPanel(props) {
|
||||
const state = props.state.config.networkHandlersConfig?.find(state => state.id === SSH_TUNNEL_ID);
|
||||
const tab = useTab(props.tabId);
|
||||
|
||||
if (!state || !tab.selected) {
|
||||
if (!state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ export const SSL: TabContainerPanelComponent<Props> = observer(function SSL({ st
|
||||
<Form>
|
||||
<ColoredContainer parent>
|
||||
<Group gap form large vertical>
|
||||
<Switch name="enabled" state={handlerState} description={handler.description} mod={['primary']} disabled={disabled || readonly}>
|
||||
<Switch id="ssl-enable-switch" name="enabled" state={handlerState} description={handler.description} mod={['primary']} disabled={disabled || readonly}>
|
||||
{translate('connections_public_connection_ssl_enable')}
|
||||
</Switch>
|
||||
{isUncategorizedExists && (
|
||||
|
||||
@@ -10,7 +10,7 @@ import { observer } from 'mobx-react-lite';
|
||||
import { useResource } from '@cloudbeaver/core-blocks';
|
||||
import { DBDriverResource, NetworkHandlerResource } from '@cloudbeaver/core-connections';
|
||||
import { CachedMapAllKey } from '@cloudbeaver/core-resource';
|
||||
import { TabContainerTabComponent, useTab } from '@cloudbeaver/core-ui';
|
||||
import type { TabContainerTabComponent } from '@cloudbeaver/core-ui';
|
||||
|
||||
import type { IConnectionFormProps } from '../IConnectionFormProps';
|
||||
import { getSSLDefaultConfig } from './getSSLDefaultConfig';
|
||||
@@ -18,7 +18,6 @@ import { getSSLDriverHandler } from './getSSLDriverHandler';
|
||||
import { SSL } from './SSL';
|
||||
|
||||
export const SSLPanel: TabContainerTabComponent<IConnectionFormProps> = observer(function SSLPanel(props) {
|
||||
const tab = useTab(props.tabId);
|
||||
const networkHandlerResource = useResource(SSLPanel, NetworkHandlerResource, CachedMapAllKey);
|
||||
const dbDriverResource = useResource(SSLPanel, DBDriverResource, props.state.config.driverId ?? null);
|
||||
|
||||
@@ -30,7 +29,7 @@ export const SSLPanel: TabContainerTabComponent<IConnectionFormProps> = observer
|
||||
|
||||
const handlerState = props.state.config.networkHandlersConfig?.find(h => h.id === handler?.id);
|
||||
|
||||
if (!handler || !handlerState || !tab.selected) {
|
||||
if (!handler || !handlerState) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+32
-28
@@ -7,42 +7,46 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
import { Button, Group, GroupItem, GroupTitle, InputField, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
import { Button, Form, Group, GroupItem, GroupTitle, InputField, useForm, useTranslate } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import { useChangePassword } from './useChangePassword';
|
||||
|
||||
export const ChangePassword = observer(function ChangePassword() {
|
||||
const translate = useTranslate();
|
||||
const state = useChangePassword();
|
||||
|
||||
const disabled = state.submitting;
|
||||
const form = useForm({
|
||||
onSubmit: state.changePassword,
|
||||
});
|
||||
|
||||
return (
|
||||
<Group form gap>
|
||||
<GroupTitle>{translate('plugin_user_profile_authentication_change_password')}</GroupTitle>
|
||||
<InputField type="password" name="oldPassword" state={state.config} disabled={disabled} mapValue={value => value.trim()} small required>
|
||||
{translate('plugin_user_profile_authentication_change_password_current_password')}
|
||||
</InputField>
|
||||
<InputField
|
||||
type="password"
|
||||
name="password"
|
||||
autoComplete="new-password"
|
||||
state={state.config}
|
||||
disabled={disabled}
|
||||
mapValue={value => value.trim()}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('plugin_user_profile_authentication_change_password_new_password')}
|
||||
</InputField>
|
||||
<InputField type="password" name="repeatedPassword" state={state.config} disabled={disabled} mapValue={value => value.trim()} small required>
|
||||
{translate('plugin_user_profile_authentication_change_password_repeat_password')}
|
||||
</InputField>
|
||||
<GroupItem>
|
||||
<Button disabled={disabled || !state.formFilled} loading={state.submitting} type="button" mod={['unelevated']} onClick={state.changePassword}>
|
||||
{translate('plugin_user_profile_authentication_change_password_submit_label')}
|
||||
</Button>
|
||||
</GroupItem>
|
||||
</Group>
|
||||
<Form context={form}>
|
||||
<Group form gap>
|
||||
<GroupTitle>{translate('plugin_user_profile_authentication_change_password')}</GroupTitle>
|
||||
<InputField type="password" name="oldPassword" state={state.config} disabled={disabled} mapValue={value => value.trim()} small required>
|
||||
{translate('plugin_user_profile_authentication_change_password_current_password')}
|
||||
</InputField>
|
||||
<InputField
|
||||
type="password"
|
||||
name="password"
|
||||
autoComplete="new-password"
|
||||
state={state.config}
|
||||
disabled={disabled}
|
||||
mapValue={value => value.trim()}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('plugin_user_profile_authentication_change_password_new_password')}
|
||||
</InputField>
|
||||
<InputField type="password" name="repeatedPassword" state={state.config} disabled={disabled} mapValue={value => value.trim()} small required>
|
||||
{translate('plugin_user_profile_authentication_change_password_repeat_password')}
|
||||
</InputField>
|
||||
<GroupItem>
|
||||
<Button disabled={disabled || !state.formFilled} loading={state.submitting} type="button" mod={['unelevated']} onClick={() => form.submit()}>
|
||||
{translate('plugin_user_profile_authentication_change_password_submit_label')}
|
||||
</Button>
|
||||
</GroupItem>
|
||||
</Group>
|
||||
</Form>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"src/**/*.scss",
|
||||
"public/**/*"
|
||||
],
|
||||
"version": "23.3.0",
|
||||
"version": "23.3.1",
|
||||
"description": "CloudBeaver Community",
|
||||
"license": "Apache-2.0",
|
||||
"main": "dist/index.js",
|
||||
|
||||
Reference in New Issue
Block a user