mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge pull request #151 from dbeaver/feat-configuration-wizard
Feat configuration wizard
This commit is contained in:
+12
-9
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
|
||||
import { PermissionsService } from '@cloudbeaver/core-root';
|
||||
import { PermissionsService, ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
import { ScreenService, RouterState } from '@cloudbeaver/core-routing';
|
||||
|
||||
import { AdministrationItemService } from '../AdministrationItem/AdministrationItemService';
|
||||
@@ -23,7 +23,8 @@ export class AdministrationScreenServiceBootstrap extends Bootstrap {
|
||||
private screenService: ScreenService,
|
||||
private permissionsService: PermissionsService,
|
||||
private administrationItemService: AdministrationItemService,
|
||||
private administrationScreenService: AdministrationScreenService
|
||||
private administrationScreenService: AdministrationScreenService,
|
||||
private serverConfigResource: ServerConfigResource
|
||||
) {
|
||||
super();
|
||||
this.permissionsService.onUpdate.subscribe(this.handleActivate.bind(this));
|
||||
@@ -87,7 +88,14 @@ export class AdministrationScreenServiceBootstrap extends Bootstrap {
|
||||
});
|
||||
}
|
||||
|
||||
load(): void | Promise<void> { }
|
||||
async load() {
|
||||
const config = await this.serverConfigResource.load(null);
|
||||
|
||||
if (config?.configurationMode) {
|
||||
this.administrationScreenService.configurationWizard = true;
|
||||
this.administrationScreenService.navigateToRoot();
|
||||
}
|
||||
}
|
||||
|
||||
private async handleCanActivate(toState: RouterState, fromState: RouterState) {
|
||||
if (!toState.params?.item) {
|
||||
@@ -103,16 +111,11 @@ export class AdministrationScreenServiceBootstrap extends Bootstrap {
|
||||
}
|
||||
|
||||
private async handleActivate() {
|
||||
if (!this.permissionsService.has(EAdminPermission.admin)) {
|
||||
if (await !this.permissionsService.hasAsync(EAdminPermission.admin)) {
|
||||
this.screenService.navigateToRoot();
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: for development purposes only, must be removed
|
||||
if (this.screenService.isActive(AdministrationScreenService.setupName)) {
|
||||
this.administrationScreenService.configurationWizard = true;
|
||||
}
|
||||
|
||||
if (this.administrationScreenService.activeItem) {
|
||||
await this.administrationItemService.activate(
|
||||
this.administrationScreenService.activeItem,
|
||||
|
||||
+6
-1
@@ -9,6 +9,7 @@
|
||||
import { observable, computed } from 'mobx';
|
||||
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { SessionResource, ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
import { ScreenService } from '@cloudbeaver/core-routing';
|
||||
import { LocalStorageSaveService } from '@cloudbeaver/core-settings';
|
||||
|
||||
@@ -83,7 +84,9 @@ export class ConfigurationWizardService {
|
||||
private administrationItemService: AdministrationItemService,
|
||||
private administrationScreenService: AdministrationScreenService,
|
||||
private autoSaveService: LocalStorageSaveService,
|
||||
private screenService: ScreenService
|
||||
private screenService: ScreenService,
|
||||
private sessionResource: SessionResource,
|
||||
private serverConfigResource: ServerConfigResource
|
||||
) {
|
||||
this.state = {
|
||||
finishedSteps: [],
|
||||
@@ -168,6 +171,8 @@ export class ConfigurationWizardService {
|
||||
await step.configurationWizardOptions.onConfigurationFinish();
|
||||
}
|
||||
}
|
||||
await this.serverConfigResource.update();
|
||||
await this.sessionResource.update();
|
||||
this.administrationScreenService.clearItemsState();
|
||||
this.state.finishedSteps = [];
|
||||
this.administrationScreenService.configurationWizard = false;
|
||||
|
||||
+1
-1
@@ -74,8 +74,8 @@ export class ConfigurationWizardPagesBootstrapService extends Bootstrap {
|
||||
}
|
||||
|
||||
try {
|
||||
await this.graphQLService.gql.configureServer({ configuration: state.serverConfig });
|
||||
await this.graphQLService.gql.setDefaultNavigatorSettings({ settings: state.navigatorConfig });
|
||||
await this.graphQLService.gql.configureServer({ configuration: state.serverConfig });
|
||||
} catch (exception) {
|
||||
this.notificationService.logException(exception, 'Can\'t save server configuration');
|
||||
|
||||
|
||||
+27
-77
@@ -10,7 +10,7 @@ import { observer } from 'mobx-react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import {
|
||||
SubmittingForm, InputGroup, InputField, Checkbox, useFocus
|
||||
SubmittingForm, InputGroup, InputField, Checkbox, useFocus, Switch
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { ServerConfigInput, NavigatorSettingsInput } from '@cloudbeaver/core-sdk';
|
||||
@@ -49,19 +49,19 @@ const boxStyles = css`
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
box-element {
|
||||
min-width: 450px;
|
||||
width: 450px;
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
serverConfig: ServerConfigInput;
|
||||
navigatorConfig: NavigatorSettingsInput;
|
||||
onChange: () => void;
|
||||
onSave: () => void;
|
||||
}
|
||||
|
||||
export const WelcomeConfigForm = observer(function WelcomeConfigForm({
|
||||
serverConfig,
|
||||
navigatorConfig,
|
||||
onChange,
|
||||
onSave,
|
||||
}: Props) {
|
||||
const translate = useTranslate();
|
||||
@@ -78,6 +78,7 @@ export const WelcomeConfigForm = observer(function WelcomeConfigForm({
|
||||
name='serverName'
|
||||
state={serverConfig}
|
||||
mod='surface'
|
||||
required
|
||||
>
|
||||
{translate('administration_configuration_wizard_welcome_server_name')}
|
||||
</InputField>
|
||||
@@ -90,6 +91,7 @@ export const WelcomeConfigForm = observer(function WelcomeConfigForm({
|
||||
name="adminName"
|
||||
state={serverConfig}
|
||||
mod='surface'
|
||||
required
|
||||
>
|
||||
{translate('administration_configuration_wizard_welcome_admin_name')}
|
||||
</InputField>
|
||||
@@ -98,6 +100,7 @@ export const WelcomeConfigForm = observer(function WelcomeConfigForm({
|
||||
name="adminPassword"
|
||||
state={serverConfig}
|
||||
mod='surface'
|
||||
required
|
||||
>
|
||||
{translate('administration_configuration_wizard_welcome_admin_password')}
|
||||
</InputField>
|
||||
@@ -105,89 +108,36 @@ export const WelcomeConfigForm = observer(function WelcomeConfigForm({
|
||||
<InputGroup>{translate('administration_configuration_wizard_welcome_plugins')}</InputGroup>
|
||||
</group>
|
||||
<group as="div">
|
||||
<Checkbox
|
||||
<Switch
|
||||
name="anonymousAccessEnabled"
|
||||
state={serverConfig}
|
||||
checkboxLabel={translate('administration_configuration_wizard_welcome_anonymous_access')}
|
||||
mod='surface'
|
||||
/>
|
||||
description={translate('administration_configuration_wizard_welcome_anonymous_access_description')}
|
||||
mod={['primary']}
|
||||
disabled={!serverConfig.authenticationEnabled}
|
||||
>
|
||||
{translate('administration_configuration_wizard_welcome_anonymous_access')}
|
||||
</Switch>
|
||||
</group>
|
||||
<group as="div">
|
||||
<Checkbox
|
||||
<Switch
|
||||
name="authenticationEnabled"
|
||||
state={serverConfig}
|
||||
checkboxLabel={translate('administration_configuration_wizard_welcome_authentication')}
|
||||
mod='surface'
|
||||
/>
|
||||
description={translate('administration_configuration_wizard_welcome_authentication_description')}
|
||||
onChange={onChange}
|
||||
mod={['primary']}
|
||||
>
|
||||
{translate('administration_configuration_wizard_welcome_authentication')}
|
||||
</Switch>
|
||||
</group>
|
||||
<group as="div">
|
||||
<Checkbox
|
||||
<Switch
|
||||
name="customConnectionsEnabled"
|
||||
state={serverConfig}
|
||||
checkboxLabel={translate('administration_configuration_wizard_welcome_custom_connections')}
|
||||
mod='surface'
|
||||
/>
|
||||
</group>
|
||||
</box-element>
|
||||
<box-element as='div' hidden>
|
||||
<group as="div">
|
||||
<InputGroup>{translate('administration_configuration_wizard_welcome_navigator')}</InputGroup>
|
||||
</group>
|
||||
<group as="div">
|
||||
<Checkbox
|
||||
name="hideFolders"
|
||||
state={navigatorConfig}
|
||||
checkboxLabel={translate('administration_configuration_wizard_welcome_navigator_hide_folders')}
|
||||
mod='surface'
|
||||
/>
|
||||
</group>
|
||||
<group as="div">
|
||||
<Checkbox
|
||||
name="hideSchemas"
|
||||
state={navigatorConfig}
|
||||
checkboxLabel={translate('administration_configuration_wizard_welcome_navigator_hide_schemas')}
|
||||
mod='surface'
|
||||
/>
|
||||
</group>
|
||||
<group as="div">
|
||||
<Checkbox
|
||||
name="hideVirtualModel"
|
||||
state={navigatorConfig}
|
||||
checkboxLabel={translate('administration_configuration_wizard_welcome_navigator_hide_virtual_model')}
|
||||
mod='surface'
|
||||
/>
|
||||
</group>
|
||||
<group as="div">
|
||||
<Checkbox
|
||||
name="mergeEntities"
|
||||
state={navigatorConfig}
|
||||
checkboxLabel={translate('administration_configuration_wizard_welcome_navigator_merge_entities')}
|
||||
mod='surface'
|
||||
/>
|
||||
</group>
|
||||
<group as="div">
|
||||
<Checkbox
|
||||
name="showOnlyEntities"
|
||||
state={navigatorConfig}
|
||||
checkboxLabel={translate('administration_configuration_wizard_welcome_navigator_show_only_entities')}
|
||||
mod='surface'
|
||||
/>
|
||||
</group>
|
||||
<group as="div">
|
||||
<Checkbox
|
||||
name="showSystemObjects"
|
||||
state={navigatorConfig}
|
||||
checkboxLabel={translate('administration_configuration_wizard_welcome_navigator_show_system_objects')}
|
||||
mod='surface'
|
||||
/>
|
||||
</group>
|
||||
<group as="div">
|
||||
<Checkbox
|
||||
name="showUtilityObjects"
|
||||
state={navigatorConfig}
|
||||
checkboxLabel={translate('administration_configuration_wizard_welcome_navigator_show_utility_objects')}
|
||||
mod='surface'
|
||||
/>
|
||||
description={translate('administration_configuration_wizard_welcome_custom_connections_description')}
|
||||
mod={['primary']}
|
||||
>
|
||||
{translate('administration_configuration_wizard_welcome_custom_connections')}
|
||||
</Switch>
|
||||
</group>
|
||||
</box-element>
|
||||
</box>
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ export const WelcomePage = observer(function WelcomePage() {
|
||||
<p>Here you can configure something...</p>
|
||||
<WelcomeConfigForm
|
||||
serverConfig={controller.state.serverConfig}
|
||||
navigatorConfig={controller.state.navigatorConfig}
|
||||
onChange={controller.onChange}
|
||||
onSave={controller.finish}
|
||||
/>
|
||||
</layout-grid-cell>
|
||||
|
||||
+7
-1
@@ -27,7 +27,7 @@ export class WelcomePageController {
|
||||
serverName: 'CloudBeaver',
|
||||
adminName: 'admin',
|
||||
adminPassword: '',
|
||||
anonymousAccessEnabled: false,
|
||||
anonymousAccessEnabled: true,
|
||||
authenticationEnabled: false,
|
||||
customConnectionsEnabled: false,
|
||||
},
|
||||
@@ -43,5 +43,11 @@ export class WelcomePageController {
|
||||
}));
|
||||
}
|
||||
|
||||
onChange = () => {
|
||||
if (!this.state.serverConfig.authenticationEnabled) {
|
||||
this.state.serverConfig.anonymousAccessEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
finish = () => this.configurationWizardService.next()
|
||||
}
|
||||
|
||||
@@ -2,18 +2,21 @@ export default [
|
||||
['administration_menu_enter', 'Administration'],
|
||||
['administration_menu_back', 'Back to app'],
|
||||
['administration_configuration_wizard_title', 'Initial Server Configuration'],
|
||||
['administration_configuration_wizard_welcome', 'Welcome'],
|
||||
['administration_configuration_wizard_welcome_plugins', 'Plugins'],
|
||||
['administration_configuration_wizard_welcome_step_description', 'Welcome'],
|
||||
['administration_configuration_wizard_welcome', 'Server configuration'],
|
||||
['administration_configuration_wizard_welcome_plugins', 'Configuration'],
|
||||
['administration_configuration_wizard_welcome_step_description', 'Server configuration'],
|
||||
['administration_configuration_wizard_welcome_server_name', 'Server Name'],
|
||||
['administration_configuration_wizard_welcome_admin', 'Admin Credentials'],
|
||||
['administration_configuration_wizard_welcome_admin_name', 'Name'],
|
||||
['administration_configuration_wizard_welcome_admin_password', 'Password'],
|
||||
['administration_configuration_wizard_welcome_anonymous_access', 'Anonymous Access'],
|
||||
['administration_configuration_wizard_welcome_authentication', 'Authentication'],
|
||||
['administration_configuration_wizard_welcome_custom_connections', 'Custom Connections'],
|
||||
['administration_configuration_wizard_finish', 'Finalization'],
|
||||
['administration_configuration_wizard_finish_step_description', 'Finalization'],
|
||||
['administration_configuration_wizard_welcome_admin', 'Administrator'],
|
||||
['administration_configuration_wizard_welcome_admin_name', 'Admin user name'],
|
||||
['administration_configuration_wizard_welcome_admin_password', 'Admin password'],
|
||||
['administration_configuration_wizard_welcome_anonymous_access', 'Allow anonymous access'],
|
||||
['administration_configuration_wizard_welcome_anonymous_access_description', 'Allows to work with CloudBeaver without user authentication'],
|
||||
['administration_configuration_wizard_welcome_authentication', 'Enable users authentication'],
|
||||
['administration_configuration_wizard_welcome_authentication_description', 'Allows users to authenticate. Otherwise only anonymous access is enabled'],
|
||||
['administration_configuration_wizard_welcome_custom_connections', 'Enable custom connections'],
|
||||
['administration_configuration_wizard_welcome_custom_connections_description', 'Allows users to create custom connections. Otherwise only administrator can create connections'],
|
||||
['administration_configuration_wizard_finish', 'Confirmation'],
|
||||
['administration_configuration_wizard_finish_step_description', 'Confirmation'],
|
||||
['administration_configuration_wizard_welcome_navigator', 'Navigator'],
|
||||
['administration_configuration_wizard_welcome_navigator_hide_folders', 'Hide Folders'],
|
||||
['administration_configuration_wizard_welcome_navigator_hide_schemas', 'Hide Schemas'],
|
||||
|
||||
@@ -44,6 +44,7 @@ export const InputField: InputFieldType = observer(function InputField(
|
||||
) {
|
||||
const {
|
||||
name,
|
||||
required,
|
||||
state,
|
||||
children,
|
||||
className,
|
||||
@@ -65,13 +66,14 @@ export const InputField: InputFieldType = observer(function InputField(
|
||||
|
||||
return styled(useStyles(baseFormControlStyles))(
|
||||
<field as="div" className={className}>
|
||||
<field-label as='label'>{children}</field-label>
|
||||
<field-label as='label'>{children} {required && '*'}</field-label>
|
||||
<input
|
||||
{...rest}
|
||||
onChange={handleChange}
|
||||
name={name}
|
||||
value={value}
|
||||
{...use({ mod })}
|
||||
required={required}
|
||||
/>
|
||||
</field>
|
||||
);
|
||||
|
||||
@@ -6,21 +6,30 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import styled, { use, css } from 'reshadow';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { useStyles } from '@cloudbeaver/core-theming';
|
||||
import { useStyles, composes } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { baseFormControlStyles } from './baseFormControlStyles';
|
||||
|
||||
const styles = css`
|
||||
field-label {
|
||||
composes: theme-typography--body2 from global;
|
||||
margin-left: 150px;
|
||||
text-align: left;
|
||||
font-weight: initial;
|
||||
}
|
||||
`;
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
field-label {
|
||||
composes: theme-border-color-background from global;
|
||||
}
|
||||
`,
|
||||
css`
|
||||
field-label {
|
||||
composes: theme-typography--body2 from global;
|
||||
margin-left: 150px;
|
||||
text-align: left;
|
||||
font-weight: initial;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: solid 1px #dedede;
|
||||
width: 100%;
|
||||
}
|
||||
`
|
||||
);
|
||||
type Props = React.PropsWithChildren<{
|
||||
className?: string;
|
||||
}>
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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';
|
||||
import { useCallback } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { useStyles, composes } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { baseFormControlStyles } from './baseFormControlStyles';
|
||||
|
||||
const switchStyles = composes(
|
||||
css`
|
||||
switch-control {
|
||||
composes: theme-switch from global;
|
||||
}
|
||||
switch-control-track {
|
||||
composes: theme-switch__track from global;
|
||||
}
|
||||
switch-input {
|
||||
composes: theme-switch_native-control from global;
|
||||
}
|
||||
switch-control-underlay {
|
||||
composes: theme-switch__thumb-underlay from global;
|
||||
}
|
||||
switch-control-thumb {
|
||||
composes: theme-switch__thumb from global;
|
||||
}
|
||||
radio-ripple {
|
||||
composes: theme-radio_ripple from global;
|
||||
}
|
||||
`,
|
||||
css`
|
||||
field label {
|
||||
width: auto;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
const switchMod = {
|
||||
primary: composes(
|
||||
css`
|
||||
switch-control {
|
||||
composes: theme-switch_primary from global;
|
||||
}
|
||||
`
|
||||
),
|
||||
};
|
||||
|
||||
const switchState = {
|
||||
disabled: composes(
|
||||
css`
|
||||
switch-control {
|
||||
composes: theme-switch--disabled from global;
|
||||
}
|
||||
`
|
||||
),
|
||||
checked: composes(
|
||||
css`
|
||||
switch-control {
|
||||
composes: theme-switch--checked from global;
|
||||
}
|
||||
`
|
||||
),
|
||||
};
|
||||
|
||||
type BaseProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'type' | 'value' | 'checked'> & {
|
||||
mod?: (keyof typeof switchMod)[];
|
||||
label?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
type ControlledProps = BaseProps & {
|
||||
checked?: boolean;
|
||||
onChange?(value: boolean, name?: string): any;
|
||||
|
||||
state?: never;
|
||||
}
|
||||
|
||||
type ObjectProps<TKey extends keyof TState, TState> = BaseProps & {
|
||||
name: TKey;
|
||||
state: TState;
|
||||
onChange?(value: boolean, name: TKey): any;
|
||||
|
||||
checked?: never;
|
||||
}
|
||||
|
||||
type SwitchType = {
|
||||
(props: ControlledProps): JSX.Element;
|
||||
<TKey extends keyof TState, TState>(props: ObjectProps<TKey, TState>): JSX.Element;
|
||||
}
|
||||
|
||||
export const Switch: SwitchType = observer(function Switch(props: ControlledProps | ObjectProps<any, any>) {
|
||||
const {
|
||||
name,
|
||||
id,
|
||||
label,
|
||||
description,
|
||||
state,
|
||||
className,
|
||||
children,
|
||||
onChange,
|
||||
mod = [],
|
||||
disabled,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const handleChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (state) {
|
||||
state[name] = event.target.checked;
|
||||
}
|
||||
if (onChange) {
|
||||
onChange(event.target.checked, name);
|
||||
}
|
||||
}, [state, name, onChange]);
|
||||
|
||||
const checked = state ? state[name] : props.checked;
|
||||
|
||||
return styled(useStyles(
|
||||
baseFormControlStyles,
|
||||
switchStyles,
|
||||
...mod.map(mod => switchMod[mod]),
|
||||
disabled && switchState.disabled,
|
||||
checked && switchState.checked
|
||||
))(
|
||||
<field as="div" className={className}>
|
||||
<field-label as="div">{children}</field-label>
|
||||
<switch-control as='div'>
|
||||
<switch-control-track as='div' />
|
||||
<switch-control-underlay as='div' >
|
||||
<switch-control-thumb as='div' />
|
||||
<switch-input
|
||||
as='input'
|
||||
{...rest}
|
||||
type="checkbox"
|
||||
id={id}
|
||||
role="switch"
|
||||
onChange={handleChange}
|
||||
aria-checked={checked}
|
||||
checked={checked}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</switch-control-underlay>
|
||||
</switch-control>
|
||||
<label htmlFor={id}>{label}</label>
|
||||
<field-description as='div'>{description}</field-description>
|
||||
</field>
|
||||
);
|
||||
});
|
||||
@@ -11,6 +11,7 @@ import { css } from 'reshadow';
|
||||
export const baseFormControlStyles = css`
|
||||
field {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex: auto;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
@@ -33,4 +34,11 @@ export const baseFormControlStyles = css`
|
||||
line-height: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
field-description {
|
||||
composes: theme-typography--caption from global;
|
||||
flex-basis: 100%;
|
||||
padding-top: 8px;
|
||||
padding-left: 150px;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -20,6 +20,7 @@ export * from './FormControls/InputGroup';
|
||||
export * from './FormControls/Radio';
|
||||
export * from './FormControls/ShadowInput';
|
||||
export * from './FormControls/SubmittingForm';
|
||||
export * from './FormControls/Switch';
|
||||
export * from './FormControls/Textarea';
|
||||
export * from './ErrorMessage';
|
||||
export * from './IconButton';
|
||||
|
||||
@@ -42,6 +42,7 @@ export class AppBootstrap {
|
||||
) { }
|
||||
|
||||
register(): void | Promise<void> {
|
||||
this.routerService.start();
|
||||
this.exceptionsCatcherService.subscribe();
|
||||
this.sessionExpireService.subscribe();
|
||||
|
||||
@@ -60,6 +61,5 @@ export class AppBootstrap {
|
||||
async doAfterPluginsInit() {
|
||||
// todo this should be moved to the NavigationTabs component creation phase but now it leads to bugs
|
||||
await this.navigationTabsService.restoreTabs();
|
||||
this.routerService.start();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,12 +14,12 @@ import { Translate } from '@cloudbeaver/core-localization';
|
||||
import { useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
export function ConnectionsDrawerItem({
|
||||
item, onSelect, style, disabled,
|
||||
item, onSelect, style, disabled, configurationWizard,
|
||||
}: AdministrationItemDrawerProps) {
|
||||
return styled(useStyles(...style))(
|
||||
<Tab tabId={item.name} onOpen={() => onSelect(item.name)} disabled={disabled}>
|
||||
<TabIcon icon='/icons/connection.svg' />
|
||||
<TabTitle><Translate token='connections_administration_item'/></TabTitle>
|
||||
<TabTitle><Translate token={configurationWizard ? 'connections_administration_configuration_wizard_step_title' : 'connections_administration_item'}/></TabTitle>
|
||||
</Tab>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
export default [
|
||||
['connections_administration_item', 'Connection Management'],
|
||||
['connections_administration_new_connection', 'New connection'],
|
||||
['connections_administration_configuration_wizard_step_description', 'Configuring connections...'],
|
||||
['connections_administration_configuration_wizard_step_title', 'Database connections'],
|
||||
['connections_administration_configuration_wizard_step_description', 'Database connections'],
|
||||
['connections_connection_edit_authentication', 'Authentication'],
|
||||
['connections_connection_edit_access', 'Access'],
|
||||
['connections_connection_edit_access_load_failed', 'Connection access loading failed'],
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
useContext, useEffect, useRef, useState, useMemo,
|
||||
useContext, useEffect, useRef, useMemo,
|
||||
} from 'react';
|
||||
|
||||
import { appContext } from './AppContext';
|
||||
@@ -35,7 +35,9 @@ export function useController<T>(ctor: IServiceConstructor<T>, ...args: any[]):
|
||||
controller.init(...args);
|
||||
}
|
||||
controllerRef.current = controller;
|
||||
}, [...args]);
|
||||
}, [...args, args.length]);
|
||||
/* we put dynamic array length as the dependency because of preact bug,
|
||||
otherwise useMemo will not be triggered on array change */
|
||||
|
||||
useEffect(() => () => {
|
||||
if (isDestructibleController(controllerRef.current)) {
|
||||
|
||||
@@ -21,6 +21,10 @@ export class ServerConfigResource extends CachedDataResource<ServerConfig | null
|
||||
return !!this.data;
|
||||
}
|
||||
|
||||
async update() {
|
||||
await this.refresh(null);
|
||||
}
|
||||
|
||||
protected async loader(key: null): Promise<ServerConfig> {
|
||||
const { serverConfig } = await this.graphQLService.gql.serverConfig();
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ export class SessionResource extends CachedDataResource<SessionState | null, nul
|
||||
return !!this.data;
|
||||
}
|
||||
|
||||
async update() {
|
||||
await this.refresh(null);
|
||||
}
|
||||
|
||||
protected async loader(key: null): Promise<SessionState> {
|
||||
const { session } = await this.graphQLService.gql.openSession();
|
||||
|
||||
|
||||
@@ -6,8 +6,13 @@ query serverConfig {
|
||||
supportsCustomConnections
|
||||
supportsConnectionBrowser
|
||||
supportsWorkspaces
|
||||
|
||||
anonymousAccessEnabled
|
||||
authenticationEnabled
|
||||
|
||||
configurationMode
|
||||
developmentMode
|
||||
|
||||
supportedLanguages {
|
||||
isoCode
|
||||
displayName
|
||||
|
||||
@@ -1271,7 +1271,7 @@ export type ReadSessionLogQuery = { log: Array<Pick<LogEntry, 'time' | 'type' |
|
||||
export type ServerConfigQueryVariables = Exact<{ [key: string]: never }>;
|
||||
|
||||
export type ServerConfigQuery = { serverConfig: (
|
||||
Pick<ServerConfig, 'name' | 'version' | 'productConfiguration' | 'supportsCustomConnections' | 'supportsConnectionBrowser' | 'supportsWorkspaces' | 'anonymousAccessEnabled' | 'authenticationEnabled'>
|
||||
Pick<ServerConfig, 'name' | 'version' | 'productConfiguration' | 'supportsCustomConnections' | 'supportsConnectionBrowser' | 'supportsWorkspaces' | 'anonymousAccessEnabled' | 'authenticationEnabled' | 'configurationMode' | 'developmentMode'>
|
||||
& { supportedLanguages: Array<Pick<ServerLanguage, 'isoCode' | 'displayName' | 'nativeName'>>; defaultNavigatorSettings: Pick<NavigatorSettings, 'showSystemObjects' | 'showUtilityObjects' | 'showOnlyEntities' | 'mergeEntities' | 'hideFolders' | 'hideSchemas' | 'hideVirtualModel'> }
|
||||
); };
|
||||
|
||||
@@ -2117,6 +2117,8 @@ export const ServerConfigDocument = `
|
||||
supportsWorkspaces
|
||||
anonymousAccessEnabled
|
||||
authenticationEnabled
|
||||
configurationMode
|
||||
developmentMode
|
||||
supportedLanguages {
|
||||
isoCode
|
||||
displayName
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
"@material/radio": "^4.0.0",
|
||||
"@material/ripple": "^4.0.0",
|
||||
"@material/theme": "^4.0.0",
|
||||
"@material/typography": "^4.0.0"
|
||||
"@material/typography": "^4.0.0",
|
||||
"@material/switch": "^4.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cloudbeaver/core-di": "^0.1.0",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
@import 'tag';
|
||||
@import 'button';
|
||||
@import 'radio';
|
||||
@import 'switch';
|
||||
@import 'tabs';
|
||||
@import 'table';
|
||||
@import 'placeholder';
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 '@material/switch/mixins';
|
||||
|
||||
@mixin switch-core-styles($query: mdc-feature-all()) {
|
||||
@include mdc-switch-core-styles($query);
|
||||
|
||||
.theme-switch {
|
||||
composes: mdc-switch;
|
||||
}
|
||||
.theme-switch_native-control {
|
||||
composes: mdc-switch__native-control;
|
||||
}
|
||||
.theme-switch__track {
|
||||
composes: mdc-switch__track;
|
||||
}
|
||||
.theme-switch__thumb-underlay {
|
||||
composes: mdc-switch__thumb-underlay;
|
||||
}
|
||||
.theme-switch__thumb {
|
||||
composes: mdc-switch__thumb;
|
||||
}
|
||||
.theme-switch--checked {
|
||||
composes: mdc-switch--checked;
|
||||
}
|
||||
.theme-switch--disabled {
|
||||
composes: mdc-switch--disabled;
|
||||
}
|
||||
|
||||
.theme-switch_primary {
|
||||
$mdc-switch-baseline-theme-color: primary;
|
||||
|
||||
@include mdc-switch-toggled-on-track-color($mdc-switch-baseline-theme-color, $query);
|
||||
@include mdc-switch-toggled-on-thumb-color($mdc-switch-baseline-theme-color, $query);
|
||||
@include mdc-switch-toggled-off-track-color($mdc-switch-toggled-off-track-color, $query);
|
||||
@include mdc-switch-toggled-off-thumb-color($mdc-switch-toggled-off-thumb-color, $query);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
@include table;
|
||||
@include button-core-styles;
|
||||
@include radio-core-styles;
|
||||
@include switch-core-styles;
|
||||
@include form-controls;
|
||||
@include placeholder;
|
||||
@include delimiter($mdc-theme-secondary);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
@include table;
|
||||
@include button-core-styles;
|
||||
@include radio-core-styles;
|
||||
@include switch-core-styles;
|
||||
@include form-controls;
|
||||
@include placeholder;
|
||||
@include delimiter($mdc-theme-secondary);
|
||||
|
||||
@@ -26,29 +26,29 @@ export type Style = BaseStyles | ThemeSelector
|
||||
* @param componentStyles styles array
|
||||
*/
|
||||
export function useStyles(
|
||||
...componentStyles: Array<Style | boolean>
|
||||
...componentStyles: Array<Style | boolean | undefined>
|
||||
) {
|
||||
|
||||
// todo do you understand that we store ALL STYLES in each component that uses this hook?
|
||||
|
||||
const [loadedStyles, setLoadedStyles] = useState<BaseStyles[]>([]);
|
||||
const themeService = useService(ThemeService);
|
||||
const currentThemeId = useObserver(() => themeService.currentThemeId);
|
||||
const filteredStyles = componentStyles.filter(Boolean) as Array<Style>;
|
||||
|
||||
useMemo(() => {
|
||||
Promise
|
||||
.all(
|
||||
componentStyles.filter(Boolean).map(
|
||||
style => ((typeof style === 'object' || style instanceof Composes) ? style : (style as ThemeSelector)(currentThemeId))
|
||||
)
|
||||
)
|
||||
.all(filteredStyles.map(style => (
|
||||
(typeof style === 'object' || style instanceof Composes) ? style : style(currentThemeId)
|
||||
)))
|
||||
.then(styles => setLoadedStyles(flat(styles)));
|
||||
}, [currentThemeId]);
|
||||
}, [currentThemeId, ...filteredStyles, filteredStyles.length]);
|
||||
|
||||
const styles = useMemo(() => {
|
||||
const themeStyles = themeService.getThemeStyles(currentThemeId);
|
||||
return applyComposes([...themeStyles, ...loadedStyles]);
|
||||
}, [currentThemeId, ...loadedStyles]);
|
||||
}, [currentThemeId, ...loadedStyles, loadedStyles.length]);
|
||||
/* we put dynamic array length as the dependency because of preact bug,
|
||||
otherwise useMemo will not be triggered on array change */
|
||||
|
||||
return create(styles); // todo this method is called in each rerender
|
||||
}
|
||||
|
||||
@@ -2256,6 +2256,21 @@
|
||||
dependencies:
|
||||
"@material/feature-targeting" "^4.0.0"
|
||||
|
||||
"@material/switch@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@material/switch/-/switch-4.0.0.tgz#5c5da30389f48f4f5245eb9f325cbc0e5f6bf2d4"
|
||||
integrity sha512-90fvz0bYgeGQNBD14rdTOI/dvAPANwDQLIodOpE/r2bOZjge1K0rHSAm+TVwNCnrrM9HGmG8Ew0sIeEIj3jMMw==
|
||||
dependencies:
|
||||
"@material/animation" "^4.0.0"
|
||||
"@material/base" "^4.0.0"
|
||||
"@material/dom" "^4.0.0"
|
||||
"@material/elevation" "^4.0.0"
|
||||
"@material/feature-targeting" "^4.0.0"
|
||||
"@material/ripple" "^4.0.0"
|
||||
"@material/rtl" "^4.0.0"
|
||||
"@material/theme" "^4.0.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@material/theme@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@material/theme/-/theme-4.0.0.tgz#8de388fcdbae20fd09b02a3bdef3049bf2f623a8"
|
||||
|
||||
Reference in New Issue
Block a user