mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-3070 chore: fix bundles structure (#1445)
Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com>
This commit is contained in:
@@ -9,11 +9,11 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { splitStyles, Split, ResizerControls, Pane, ErrorBoundary, useSplitUserState, useStyles } from '@cloudbeaver/core-blocks';
|
||||
import { splitStyles, Split, ResizerControls, Pane, ErrorBoundary, useSplitUserState, useStyles, Loader } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { SideBarPanel, SideBarPanelService } from '@cloudbeaver/core-ui';
|
||||
import { ConnectionsSettingsService } from '@cloudbeaver/plugin-connections';
|
||||
import { NavigationTree } from '@cloudbeaver/plugin-navigation-tree';
|
||||
import { NavigationTreeLoader } from '@cloudbeaver/plugin-navigation-tree';
|
||||
|
||||
import { RightArea } from './RightArea';
|
||||
|
||||
@@ -27,6 +27,9 @@ const mainStyles = css`
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
Loader {
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Main = observer(function Main() {
|
||||
@@ -43,38 +46,44 @@ export const Main = observer(function Main() {
|
||||
const sideBarDisabled = activeBars.length === 0;
|
||||
|
||||
return styled(styles)(
|
||||
<space as="main">
|
||||
<Split
|
||||
{...splitMainState}
|
||||
sticky={30}
|
||||
mode={connectionsDisabled ? 'minimize' : splitMainState.mode}
|
||||
disable={connectionsDisabled}
|
||||
>
|
||||
<Pane main>
|
||||
<ErrorBoundary remount>
|
||||
<NavigationTree />
|
||||
</ErrorBoundary>
|
||||
</Pane>
|
||||
<ResizerControls />
|
||||
<Pane>
|
||||
<Split
|
||||
{...splitRightState}
|
||||
mode={sideBarDisabled ? 'minimize' : splitRightState.mode}
|
||||
disable={sideBarDisabled}
|
||||
sticky={30}
|
||||
>
|
||||
<Pane>
|
||||
<RightArea />
|
||||
</Pane>
|
||||
<ResizerControls />
|
||||
<Pane main>
|
||||
<ErrorBoundary remount>
|
||||
<SideBarPanel container={sideBarPanelService.tabsContainer} />
|
||||
</ErrorBoundary>
|
||||
</Pane>
|
||||
</Split>
|
||||
</Pane>
|
||||
</Split>
|
||||
</space>
|
||||
<Loader loading={false} overlay>
|
||||
<space as="main">
|
||||
<Split
|
||||
{...splitMainState}
|
||||
sticky={30}
|
||||
mode={connectionsDisabled ? 'minimize' : splitMainState.mode}
|
||||
disable={connectionsDisabled}
|
||||
>
|
||||
<Pane main>
|
||||
<ErrorBoundary remount>
|
||||
<Loader loading={false} overlay>
|
||||
<NavigationTreeLoader />
|
||||
</Loader>
|
||||
</ErrorBoundary>
|
||||
</Pane>
|
||||
<ResizerControls />
|
||||
<Pane>
|
||||
<Split
|
||||
{...splitRightState}
|
||||
mode={sideBarDisabled ? 'minimize' : splitRightState.mode}
|
||||
disable={sideBarDisabled}
|
||||
sticky={30}
|
||||
>
|
||||
<Pane>
|
||||
<RightArea />
|
||||
</Pane>
|
||||
<ResizerControls />
|
||||
<Pane main>
|
||||
<ErrorBoundary remount>
|
||||
<Loader loading={false} overlay>
|
||||
<SideBarPanel container={sideBarPanelService.tabsContainer} />
|
||||
</Loader>
|
||||
</ErrorBoundary>
|
||||
</Pane>
|
||||
</Split>
|
||||
</Pane>
|
||||
</Split>
|
||||
</space>
|
||||
</Loader>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -21,7 +21,8 @@ import {
|
||||
SlideOverlay,
|
||||
useStyles,
|
||||
ErrorBoundary,
|
||||
useSplitUserState
|
||||
useSplitUserState,
|
||||
Loader
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { OptionsPanelService } from '@cloudbeaver/core-ui';
|
||||
@@ -34,6 +35,9 @@ const styles = css`
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
}
|
||||
Loader {
|
||||
height: 100%;
|
||||
}
|
||||
SlideBox {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -56,7 +60,11 @@ export const RightArea = observer<Props>(function RightArea({ className }) {
|
||||
return styled(useStyles(styles, splitStyles, splitHorizontalStyles, slideBoxStyles))(
|
||||
<SlideBox open={optionsPanelService.active} className={className}>
|
||||
<SlideElement>
|
||||
<ErrorBoundary remount><OptionsPanel /></ErrorBoundary>
|
||||
<ErrorBoundary remount>
|
||||
<Loader loading={false} overlay>
|
||||
<OptionsPanel />
|
||||
</Loader>
|
||||
</ErrorBoundary>
|
||||
</SlideElement>
|
||||
<SlideElement>
|
||||
<Split
|
||||
@@ -69,13 +77,17 @@ export const RightArea = observer<Props>(function RightArea({ className }) {
|
||||
>
|
||||
<Pane>
|
||||
<ErrorBoundary remount>
|
||||
<NavigationTabsBar />
|
||||
<Loader loading={false} overlay>
|
||||
<NavigationTabsBar />
|
||||
</Loader>
|
||||
</ErrorBoundary>
|
||||
</Pane>
|
||||
<ResizerControls />
|
||||
<Pane basis='30%' main>
|
||||
<ErrorBoundary remount>
|
||||
<ToolsPanel container={toolsPanelService.tabsContainer} />
|
||||
<Loader loading={false} overlay>
|
||||
<ToolsPanel container={toolsPanelService.tabsContainer} />
|
||||
</Loader>
|
||||
</ErrorBoundary>
|
||||
</Pane>
|
||||
</Split>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import { observable } from 'mobx';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useState, useEffect, useContext, useRef } from 'react';
|
||||
import { useState, useEffect, useContext, useRef, Suspense } from 'react';
|
||||
import styled, { use } from 'reshadow';
|
||||
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
@@ -216,11 +216,13 @@ export const Loader = observer<Props>(function Loader({
|
||||
|
||||
if (children && (!loader || !loading) && !overlay) {
|
||||
if (loaded) {
|
||||
if (typeof children === 'function') {
|
||||
return <LoaderContext.Provider value={contextState}>{children()}</LoaderContext.Provider>;
|
||||
} else {
|
||||
return <LoaderContext.Provider value={contextState}>{children}</LoaderContext.Provider>;
|
||||
}
|
||||
return (
|
||||
<LoaderContext.Provider value={contextState}>
|
||||
<Suspense fallback={<Loader className={className} />}>
|
||||
{typeof children === 'function' ? children() : children}
|
||||
</Suspense>
|
||||
</LoaderContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
if (!loading) {
|
||||
@@ -230,11 +232,13 @@ export const Loader = observer<Props>(function Loader({
|
||||
|
||||
if ((!isVisible && overlay) || !loading) {
|
||||
if (overlay) {
|
||||
if (typeof children === 'function') {
|
||||
return <LoaderContext.Provider value={contextState}>{children()}</LoaderContext.Provider>;
|
||||
} else {
|
||||
return <LoaderContext.Provider value={contextState}>{children}</LoaderContext.Provider>;
|
||||
}
|
||||
return (
|
||||
<LoaderContext.Provider value={contextState}>
|
||||
<Suspense fallback={<Loader className={className} />}>
|
||||
{typeof children === 'function' ? children() : children}
|
||||
</Suspense>
|
||||
</LoaderContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -253,7 +257,11 @@ export const Loader = observer<Props>(function Loader({
|
||||
return styled(style)(
|
||||
<LoaderContext.Provider value={contextState}>
|
||||
<>
|
||||
{overlay && children}
|
||||
{overlay && (
|
||||
<Suspense fallback={<Loader className={className} />}>
|
||||
{typeof children === 'function' ? children() : children}
|
||||
</Suspense>
|
||||
)}
|
||||
<loader ref={loaderRef} className={className} {...use({ small, fullSize, inline, secondary, overlay })}>
|
||||
<icon><StaticImage icon={spinnerURL} /></icon>
|
||||
{!hideMessage && <message><Translate token={message || 'ui_processing_loading'} /></message>}
|
||||
|
||||
@@ -52,7 +52,7 @@ module.exports = {
|
||||
// "importSource": "preact-jsx-runtime"
|
||||
},
|
||||
],
|
||||
['@babel/preset-typescript', { isTSX: true, allExtensions: true }],
|
||||
['@babel/preset-typescript', { isTSX: true, allExtensions: true, onlyRemoveTypeImports: true }],
|
||||
],
|
||||
plugins: [
|
||||
'babel-plugin-transform-typescript-metadata',
|
||||
|
||||
@@ -114,26 +114,46 @@ module.exports = (env, argv) => {
|
||||
moduleIds: 'deterministic',
|
||||
splitChunks: {
|
||||
chunks: 'async',
|
||||
minSize: 20000,
|
||||
minRemainingSize: 0,
|
||||
minChunks: 1,
|
||||
maxAsyncRequests: 30,
|
||||
maxInitialRequests: 30,
|
||||
enforceSizeThreshold: 50000,
|
||||
cacheGroups: {
|
||||
locale: {
|
||||
test: /[\\/]locales[\\/].*?\.ts/,
|
||||
filename: '[name].[contenthash].bundle.js',
|
||||
filename: '[name].[contenthash].js',
|
||||
name(module) {
|
||||
return module.rawRequest.substr(2);
|
||||
},
|
||||
priority: -5,
|
||||
reuseExistingChunk: true,
|
||||
},
|
||||
packages: {
|
||||
test: /[\\/]packages[\\/]((plugin|core)-.*?)[\\/]src[\\/]/,
|
||||
filename: '[name].[contenthash].js',
|
||||
name(module) {
|
||||
const path = module.context ?? module.resource;
|
||||
const match = /[\\/]packages[\\/]((plugin|core)-.*?)([\\/]|$)/.exec(path);
|
||||
if (!path || !match) {
|
||||
return 'package';
|
||||
}
|
||||
return match[1];
|
||||
},
|
||||
priority: -10,
|
||||
enforce: true,
|
||||
},
|
||||
defaultVendors: {
|
||||
chunks: 'all',
|
||||
name: 'vendors',
|
||||
test: new RegExp(`[\\\\/]node_modules[\\\\/](?!${excludedFromVendor.join('|')}).*?[\\\\/]`, ''),
|
||||
priority: -10,
|
||||
reuseExistingChunk: false,
|
||||
enforce: true,
|
||||
priority: -20,
|
||||
reuseExistingChunk: true,
|
||||
},
|
||||
default: {
|
||||
minChunks: 2,
|
||||
priority: -20,
|
||||
priority: -40,
|
||||
reuseExistingChunk: true,
|
||||
},
|
||||
// styles: {
|
||||
|
||||
@@ -11,8 +11,7 @@ import { useContext, useEffect } from 'react';
|
||||
import { Dialog, useDialogState } from 'reakit/Dialog';
|
||||
import styled, { use } from 'reshadow';
|
||||
|
||||
import { Icon, IconOrImage, useTranslate, useStyles } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import { Icon, IconOrImage, useTranslate, useStyles, Loader } from '@cloudbeaver/core-blocks';
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { DialogContext } from '../DialogContext';
|
||||
@@ -86,7 +85,9 @@ export const CommonDialogWrapper = observer<CommonDialogWrapperProps>(function C
|
||||
<dialog-body {...use({ 'no-padding': noBodyPadding, 'no-overflow': noOverflow })}>
|
||||
<dialog-body-overflow-box>
|
||||
<dialog-body-content>
|
||||
{children}
|
||||
<Loader loading={false} overlay>
|
||||
{children}
|
||||
</Loader>
|
||||
</dialog-body-content>
|
||||
{!noOverflow && <dialog-body-overflow />}
|
||||
</dialog-body-overflow-box>
|
||||
|
||||
@@ -147,6 +147,9 @@ export const commonDialogBaseStyle = css`
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
Loader {
|
||||
height: 100%;
|
||||
}
|
||||
dialog-body[|no-padding] + footer {
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@ import type { ServerLanguage } from '@cloudbeaver/core-sdk';
|
||||
import { SettingsService } from '@cloudbeaver/core-settings';
|
||||
|
||||
import type { ILocaleProvider } from './ILocaleProvider';
|
||||
import { defaultENLocale } from './locales/en';
|
||||
import { defaultITLocale } from './locales/it';
|
||||
import { defaultRULocale } from './locales/ru';
|
||||
import { defaultZHLocale } from './locales/zh';
|
||||
import type { TLocalizationToken } from './TLocalizationToken';
|
||||
|
||||
export type ServerLanguageShort = Pick<ServerLanguage, 'isoCode' | 'nativeName'>;
|
||||
@@ -160,16 +156,16 @@ export class LocalizationService extends Bootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
private coreProvider(locale: string) {
|
||||
private async coreProvider(locale: string) {
|
||||
switch (locale) {
|
||||
case 'ru':
|
||||
return defaultRULocale;
|
||||
return (await import('./locales/ru')).default;
|
||||
case 'it':
|
||||
return defaultITLocale;
|
||||
return (await import('./locales/it')).default;
|
||||
case 'zh':
|
||||
return defaultZHLocale;
|
||||
return (await import('./locales/zh')).default;
|
||||
default:
|
||||
return defaultENLocale;
|
||||
return (await import('./locales/en')).default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const defaultENLocale = [
|
||||
export default [
|
||||
['name', 'en'],
|
||||
['ui_rename_taken_or_invalid', 'This name is unavailable or invalid'],
|
||||
['ui_tag_new', 'New'],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const defaultITLocale = [
|
||||
export default [
|
||||
['name', 'it'],
|
||||
['ui_rename_taken_or_invalid', 'This name is unavailable or invalid'],
|
||||
['ui_tag_new', 'Nuovo'],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const defaultRULocale = [
|
||||
export default [
|
||||
['name', 'ru'],
|
||||
['ui_rename_taken_or_invalid', 'Имя занято или недопустимо'],
|
||||
['ui_tag_new', 'Новый'],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const defaultZHLocale = [
|
||||
export default [
|
||||
['name', 'zh'],
|
||||
['ui_rename_taken_or_invalid', 'This name is unavailable or invalid'],
|
||||
['ui_tag_new', '新建'],
|
||||
|
||||
@@ -10,7 +10,7 @@ import { observer } from 'mobx-react-lite';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import { TabPanel as BaseTabPanel, TabStateReturn } from 'reakit/Tab';
|
||||
|
||||
import { ErrorBoundary, getComputed } from '@cloudbeaver/core-blocks';
|
||||
import { ErrorBoundary, getComputed, Loader } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import { TabContext } from './TabContext';
|
||||
import type { TabPanelProps } from './TabPanelProps';
|
||||
@@ -38,22 +38,26 @@ export const TabPanel: React.FC<TabPanelProps> = observer(function TabPanel({
|
||||
if (typeof children === 'function') {
|
||||
return (
|
||||
<ErrorBoundary remount>
|
||||
<TabContext.Provider value={tabContext}>
|
||||
<BaseTabPanel {...state.state} tabId={tabId} className={className}>
|
||||
{(children as (state: TabStateReturn) => React.ReactNode)(state.state)}
|
||||
</BaseTabPanel>
|
||||
</TabContext.Provider>
|
||||
<Loader loading={false} overlay>
|
||||
<TabContext.Provider value={tabContext}>
|
||||
<BaseTabPanel {...state.state} tabId={tabId} className={className}>
|
||||
{(children as (state: TabStateReturn) => React.ReactNode)(state.state)}
|
||||
</BaseTabPanel>
|
||||
</TabContext.Provider>
|
||||
</Loader>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorBoundary remount>
|
||||
<TabContext.Provider value={tabContext}>
|
||||
<BaseTabPanel {...state.state} tabId={tabId} className={className}>
|
||||
{children}
|
||||
</BaseTabPanel>
|
||||
</TabContext.Provider>
|
||||
<Loader loading={false} overlay>
|
||||
<TabContext.Provider value={tabContext}>
|
||||
<BaseTabPanel {...state.state} tabId={tabId} className={className}>
|
||||
{children}
|
||||
</BaseTabPanel>
|
||||
</TabContext.Provider>
|
||||
</Loader>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useLayoutEffect, useRef } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { AdministrationItemService, filterOnlyActive, IAdministrationItemRoute } from '@cloudbeaver/core-administration';
|
||||
import { SlideBox, SlideElement, ErrorBoundary, SlideOverlay, slideBoxStyles, useStyles } from '@cloudbeaver/core-blocks';
|
||||
import { SlideBox, SlideElement, ErrorBoundary, SlideOverlay, slideBoxStyles, useStyles, Loader } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { TabsState, TabList, verticalTabStyles, BASE_TAB_STYLES, OptionsPanelService } from '@cloudbeaver/core-ui';
|
||||
|
||||
@@ -59,6 +59,9 @@ const administrationStyles = css`
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
Loader {
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
@@ -102,16 +105,20 @@ export const Administration = observer<React.PropsWithChildren<Props>>(function
|
||||
<SlideBox open={optionsPanelService.active}>
|
||||
<SlideElement>
|
||||
<ErrorBoundary remount>
|
||||
<content>
|
||||
<OptionsPanel />
|
||||
</content>
|
||||
<Loader loading={false} overlay>
|
||||
<content>
|
||||
<OptionsPanel />
|
||||
</content>
|
||||
</Loader>
|
||||
</ErrorBoundary>
|
||||
</SlideElement>
|
||||
<SlideElement>
|
||||
<ErrorBoundary remount>
|
||||
<content>
|
||||
<ItemContent activeScreen={activeScreen} configurationWizard={configurationWizard} />
|
||||
</content>
|
||||
<Loader loading={false} overlay>
|
||||
<content>
|
||||
<ItemContent activeScreen={activeScreen} configurationWizard={configurationWizard} />
|
||||
</content>
|
||||
</Loader>
|
||||
</ErrorBoundary>
|
||||
<SlideOverlay onClick={() => optionsPanelService.close()} />
|
||||
</SlideElement>
|
||||
|
||||
+11
-2
@@ -6,15 +6,21 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { PlaceholderContainer } from '@cloudbeaver/core-blocks';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { ENotificationType, NotificationService } from '@cloudbeaver/core-events';
|
||||
import { ExecutorHandlersCollection, ExecutorInterrupter, IExecutorHandler, IExecutorHandlersCollection } from '@cloudbeaver/core-executor';
|
||||
import { TabsContainer } from '@cloudbeaver/core-ui';
|
||||
|
||||
import { AuthConfigurationFormBaseActions } from './AuthConfigurationFormBaseActions';
|
||||
import type { IAuthConfigurationFormFillConfigData, IAuthConfigurationFormProps, IAuthConfigurationFormSubmitData, IAuthConfigurationFormState } from './IAuthConfigurationFormProps';
|
||||
|
||||
const AuthConfigurationFormBaseActions = React.lazy(async () => {
|
||||
const { AuthConfigurationFormBaseActions } = await import('./AuthConfigurationFormBaseActions');
|
||||
return { default: AuthConfigurationFormBaseActions };
|
||||
});
|
||||
|
||||
export interface IConfigurationFormValidation {
|
||||
valid: boolean;
|
||||
messages: string[];
|
||||
@@ -93,7 +99,10 @@ export class AuthConfigurationFormService {
|
||||
},
|
||||
});
|
||||
|
||||
private readonly showSubmittingStatusMessage: IExecutorHandler<IAuthConfigurationFormSubmitData> = (data, contexts) => {
|
||||
private readonly showSubmittingStatusMessage: IExecutorHandler<IAuthConfigurationFormSubmitData> = (
|
||||
data,
|
||||
contexts
|
||||
) => {
|
||||
const status = contexts.getContext(this.configurationStatusContext);
|
||||
|
||||
if (!status.saved) {
|
||||
|
||||
+15
-3
@@ -6,6 +6,8 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { AdministrationItemService, AdministrationItemType } from '@cloudbeaver/core-administration';
|
||||
import { AuthConfigurationsResource, AuthProviderService, AuthProvidersResource } from '@cloudbeaver/core-authentication';
|
||||
import { PlaceholderContainer } from '@cloudbeaver/core-blocks';
|
||||
@@ -13,10 +15,20 @@ import { injectable, Bootstrap } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import { AdminAuthProviderConfiguration, CachedMapAllKey } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import { AuthConfigurationsAdministration } from './AuthConfigurationsAdministration';
|
||||
import { AuthConfigurationsDrawerItem } from './AuthConfigurationsDrawerItem';
|
||||
import { CreateAuthConfigurationService } from './CreateAuthConfigurationService';
|
||||
import { IdentityProvidersServiceLink } from './IdentityProvidersServiceLink';
|
||||
|
||||
const AuthConfigurationsAdministration = React.lazy(async () => {
|
||||
const { AuthConfigurationsAdministration } = await import('./AuthConfigurationsAdministration');
|
||||
return { default: AuthConfigurationsAdministration };
|
||||
});
|
||||
const AuthConfigurationsDrawerItem = React.lazy(async () => {
|
||||
const { AuthConfigurationsDrawerItem } = await import('./AuthConfigurationsDrawerItem');
|
||||
return { default: AuthConfigurationsDrawerItem };
|
||||
});
|
||||
const IdentityProvidersServiceLink = React.lazy(async () => {
|
||||
const { IdentityProvidersServiceLink } = await import('./IdentityProvidersServiceLink');
|
||||
return { default: IdentityProvidersServiceLink };
|
||||
});
|
||||
|
||||
export interface IAuthConfigurationDetailsPlaceholderProps {
|
||||
configuration: AdminAuthProviderConfiguration;
|
||||
|
||||
+7
-1
@@ -6,6 +6,8 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { AuthConfigurationsResource, AuthProvidersResource } from '@cloudbeaver/core-authentication';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
|
||||
@@ -14,7 +16,11 @@ import { getUniqueName } from '@cloudbeaver/core-utils';
|
||||
import { AuthConfigurationFormService } from '../AuthConfigurationFormService';
|
||||
import { authConfigurationContext } from '../Contexts/authConfigurationContext';
|
||||
import type { IAuthConfigurationFormFillConfigData, IAuthConfigurationFormState, IAuthConfigurationFormSubmitData } from '../IAuthConfigurationFormProps';
|
||||
import { AuthConfigurationOptions } from './AuthConfigurationOptions';
|
||||
|
||||
const AuthConfigurationOptions = React.lazy(async () => {
|
||||
const { AuthConfigurationOptions } = await import('./AuthConfigurationOptions');
|
||||
return { default: AuthConfigurationOptions };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class AuthConfigurationOptionsTabService extends Bootstrap {
|
||||
|
||||
+7
-1
@@ -6,6 +6,8 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { TeamsResource } from '@cloudbeaver/core-authentication';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
@@ -17,9 +19,13 @@ import { isArraysEqual, MetadataValueGetter } from '@cloudbeaver/core-utils';
|
||||
import { teamContext } from '../Contexts/teamContext';
|
||||
import type { ITeamFormProps, ITeamFormSubmitData } from '../ITeamFormProps';
|
||||
import { TeamFormService } from '../TeamFormService';
|
||||
import { GrantedConnections } from './GrantedConnections';
|
||||
import type { IGrantedConnectionsTabState } from './IGrantedConnectionsTabState';
|
||||
|
||||
const GrantedConnections = React.lazy(async () => {
|
||||
const { GrantedConnections } = await import('./GrantedConnections');
|
||||
return { default: GrantedConnections };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class GrantedConnectionsTabService extends Bootstrap {
|
||||
private readonly key: string;
|
||||
|
||||
+7
-1
@@ -6,6 +6,8 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { TeamsResource, UsersResource } from '@cloudbeaver/core-authentication';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
@@ -15,9 +17,13 @@ import { isArraysEqual, MetadataValueGetter } from '@cloudbeaver/core-utils';
|
||||
import { teamContext } from '../Contexts/teamContext';
|
||||
import type { ITeamFormProps, ITeamFormSubmitData } from '../ITeamFormProps';
|
||||
import { TeamFormService } from '../TeamFormService';
|
||||
import { GrantedUsers } from './GrantedUsers';
|
||||
import type { IGrantedUsersTabState } from './IGrantedUsersTabState';
|
||||
|
||||
const GrantedUsers = React.lazy(async () => {
|
||||
const { GrantedUsers } = await import('./GrantedUsers');
|
||||
return { default: GrantedUsers };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class GrantedUsersTabService extends Bootstrap {
|
||||
private readonly key: string;
|
||||
|
||||
+7
-1
@@ -6,6 +6,8 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { TeamsResource } from '@cloudbeaver/core-authentication';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
|
||||
@@ -15,7 +17,11 @@ import { getUniqueName } from '@cloudbeaver/core-utils';
|
||||
import { teamContext } from '../Contexts/teamContext';
|
||||
import type { ITeamFormFillConfigData, ITeamFormSubmitData } from '../ITeamFormProps';
|
||||
import { TeamFormService } from '../TeamFormService';
|
||||
import { TeamOptions } from './TeamOptions';
|
||||
|
||||
const TeamOptions = React.lazy(async () => {
|
||||
const { TeamOptions } = await import('./TeamOptions');
|
||||
return { default: TeamOptions };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class TeamOptionsTabService extends Bootstrap {
|
||||
|
||||
+7
-1
@@ -6,6 +6,8 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { PlaceholderContainer } from '@cloudbeaver/core-blocks';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { ENotificationType, NotificationService } from '@cloudbeaver/core-events';
|
||||
@@ -13,7 +15,11 @@ import { ExecutorHandlersCollection, ExecutorInterrupter, IExecutorHandler, IExe
|
||||
import { TabsContainer } from '@cloudbeaver/core-ui';
|
||||
|
||||
import type { ITeamFormFillConfigData, ITeamFormProps, ITeamFormState, ITeamFormSubmitData } from './ITeamFormProps';
|
||||
import { TeamFormBaseActions } from './TeamFormBaseActions';
|
||||
|
||||
const TeamFormBaseActions = React.lazy(async () => {
|
||||
const { TeamFormBaseActions } = await import('./TeamFormBaseActions');
|
||||
return { default: TeamFormBaseActions };
|
||||
});
|
||||
|
||||
export interface ITeamFormValidation {
|
||||
valid: boolean;
|
||||
|
||||
+7
-1
@@ -6,12 +6,18 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import { isGlobalProject, ProjectInfoResource } from '@cloudbeaver/core-projects';
|
||||
import { CachedMapAllKey } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import { UserFormService } from '../UserFormService';
|
||||
import { ConnectionAccess } from './ConnectionAccess';
|
||||
|
||||
const ConnectionAccess = React.lazy(async () => {
|
||||
const { ConnectionAccess } = await import('./ConnectionAccess');
|
||||
return { default: ConnectionAccess };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class ConnectionAccessTabBootstrap extends Bootstrap {
|
||||
|
||||
+15
-3
@@ -6,14 +6,26 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { AUTH_PROVIDER_LOCAL_ID } from '@cloudbeaver/core-authentication';
|
||||
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
|
||||
|
||||
import { getOriginTabId } from './getOriginTabId';
|
||||
import { OriginInfoPanel } from './OriginInfoPanel';
|
||||
import { OriginInfoTab } from './OriginInfoTab';
|
||||
import { UserFormService } from './UserFormService';
|
||||
import { UserInfo } from './UserInfo';
|
||||
|
||||
const OriginInfoPanel = React.lazy(async () => {
|
||||
const { OriginInfoPanel } = await import('./OriginInfoPanel');
|
||||
return { default: OriginInfoPanel };
|
||||
});
|
||||
const OriginInfoTab = React.lazy(async () => {
|
||||
const { OriginInfoTab } = await import('./OriginInfoTab');
|
||||
return { default: OriginInfoTab };
|
||||
});
|
||||
const UserInfo = React.lazy(async () => {
|
||||
const { UserInfo } = await import('./UserInfo');
|
||||
return { default: UserInfo };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class UserFormBaseBootstrap extends Bootstrap {
|
||||
|
||||
+17
-3
@@ -6,17 +6,31 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { AdministrationItemService } from '@cloudbeaver/core-administration';
|
||||
import { AdminUser, TeamsResource } from '@cloudbeaver/core-authentication';
|
||||
import { PlaceholderContainer } from '@cloudbeaver/core-blocks';
|
||||
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
|
||||
|
||||
import { CreateTeamService } from './Teams/CreateTeamService';
|
||||
import { UsersAdministration } from './UsersAdministration';
|
||||
import { EUsersAdministrationSub, UsersAdministrationNavigationService } from './UsersAdministrationNavigationService';
|
||||
import { UsersDrawerItem } from './UsersDrawerItem';
|
||||
import { CreateUserService } from './UsersTable/CreateUserService';
|
||||
import { Origin } from './UsersTable/UserDetailsInfo/Origin';
|
||||
|
||||
const Origin = React.lazy(async () => {
|
||||
const { Origin } = await import('./UsersTable/UserDetailsInfo/Origin');
|
||||
return { default: Origin };
|
||||
});
|
||||
|
||||
const UsersDrawerItem = React.lazy(async () => {
|
||||
const { UsersDrawerItem } = await import('./UsersDrawerItem');
|
||||
return { default: UsersDrawerItem };
|
||||
});
|
||||
|
||||
const UsersAdministration = React.lazy(async () => {
|
||||
const { UsersAdministration } = await import('./UsersAdministration');
|
||||
return { default: UsersAdministration };
|
||||
});
|
||||
|
||||
export interface IUserDetailsInfoProps {
|
||||
user: AdminUser;
|
||||
|
||||
@@ -6,12 +6,18 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
|
||||
import { ServerConfigurationAdministrationNavService, ServerConfigurationService } from '@cloudbeaver/plugin-administration';
|
||||
import { AuthenticationService } from '@cloudbeaver/plugin-authentication';
|
||||
|
||||
import { AuthConfigurationsAdministrationNavService } from './Administration/IdentityProviders/AuthConfigurationsAdministrationNavService';
|
||||
import { AuthenticationProviders } from './Administration/ServerConfiguration/AuthenticationProviders';
|
||||
|
||||
const AuthenticationProviders = React.lazy(async () => {
|
||||
const { AuthenticationProviders } = await import('./Administration/ServerConfiguration/AuthenticationProviders');
|
||||
return { default: AuthenticationProviders };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class PluginBootstrap extends Bootstrap {
|
||||
|
||||
@@ -7,17 +7,24 @@
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { useResource } from '@cloudbeaver/core-blocks';
|
||||
import { Loader, useResource } from '@cloudbeaver/core-blocks';
|
||||
import { DBDriverResource } from '@cloudbeaver/core-connections';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { ProjectInfoResource } from '@cloudbeaver/core-projects';
|
||||
import { AdminConnectionSearchInfo, CachedMapAllKey } from '@cloudbeaver/core-sdk';
|
||||
import { ConnectionForm } from '@cloudbeaver/plugin-connections';
|
||||
import { ConnectionFormLoader } from '@cloudbeaver/plugin-connections';
|
||||
|
||||
import { ConnectionSearchService } from './ConnectionSearchService';
|
||||
import { DatabaseList } from './DatabaseList';
|
||||
|
||||
const styles = css`
|
||||
Loader {
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SearchDatabase: React.FC = observer(function SearchDatabase() {
|
||||
const connectionSearchService = useService(ConnectionSearchService);
|
||||
|
||||
@@ -29,12 +36,14 @@ export const SearchDatabase: React.FC = observer(function SearchDatabase() {
|
||||
}
|
||||
|
||||
if (connectionSearchService.formState) {
|
||||
return (
|
||||
<ConnectionForm
|
||||
state={connectionSearchService.formState}
|
||||
onSave={() => connectionSearchService.saveConnection()}
|
||||
onCancel={() => connectionSearchService.goBack()}
|
||||
/>
|
||||
return styled(styles)(
|
||||
<Loader loading={false} overlay>
|
||||
<ConnectionFormLoader
|
||||
state={connectionSearchService.formState}
|
||||
onSave={() => connectionSearchService.saveConnection()}
|
||||
onCancel={() => connectionSearchService.goBack()}
|
||||
/>
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -19,7 +19,7 @@ import {
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { useController } from '@cloudbeaver/core-di';
|
||||
import { CommonDialogWrapper, DialogComponent } from '@cloudbeaver/core-dialogs';
|
||||
import { ConnectionAuthenticationForm } from '@cloudbeaver/plugin-connections';
|
||||
import { ConnectionAuthenticationFormLoader } from '@cloudbeaver/plugin-connections';
|
||||
|
||||
import { ConnectionController, ConnectionStep } from './ConnectionController';
|
||||
import { ConnectionDialogFooter } from './ConnectionDialogFooter';
|
||||
@@ -37,6 +37,9 @@ const styles = css`
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
ConnectionAuthenticationFormLoader {
|
||||
align-content: center;
|
||||
}
|
||||
ErrorMessage {
|
||||
composes: theme-background-secondary theme-text-on-secondary from global;
|
||||
}
|
||||
@@ -88,7 +91,7 @@ export const ConnectionDialog: DialogComponent<null, null> = observer(function C
|
||||
</center>
|
||||
) : (
|
||||
<SubmittingForm ref={focusedRef} onSubmit={controller.onConnect}>
|
||||
<ConnectionAuthenticationForm
|
||||
<ConnectionAuthenticationFormLoader
|
||||
config={controller.config}
|
||||
authModelId={controller.authModel.id}
|
||||
networkHandlers={controller.networkHandlers}
|
||||
|
||||
+23
-5
@@ -6,6 +6,8 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { AdministrationItemService, AdministrationItemType } from '@cloudbeaver/core-administration';
|
||||
import { PlaceholderContainer } from '@cloudbeaver/core-blocks';
|
||||
import { ConnectionInfoActiveProjectKey, ConnectionInfoResource, DatabaseConnection, DBDriverResource } from '@cloudbeaver/core-connections';
|
||||
@@ -13,17 +15,33 @@ import { injectable, Bootstrap } from '@cloudbeaver/core-di';
|
||||
import { CommonDialogService, ConfirmationDialog, DialogueStateResult } from '@cloudbeaver/core-dialogs';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
|
||||
import { ConnectionsAdministration } from './ConnectionsAdministration';
|
||||
import { ConnectionsDrawerItem } from './ConnectionsDrawerItem';
|
||||
import { Origin } from './ConnectionsTable/ConnectionDetailsInfo/Origin';
|
||||
import { SSH } from './ConnectionsTable/ConnectionDetailsInfo/SSH';
|
||||
import { Template } from './ConnectionsTable/ConnectionDetailsInfo/Template';
|
||||
import { CreateConnectionService } from './CreateConnectionService';
|
||||
|
||||
export interface IConnectionDetailsPlaceholderProps {
|
||||
connection: DatabaseConnection;
|
||||
}
|
||||
|
||||
const ConnectionsAdministration = React.lazy(async () => {
|
||||
const { ConnectionsAdministration } = await import('./ConnectionsAdministration');
|
||||
return { default: ConnectionsAdministration };
|
||||
});
|
||||
const ConnectionsDrawerItem = React.lazy(async () => {
|
||||
const { ConnectionsDrawerItem } = await import('./ConnectionsDrawerItem');
|
||||
return { default: ConnectionsDrawerItem };
|
||||
});
|
||||
const Origin = React.lazy(async () => {
|
||||
const { Origin } = await import('./ConnectionsTable/ConnectionDetailsInfo/Origin');
|
||||
return { default: Origin };
|
||||
});
|
||||
const SSH = React.lazy(async () => {
|
||||
const { SSH } = await import('./ConnectionsTable/ConnectionDetailsInfo/SSH');
|
||||
return { default: SSH };
|
||||
});
|
||||
const Template = React.lazy(async () => {
|
||||
const { Template } = await import('./ConnectionsTable/ConnectionDetailsInfo/Template');
|
||||
return { default: Template };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class ConnectionsAdministrationService extends Bootstrap {
|
||||
readonly connectionDetailsPlaceholder = new PlaceholderContainer<IConnectionDetailsPlaceholderProps>();
|
||||
|
||||
+10
-4
@@ -10,9 +10,10 @@ import { observer } from 'mobx-react-lite';
|
||||
import { useRef, useEffect } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { Loader } from '@cloudbeaver/core-blocks';
|
||||
import { ConnectionInfoResource, IConnectionInfoParams } from '@cloudbeaver/core-connections';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { ConnectionForm, useConnectionFormState } from '@cloudbeaver/plugin-connections';
|
||||
import { ConnectionFormLoader, useConnectionFormState } from '@cloudbeaver/plugin-connections';
|
||||
|
||||
const styles = css`
|
||||
box {
|
||||
@@ -23,6 +24,9 @@ const styles = css`
|
||||
flex-direction: column;
|
||||
height: 740px;
|
||||
}
|
||||
Loader {
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
@@ -54,11 +58,13 @@ export const ConnectionEdit = observer<Props>(function ConnectionEditNew({
|
||||
|
||||
return styled(styles)(
|
||||
<box ref={boxRef}>
|
||||
<ConnectionForm
|
||||
state={data}
|
||||
<Loader loading={false} overlay>
|
||||
<ConnectionFormLoader
|
||||
state={data}
|
||||
// onCancel={collapse}
|
||||
// onSave={collapse}
|
||||
/>
|
||||
/>
|
||||
</Loader>
|
||||
</box>
|
||||
);
|
||||
});
|
||||
|
||||
+9
-6
@@ -13,7 +13,7 @@ import { IconButton, Loader, StaticImage, Icon, useResource, useTranslate, useSt
|
||||
import { DBDriverResource } from '@cloudbeaver/core-connections';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { TabsState, TabList, UNDERLINE_TAB_STYLES, TabPanelList, BASE_TAB_STYLES } from '@cloudbeaver/core-ui';
|
||||
import { ConnectionForm } from '@cloudbeaver/plugin-connections';
|
||||
import { ConnectionFormLoader } from '@cloudbeaver/plugin-connections';
|
||||
|
||||
import { CreateConnectionService } from '../CreateConnectionService';
|
||||
|
||||
@@ -70,6 +70,7 @@ const styles = css`
|
||||
|
||||
Loader {
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
title-bar {
|
||||
@@ -137,11 +138,13 @@ export const CreateConnection = observer<Props>(function CreateConnection({
|
||||
<IconButton name="cross" viewBox="0 0 24 24" onClick={createConnectionService.cancelCreate} />
|
||||
</title-bar>
|
||||
<connection-create-content>
|
||||
<ConnectionForm
|
||||
state={createConnectionService.data}
|
||||
onCancel={createConnectionService.clearConnectionTemplate}
|
||||
onSave={createConnectionService.clearConnectionTemplate}
|
||||
/>
|
||||
<Loader loading={false} overlay>
|
||||
<ConnectionFormLoader
|
||||
state={createConnectionService.data}
|
||||
onCancel={createConnectionService.clearConnectionTemplate}
|
||||
onSave={createConnectionService.clearConnectionTemplate}
|
||||
/>
|
||||
</Loader>
|
||||
</connection-create-content>
|
||||
</connection-create>
|
||||
);
|
||||
|
||||
+7
-1
@@ -6,10 +6,16 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
|
||||
|
||||
import { CreateConnectionService } from '../CreateConnectionService';
|
||||
import { CustomConnection } from './Manual/CustomConnection';
|
||||
|
||||
const CustomConnection = React.lazy(async () => {
|
||||
const { CustomConnection } = await import('./Manual/CustomConnection');
|
||||
return { default: CustomConnection };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class CreateConnectionBaseBootstrap extends Bootstrap {
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ export class ConnectionManualService {
|
||||
|
||||
constructor(
|
||||
private readonly connectionInfoResource: ConnectionInfoResource,
|
||||
private createConnectionService: CreateConnectionService
|
||||
private readonly createConnectionService: CreateConnectionService
|
||||
) {
|
||||
this.select = this.select.bind(this);
|
||||
}
|
||||
+6
-1
@@ -5,6 +5,7 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { AdministrationScreenService } from '@cloudbeaver/core-administration';
|
||||
import { EAdminPermission } from '@cloudbeaver/core-authentication';
|
||||
@@ -16,9 +17,13 @@ import { PermissionsService } from '@cloudbeaver/core-root';
|
||||
import type { MetadataValueGetter } from '@cloudbeaver/core-utils';
|
||||
import { connectionConfigContext, ConnectionFormService, connectionFormStateContext, IConnectionFormProps, IConnectionFormState, IConnectionFormSubmitData } from '@cloudbeaver/plugin-connections';
|
||||
|
||||
import { ConnectionAccess } from './ConnectionAccess';
|
||||
import type { IConnectionAccessTabState } from './IConnectionAccessTabState';
|
||||
|
||||
const ConnectionAccess = React.lazy(async () => {
|
||||
const { ConnectionAccess } = await import('./ConnectionAccess');
|
||||
return { default: ConnectionAccess };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class ConnectionAccessTabService extends Bootstrap {
|
||||
private readonly key: string;
|
||||
|
||||
+3
-4
@@ -12,11 +12,10 @@ import styled, { css } from 'reshadow';
|
||||
import { SubmittingForm, useFocus, Button, useTranslate, useStyles } from '@cloudbeaver/core-blocks';
|
||||
import { useDBDriver } from '@cloudbeaver/core-connections';
|
||||
import { CommonDialogWrapper, DialogComponent } from '@cloudbeaver/core-dialogs';
|
||||
|
||||
import type { ConnectionConfig } from '@cloudbeaver/core-sdk';
|
||||
|
||||
|
||||
import { ConnectionAuthenticationForm } from './ConnectionAuthenticationForm';
|
||||
import { ConnectionAuthenticationFormLoader } from './ConnectionAuthenticationFormLoader';
|
||||
|
||||
const styles = css`
|
||||
SubmittingForm {
|
||||
@@ -26,7 +25,7 @@ const styles = css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
ConnectionAuthenticationForm {
|
||||
ConnectionAuthenticationFormLoader {
|
||||
align-content: center;
|
||||
}
|
||||
Button {
|
||||
@@ -64,7 +63,7 @@ export const ConnectionAuthenticationDialog: DialogComponent<Payload> = observer
|
||||
onReject={rejectDialog}
|
||||
>
|
||||
<SubmittingForm ref={focusedRef} onSubmit={() => resolveDialog()}>
|
||||
<ConnectionAuthenticationForm
|
||||
<ConnectionAuthenticationFormLoader
|
||||
config={payload.config}
|
||||
authModelId={payload.authModelId}
|
||||
networkHandlers={payload.networkHandlers}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const ConnectionAuthenticationDialogLoader = React.lazy(async () => {
|
||||
const { ConnectionAuthenticationDialog } = await import('./ConnectionAuthenticationDialog');
|
||||
return { default: ConnectionAuthenticationDialog };
|
||||
});
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const ConnectionAuthenticationFormLoader = React.lazy(async () => {
|
||||
const { ConnectionAuthenticationForm } = await import('./ConnectionAuthenticationForm');
|
||||
return { default: ConnectionAuthenticationForm };
|
||||
});
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const ConnectionFormBaseActionsLoader = React.lazy(async () => {
|
||||
const { ConnectionFormBaseActions } = await import('./ConnectionFormBaseActions');
|
||||
return { default: ConnectionFormBaseActions };
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const ConnectionFormLoader = React.lazy(async () => {
|
||||
const { ConnectionForm } = await import('./ConnectionForm');
|
||||
return { default: ConnectionForm };
|
||||
});
|
||||
@@ -16,8 +16,8 @@ import { ExecutorHandlersCollection, ExecutorInterrupter, IExecutorHandler, IExe
|
||||
import { LocalizationService } from '@cloudbeaver/core-localization';
|
||||
import { TabsContainer } from '@cloudbeaver/core-ui';
|
||||
|
||||
import { ConnectionAuthenticationDialog } from '../ConnectionAuthentication/ConnectionAuthenticationDialog';
|
||||
import { ConnectionFormBaseActions } from './ConnectionFormBaseActions';
|
||||
import { ConnectionAuthenticationDialogLoader } from '../ConnectionAuthentication/ConnectionAuthenticationDialogLoader';
|
||||
import { ConnectionFormBaseActionsLoader } from './ConnectionFormBaseActionsLoader';
|
||||
import { connectionConfigContext } from './Contexts/connectionConfigContext';
|
||||
import { connectionCredentialsStateContext } from './Contexts/connectionCredentialsStateContext';
|
||||
import type { IConnectionFormProps, IConnectionFormState, IConnectionFormFillConfigData, IConnectionFormSubmitData } from './IConnectionFormProps';
|
||||
@@ -74,7 +74,7 @@ export class ConnectionFormService {
|
||||
this.formSubmittingTask.addPostHandler(this.showSubmittingStatusMessage);
|
||||
this.formValidationTask.addPostHandler(this.ensureValidation);
|
||||
|
||||
this.actionsContainer.add(ConnectionFormBaseActions);
|
||||
this.actionsContainer.add(ConnectionFormBaseActionsLoader);
|
||||
}
|
||||
|
||||
connectionValidationContext = (): IConnectionFormValidation => ({
|
||||
@@ -153,7 +153,7 @@ export class ConnectionFormService {
|
||||
}
|
||||
});
|
||||
|
||||
const result = await this.commonDialogService.open(ConnectionAuthenticationDialog, {
|
||||
const result = await this.commonDialogService.open(ConnectionAuthenticationDialogLoader, {
|
||||
config,
|
||||
authModelId: credentialsState.authModelId,
|
||||
networkHandlers: credentialsState.networkHandlers,
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ import { ConnectionFormService } from '../ConnectionFormService';
|
||||
import { connectionConfigContext } from '../Contexts/connectionConfigContext';
|
||||
import { connectionFormStateContext } from '../Contexts/connectionFormStateContext';
|
||||
import type { IConnectionFormFillConfigData, IConnectionFormSubmitData, IConnectionFormState } from '../IConnectionFormProps';
|
||||
import { DriverProperties } from './DriverProperties';
|
||||
import { DriverPropertiesLoader } from './DriverPropertiesLoader';
|
||||
|
||||
@injectable()
|
||||
export class ConnectionDriverPropertiesTabService extends Bootstrap {
|
||||
@@ -39,7 +39,7 @@ export class ConnectionDriverPropertiesTabService extends Bootstrap {
|
||||
name: 'customConnection_properties',
|
||||
title: 'customConnection_properties',
|
||||
order: 2,
|
||||
panel: () => DriverProperties,
|
||||
panel: () => DriverPropertiesLoader,
|
||||
isDisabled: (tabId, props) => {
|
||||
if (props?.state.config.driverId) {
|
||||
return !props.state.config.driverId;
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const DriverPropertiesLoader = React.lazy(async () => {
|
||||
const { DriverProperties } = await import('./DriverProperties');
|
||||
return { default: DriverProperties };
|
||||
});
|
||||
+6
-1
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import { action, makeObservable, runInAction, toJS } from 'mobx';
|
||||
import React from 'react';
|
||||
|
||||
import { AuthProvidersResource, AUTH_PROVIDER_LOCAL_ID, EAdminPermission, UserInfoResource } from '@cloudbeaver/core-authentication';
|
||||
import { ConnectionInfoProjectKey, createConnectionParam, DatabaseAuthModelsResource, DatabaseConnection, DBDriverResource, isLocalConnection } from '@cloudbeaver/core-connections';
|
||||
@@ -24,7 +25,11 @@ import { connectionConfigContext } from '../Contexts/connectionConfigContext';
|
||||
import { connectionCredentialsStateContext } from '../Contexts/connectionCredentialsStateContext';
|
||||
import { connectionFormStateContext } from '../Contexts/connectionFormStateContext';
|
||||
import type { IConnectionFormSubmitData, IConnectionFormFillConfigData, IConnectionFormState } from '../IConnectionFormProps';
|
||||
import { Options } from './Options';
|
||||
|
||||
export const Options = React.lazy(async () => {
|
||||
const { Options } = await import('./Options');
|
||||
return { default: Options };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class ConnectionOptionsTabService extends Bootstrap {
|
||||
|
||||
+15
-3
@@ -6,6 +6,8 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { isLocalConnection } from '@cloudbeaver/core-connections';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
|
||||
@@ -13,9 +15,19 @@ import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
|
||||
import { connectionFormConfigureContext } from '../connectionFormConfigureContext';
|
||||
import { ConnectionFormService } from '../ConnectionFormService';
|
||||
import type { IConnectionFormState } from '../IConnectionFormProps';
|
||||
import { ConnectionFormAuthenticationAction } from './ConnectionFormAuthenticationAction';
|
||||
import { OriginInfo } from './OriginInfo';
|
||||
import { OriginInfoTab } from './OriginInfoTab';
|
||||
|
||||
export const ConnectionFormAuthenticationAction = React.lazy(async () => {
|
||||
const { ConnectionFormAuthenticationAction } = await import('./ConnectionFormAuthenticationAction');
|
||||
return { default: ConnectionFormAuthenticationAction };
|
||||
});
|
||||
export const OriginInfo = React.lazy(async () => {
|
||||
const { OriginInfo } = await import('./OriginInfo');
|
||||
return { default: OriginInfo };
|
||||
});
|
||||
export const OriginInfoTab = React.lazy(async () => {
|
||||
const { OriginInfoTab } = await import('./OriginInfoTab');
|
||||
return { default: OriginInfoTab };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class ConnectionOriginInfoTabService extends Bootstrap {
|
||||
|
||||
+10
-2
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import { action, makeObservable } from 'mobx';
|
||||
import React from 'react';
|
||||
|
||||
import { DBDriverResource, SSH_TUNNEL_ID } from '@cloudbeaver/core-connections';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
@@ -19,8 +20,15 @@ import { connectionConfigContext } from '../Contexts/connectionConfigContext';
|
||||
import { connectionCredentialsStateContext } from '../Contexts/connectionCredentialsStateContext';
|
||||
import { connectionFormStateContext } from '../Contexts/connectionFormStateContext';
|
||||
import type { IConnectionFormFillConfigData, IConnectionFormState, IConnectionFormSubmitData } from '../IConnectionFormProps';
|
||||
import { SSHPanel } from './SSHPanel';
|
||||
import { SSHTab } from './SSHTab';
|
||||
|
||||
export const SSHTab = React.lazy(async () => {
|
||||
const { SSHTab } = await import('./SSHTab');
|
||||
return { default: SSHTab };
|
||||
});
|
||||
export const SSHPanel = React.lazy(async () => {
|
||||
const { SSHPanel } = await import('./SSHPanel');
|
||||
return { default: SSHPanel };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class ConnectionSSHTabService extends Bootstrap {
|
||||
|
||||
@@ -21,7 +21,7 @@ import { IConnectionInfoParams, useConnectionInfo, useDBDriver } from '@cloudbea
|
||||
import { useController } from '@cloudbeaver/core-di';
|
||||
import { CommonDialogWrapper, DialogComponent } from '@cloudbeaver/core-dialogs';
|
||||
|
||||
import { ConnectionAuthenticationForm } from '../ConnectionAuthentication/ConnectionAuthenticationForm';
|
||||
import { ConnectionAuthenticationFormLoader } from '../ConnectionAuthentication/ConnectionAuthenticationFormLoader';
|
||||
import { DBAuthDialogController } from './DBAuthDialogController';
|
||||
import { DBAuthDialogFooter } from './DBAuthDialogFooter';
|
||||
|
||||
@@ -33,7 +33,7 @@ const styles = css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
ConnectionAuthenticationForm {
|
||||
ConnectionAuthenticationFormLoader {
|
||||
align-content: center;
|
||||
}
|
||||
ErrorMessage {
|
||||
@@ -91,7 +91,7 @@ export const DatabaseAuthDialog: DialogComponent<Payload> = observer(function Da
|
||||
{!connection.isLoaded() || connection.isLoading() || !controller.configured
|
||||
? <Loader />
|
||||
: (
|
||||
<ConnectionAuthenticationForm
|
||||
<ConnectionAuthenticationFormLoader
|
||||
config={controller.config}
|
||||
authModelId={authModelId}
|
||||
authProperties={connection.connectionInfo?.authProperties}
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ import styled, { css } from 'reshadow';
|
||||
import { Loader } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
|
||||
import { ConnectionForm } from '../ConnectionForm/ConnectionForm';
|
||||
import { ConnectionFormLoader } from '../ConnectionForm/ConnectionFormLoader';
|
||||
import { PublicConnectionFormService } from './PublicConnectionFormService';
|
||||
|
||||
const styles = css`
|
||||
@@ -31,7 +31,7 @@ export const PublicConnectionForm: React.FC = observer(function PublicConnection
|
||||
return styled(styles)(
|
||||
<Loader loading={service.formState === null}>
|
||||
{() => service.formState && (
|
||||
<ConnectionForm
|
||||
<ConnectionFormLoader
|
||||
// key={service.formState.id}
|
||||
state={service.formState}
|
||||
onSave={save}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { connectionPlugin } from './manifest';
|
||||
export * from './ConnectionAuthentication/IConnectionAuthenticationConfig';
|
||||
export * from './ConnectionAuthentication/ConnectionAuthenticationForm';
|
||||
export * from './ConnectionAuthentication/ConnectionAuthenticationFormLoader';
|
||||
export * from './ConnectionForm/Options/ConnectionOptionsTabService';
|
||||
export * from './ConnectionForm/DriverProperties/ConnectionDriverPropertiesTabService';
|
||||
export * from './ConnectionForm/SSH/ConnectionSSHTabService';
|
||||
export * from './ConnectionForm/OriginInfo/ConnectionOriginInfoTabService';
|
||||
export * from './ConnectionForm/Contexts/connectionConfigContext';
|
||||
export * from './ConnectionForm/ConnectionFormBaseActions';
|
||||
export * from './ConnectionForm/ConnectionFormBaseActionsLoader';
|
||||
export * from './ConnectionForm/connectionFormConfigureContext';
|
||||
export * from './ConnectionForm/ConnectionForm';
|
||||
export * from './ConnectionForm/ConnectionFormLoader';
|
||||
export * from './ConnectionForm/ConnectionFormService';
|
||||
export * from './ConnectionForm/ConnectionFormState';
|
||||
export * from './ConnectionForm/Contexts/connectionFormStateContext';
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Loader, TextPlaceholder } from '@cloudbeaver/core-blocks';
|
||||
import type { ObjectPagePanelComponent } from '@cloudbeaver/plugin-object-viewer';
|
||||
|
||||
import type { IDataViewerPageState } from '../IDataViewerPageState';
|
||||
import { TableViewer } from '../TableViewer/TableViewer';
|
||||
import { TableViewerLoader } from '../TableViewer/TableViewerLoader';
|
||||
import { useDataViewerDatabaseDataModel } from './useDataViewerDatabaseDataModel';
|
||||
|
||||
export const DataViewerPanel: ObjectPagePanelComponent<IDataViewerPageState> = observer(function DataViewerPanel({
|
||||
@@ -58,7 +58,7 @@ export const DataViewerPanel: ObjectPagePanelComponent<IDataViewerPageState> = o
|
||||
return (
|
||||
<Loader state={dataViewerDatabaseDataModel}>
|
||||
{tab.handlerState.tableId ? (
|
||||
<TableViewer
|
||||
<TableViewerLoader
|
||||
tableId={tab.handlerState.tableId}
|
||||
resultIndex={pageState?.resultIndex}
|
||||
presentationId={pageState?.presentationId}
|
||||
|
||||
@@ -89,7 +89,7 @@ interface ErrorInfo {
|
||||
show: () => void;
|
||||
}
|
||||
|
||||
export const TableError = observer<Props>(function TableViewer({
|
||||
export const TableError = observer<Props>(function TableError({
|
||||
model,
|
||||
loading,
|
||||
className,
|
||||
|
||||
+11
-2
@@ -6,6 +6,8 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { PlaceholderContainer } from '@cloudbeaver/core-blocks';
|
||||
import { injectable, Bootstrap } from '@cloudbeaver/core-di';
|
||||
import { MenuService, ActionService, DATA_CONTEXT_MENU } from '@cloudbeaver/core-view';
|
||||
@@ -16,8 +18,15 @@ import type { IDatabaseDataModel } from '../../DatabaseDataModel/IDatabaseDataMo
|
||||
import { DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL } from './DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL';
|
||||
import { DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX } from './DATA_CONTEXT_DATA_VIEWER_DATABASE_DATA_MODEL_RESULT_INDEX';
|
||||
import { DATA_VIEWER_DATA_MODEL_TOOLS_MENU } from './DATA_VIEWER_DATA_MODEL_TOOLS_MENU';
|
||||
import { TableHeaderMenu } from './TableHeaderMenu';
|
||||
import { TableWhereFilter } from './TableWhereFilter';
|
||||
|
||||
export const TableWhereFilter = React.lazy(async () => {
|
||||
const { TableWhereFilter } = await import('./TableWhereFilter');
|
||||
return { default: TableWhereFilter };
|
||||
});
|
||||
export const TableHeaderMenu = React.lazy(async () => {
|
||||
const { TableHeaderMenu } = await import('./TableHeaderMenu');
|
||||
return { default: TableHeaderMenu };
|
||||
});
|
||||
|
||||
export interface ITableHeaderPlaceholderProps {
|
||||
model: IDatabaseDataModel<any, any>;
|
||||
|
||||
@@ -15,7 +15,6 @@ import { getComputed, Loader, Pane, ResizerControls, Split, splitStyles, TextPla
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { ResultDataFormat } from '@cloudbeaver/core-sdk';
|
||||
|
||||
|
||||
import { ResultSetConstraintAction } from '../DatabaseDataModel/Actions/ResultSet/ResultSetConstraintAction';
|
||||
import { DataPresentationService, DataPresentationType } from '../DataPresentationService';
|
||||
import type { IDataTableActionsPrivate } from './IDataTableActions';
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const TableViewerLoader = React.lazy(async () => {
|
||||
const { TableViewer } = await import('./TableViewer');
|
||||
return { default: TableViewer };
|
||||
});
|
||||
+7
-1
@@ -6,11 +6,17 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { DataPresentationService, DataPresentationType } from '../../DataPresentationService';
|
||||
import { DataValuePanelService } from './DataValuePanelService';
|
||||
import { ValuePanel } from './ValuePanel';
|
||||
|
||||
export const ValuePanel = React.lazy(async () => {
|
||||
const { ValuePanel } = await import('./ValuePanel');
|
||||
return { default: ValuePanel };
|
||||
});
|
||||
|
||||
@injectable()
|
||||
export class DataValuePanelBootstrap extends Bootstrap {
|
||||
|
||||
@@ -46,7 +46,8 @@ export * from './TableViewer/ValuePanel/DataValuePanelService';
|
||||
|
||||
export * from './TableViewer/IDataTableActions';
|
||||
export * from './TableViewer/IDataPresentationActions';
|
||||
export * from './TableViewer/TableViewer';
|
||||
|
||||
export * from './TableViewer/TableViewerLoader';
|
||||
export * from './TableViewer/TableFooter/TableFooterMenu/TableFooterMenuService';
|
||||
|
||||
export * from './ContainerDataSource';
|
||||
|
||||
+5
-5
@@ -17,8 +17,8 @@ import { type NavNode, ROOT_NODE_PATH, NavTreeResource, NavNodeInfoResource, EOb
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { useNavTreeDropBox } from '../useNavTreeDropBox';
|
||||
import { ElementsTreeContentLoader } from './ElementsTreeContentLoader';
|
||||
import { IElementsTreeContext, ElementsTreeContext } from './ElementsTreeContext';
|
||||
import { ElementsTreeLoader } from './ElementsTreeLoader';
|
||||
import { elementsTreeNameFilter } from './elementsTreeNameFilter';
|
||||
import { ElementsTreeTools } from './ElementsTreeTools/ElementsTreeTools';
|
||||
import type { IElementsTreeSettingsProps } from './ElementsTreeTools/NavigationTreeSettings/ElementsTreeSettingsService';
|
||||
@@ -88,7 +88,7 @@ const styles = css`
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props extends IElementsTreeOptions {
|
||||
export interface ElementsTreeProps extends IElementsTreeOptions, React.PropsWithChildren {
|
||||
root?: string;
|
||||
limit?: number;
|
||||
selectionTree?: boolean;
|
||||
@@ -103,7 +103,7 @@ interface Props extends IElementsTreeOptions {
|
||||
onOpen?: (node: NavNode, folder: boolean) => Promise<void> | void;
|
||||
}
|
||||
|
||||
export const ElementsTree = observer<Props>(function ElementsTree({
|
||||
export const ElementsTree = observer<ElementsTreeProps>(function ElementsTree({
|
||||
root: baseRoot = ROOT_NODE_PATH,
|
||||
limit,
|
||||
control,
|
||||
@@ -321,7 +321,7 @@ export const ElementsTree = observer<Props>(function ElementsTree({
|
||||
<TreeNodeNestedMessage><Translate token='app_navigationTree_drop_here' /></TreeNodeNestedMessage>
|
||||
</TreeNodeNested>
|
||||
</drop-outside>
|
||||
<ElementsTreeLoader
|
||||
<ElementsTreeContentLoader
|
||||
root={root}
|
||||
context={context}
|
||||
emptyPlaceholder={emptyPlaceholder}
|
||||
@@ -338,7 +338,7 @@ export const ElementsTree = observer<Props>(function ElementsTree({
|
||||
/>
|
||||
</tree-elements>
|
||||
{loaderAvailable && <Loader state={tree} overlay={hasChildren} />}
|
||||
</ElementsTreeLoader>
|
||||
</ElementsTreeContentLoader>
|
||||
</tree>
|
||||
</FolderExplorer>
|
||||
</box>
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ interface Props {
|
||||
hasChildren?: boolean;
|
||||
}
|
||||
|
||||
export const ElementsTreeLoader = observer<React.PropsWithChildren<Props>>(function ElementsTreeLoader({
|
||||
export const ElementsTreeContentLoader = observer<React.PropsWithChildren<Props>>(function ElementsTreeContentLoader({
|
||||
root,
|
||||
context,
|
||||
childrenState,
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const ElementsTreeLoader = React.lazy(async () => {
|
||||
const { ElementsTree } = await import('./ElementsTree');
|
||||
return { default: ElementsTree };
|
||||
});
|
||||
+2
-2
@@ -18,7 +18,7 @@ import { useDataContext } from '@cloudbeaver/core-view';
|
||||
|
||||
import { useNavTreeDropBox } from '../../useNavTreeDropBox';
|
||||
import type { NavigationNodeComponent } from '../NavigationNodeComponent';
|
||||
import { NavigationNodeControl } from './NavigationNode/NavigationNodeControl';
|
||||
import { NavigationNodeControlLoader } from './NavigationNode/NavigationNodeLoaders';
|
||||
import { NavigationNodeNested } from './NavigationNode/NavigationNodeNested';
|
||||
import { useNavigationNode } from './useNavigationNode';
|
||||
|
||||
@@ -64,7 +64,7 @@ export const NavigationNode: NavigationNodeComponent = observer(function Navigat
|
||||
context.set(DATA_CONTEXT_NAV_NODE, node);
|
||||
context.set(DATA_CONTEXT_NAV_NODES, navNode.getSelected);
|
||||
|
||||
const Control = navNode.control || externalControl || NavigationNodeControl;
|
||||
const Control = navNode.control || externalControl || NavigationNodeControlLoader;
|
||||
|
||||
if (navNode.leaf || !navNode.loaded) {
|
||||
externalExpanded = false;
|
||||
|
||||
+4
-4
@@ -17,8 +17,8 @@ import { NavNodeInfoResource, NavTreeResource, EObjectFeature, type INodeActions
|
||||
|
||||
import { ElementsTreeContext } from '../../ElementsTreeContext';
|
||||
import type { NavTreeControlComponent, NavTreeControlProps } from '../../NavigationNodeComponent';
|
||||
import { TreeNodeMenu } from '../TreeNodeMenu/TreeNodeMenu';
|
||||
import { NavigationNodeEditor } from './NavigationNodeEditor';
|
||||
import { TreeNodeMenuLoader } from '../TreeNodeMenu/TreeNodeMenuLoader';
|
||||
import { NavigationNodeEditorLoader } from './NavigationNodeLoaders';
|
||||
|
||||
const nodeIconStyle = css`
|
||||
TreeNodeIcon[|connected] StaticImage {
|
||||
@@ -112,14 +112,14 @@ export const NavigationNodeControl: NavTreeControlComponent = observer<NavTreeCo
|
||||
</TreeNodeIcon>
|
||||
<TreeNodeName title={node.name}>
|
||||
{editing ? (
|
||||
<NavigationNodeEditor node={node} onClose={() => setEditing(false)} />
|
||||
<NavigationNodeEditorLoader node={node} onClose={() => setEditing(false)} />
|
||||
) : (
|
||||
<name-box>{node.name}</name-box>
|
||||
)}
|
||||
</TreeNodeName>
|
||||
{!editing && !dndPlaceholder && (
|
||||
<portal onClick={handlePortalClick}>
|
||||
<TreeNodeMenu node={node} actions={nodeActions} selected={selected} />
|
||||
<TreeNodeMenuLoader node={node} actions={nodeActions} selected={selected} />
|
||||
</portal>
|
||||
)}
|
||||
</TreeNodeControl>
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const NavigationNodeControlLoader = React.lazy(async () => {
|
||||
const { NavigationNodeControl } = await import('./NavigationNodeControl');
|
||||
return { default: NavigationNodeControl };
|
||||
});
|
||||
|
||||
export const NavigationNodeEditorLoader = React.lazy(async () => {
|
||||
const { NavigationNodeEditor } = await import('./NavigationNodeEditor');
|
||||
return { default: NavigationNodeEditor };
|
||||
});
|
||||
+2
-2
@@ -15,7 +15,7 @@ import { getComputed, TreeNode, useObjectRef, useStyles } from '@cloudbeaver/cor
|
||||
|
||||
import { ElementsTreeContext } from '../ElementsTreeContext';
|
||||
import type { NavigationNodeComponent } from '../NavigationNodeComponent';
|
||||
import { NavigationNodeControl } from './NavigationNode/NavigationNodeControl';
|
||||
import { NavigationNodeControlLoader } from './NavigationNode/NavigationNodeLoaders';
|
||||
|
||||
export const NavigationNodeDragged: NavigationNodeComponent = observer(function NavigationNode({
|
||||
node,
|
||||
@@ -28,7 +28,7 @@ export const NavigationNodeDragged: NavigationNodeComponent = observer(function
|
||||
});
|
||||
const control = getComputed(() => contextRef.context?.control);
|
||||
|
||||
const Control = control || externalControl || NavigationNodeControl;
|
||||
const Control = control || externalControl || NavigationNodeControlLoader;
|
||||
|
||||
return styled(useStyles(style))(
|
||||
<TreeNode externalExpanded={false} className={className} leaf>
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ import { NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree';
|
||||
|
||||
import { ElementsTreeContext } from '../ElementsTreeContext';
|
||||
import type { NavTreeNodeComponent } from '../NavigationNodeComponent';
|
||||
import { NavigationNodeRenderer } from './NavigationNodeRenderer';
|
||||
import { NavigationNodeRendererLoader } from './NavigationNodeRendererLoader';
|
||||
|
||||
export const NavigationNodeElement: NavTreeNodeComponent = observer(function NavigationNodeElement({
|
||||
nodeId,
|
||||
@@ -59,7 +59,7 @@ export const NavigationNodeElement: NavTreeNodeComponent = observer(function Nav
|
||||
|
||||
// TODO: after node update reference can be lost and NavigationNode skip update
|
||||
return (
|
||||
<NavigationNodeRenderer
|
||||
<NavigationNodeRendererLoader
|
||||
node={node}
|
||||
path={path}
|
||||
expanded={expanded}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const NavigationNodeRendererLoader = React.lazy(async () => {
|
||||
const { NavigationNodeRenderer } = await import('./NavigationNodeRenderer');
|
||||
return { default: NavigationNodeRenderer };
|
||||
});
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const TreeNodeMenuLoader = React.lazy(async () => {
|
||||
const { TreeNodeMenu } = await import('./TreeNodeMenu');
|
||||
return { default: TreeNodeMenu };
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const NavigationTreeLoader = React.lazy(async () => {
|
||||
const { NavigationTree } = await import('./NavigationTree');
|
||||
return { default: NavigationTree };
|
||||
});
|
||||
+5
-5
@@ -17,8 +17,8 @@ import { NavNodeInfoResource, type INodeActions } from '@cloudbeaver/core-naviga
|
||||
|
||||
import { ElementsTreeContext } from '../ElementsTree/ElementsTreeContext';
|
||||
import type { NavTreeControlComponent, NavTreeControlProps } from '../ElementsTree/NavigationNodeComponent';
|
||||
import { NavigationNodeEditor } from '../ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeEditor';
|
||||
import { TreeNodeMenu } from '../ElementsTree/NavigationTreeNode/TreeNodeMenu/TreeNodeMenu';
|
||||
import { NavigationNodeEditorLoader } from '../ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeLoaders';
|
||||
import { TreeNodeMenuLoader } from '../ElementsTree/NavigationTreeNode/TreeNodeMenu/TreeNodeMenuLoader';
|
||||
|
||||
const styles = css`
|
||||
TreeNodeControl {
|
||||
@@ -54,7 +54,7 @@ const styles = css`
|
||||
}
|
||||
`;
|
||||
|
||||
export const NavigationNodeProjectControl: NavTreeControlComponent = observer<NavTreeControlProps, HTMLDivElement>(forwardRef(function NavigationNodeControl({
|
||||
export const NavigationNodeProjectControl: NavTreeControlComponent = observer<NavTreeControlProps, HTMLDivElement>(forwardRef(function NavigationNodeProjectControl({
|
||||
node,
|
||||
dndElement,
|
||||
dndPlaceholder,
|
||||
@@ -96,14 +96,14 @@ export const NavigationNodeProjectControl: NavTreeControlComponent = observer<Na
|
||||
>
|
||||
<TreeNodeName title={node.name}>
|
||||
{editing ? (
|
||||
<NavigationNodeEditor node={node} onClose={() => setEditing(false)} />
|
||||
<NavigationNodeEditorLoader node={node} onClose={() => setEditing(false)} />
|
||||
) : (
|
||||
<name-box>{node.name}</name-box>
|
||||
)}
|
||||
</TreeNodeName>
|
||||
{!editing && !dndPlaceholder && (
|
||||
<portal onClick={handlePortalClick}>
|
||||
<TreeNodeMenu node={node} actions={nodeActions} selected={selected} />
|
||||
<TreeNodeMenuLoader node={node} actions={nodeActions} selected={selected} />
|
||||
</portal>
|
||||
)}
|
||||
</TreeNodeControl>
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ import { NAV_NODE_TYPE_PROJECT, ProjectsService } from '@cloudbeaver/core-projec
|
||||
import { useNode } from '../../NodesManager/useNode';
|
||||
import { ElementsTreeContext } from '../ElementsTree/ElementsTreeContext';
|
||||
import type { NavigationNodeRendererComponent } from '../ElementsTree/NavigationNodeComponent';
|
||||
import { NavigationNodeRenderer } from '../ElementsTree/NavigationTreeNode/NavigationNodeRenderer';
|
||||
import { NavigationNodeRendererLoader } from '../ElementsTree/NavigationTreeNode/NavigationNodeRendererLoader';
|
||||
import type { IElementsTreeCustomRenderer } from '../ElementsTree/useElementsTree';
|
||||
import { NavigationNodeProjectControl } from './NavigationNodeProjectControl';
|
||||
|
||||
@@ -81,7 +81,7 @@ const ProjectRenderer: NavigationNodeRendererComponent = observer(function Manag
|
||||
const project = node.nodeType === NAV_NODE_TYPE_PROJECT && singleProject;
|
||||
|
||||
return styled(nestedStyles)(
|
||||
<NavigationNodeRenderer
|
||||
<NavigationNodeRendererLoader
|
||||
node={node}
|
||||
path={path}
|
||||
expanded={expanded}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2022 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 React from 'react';
|
||||
|
||||
export const NodeLinkLoader = React.lazy(async () => {
|
||||
const { NodeLink } = await import('./NodeLink');
|
||||
return { default: NodeLink };
|
||||
});
|
||||
@@ -2,13 +2,16 @@ export * from './NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSet
|
||||
export * from './NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/DATA_CONTEXT_NAV_TREE_ROOT';
|
||||
export * from './NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/KEY_BINDING_ENABLE_FILTER';
|
||||
export * from './NavigationTree/ElementsTree/ElementsTreeTools/NavigationTreeSettings/ElementsTreeSettingsService';
|
||||
export * from './NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeEditor';
|
||||
export * from './NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeControl';
|
||||
export * from './NavigationTree/ElementsTree/NavigationTreeNode/TreeNodeMenu/TreeNodeMenu';
|
||||
export * from './NavigationTree/ElementsTree/NavigationTreeNode/NavigationNodeRenderer';
|
||||
|
||||
export * from './NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeLoaders';
|
||||
export * from './NavigationTree/ElementsTree/NavigationTreeNode/TreeNodeMenu/TreeNodeMenuLoader';
|
||||
export * from './NavigationTree/ElementsTree/NavigationTreeNode/NavigationNodeRendererLoader';
|
||||
|
||||
export * from './NavigationTree/ElementsTree/ACTION_LINK_OBJECT';
|
||||
export * from './NavigationTree/ElementsTree/DATA_CONTEXT_ELEMENTS_TREE';
|
||||
export * from './NavigationTree/ElementsTree/ElementsTree';
|
||||
|
||||
export * from './NavigationTree/ElementsTree/ElementsTreeLoader';
|
||||
|
||||
export * from './NavigationTree/ElementsTree/ElementsTreeContext';
|
||||
export * from './NavigationTree/ElementsTree/KEY_BINDING_COLLAPSE_ALL';
|
||||
export * from './NavigationTree/ElementsTree/KEY_BINDING_LINK_OBJECT';
|
||||
@@ -21,7 +24,9 @@ export * from './NavigationTree/ElementsTree/ElementsTreeTools/ElementsTreeTools
|
||||
export * from './NavigationTree/ElementsTree/elementsTreeNameFilter';
|
||||
export * from './NavigationTree/NavigationTreeBootstrap';
|
||||
export * from './NavigationTree/NavigationTreeService';
|
||||
export * from './NavigationTree/NavigationTree';
|
||||
|
||||
export * from './NavigationTree/NavigationTreeLoader';
|
||||
|
||||
export * from './NavigationTree/getNavigationTreeUserSettingsId';
|
||||
export * from './NodesManager/NavNodeView/IFolderTransform';
|
||||
export * from './NodesManager/NavNodeView/NavNodeViewService';
|
||||
@@ -29,7 +34,9 @@ export * from './NodesManager/NavNodeContextMenuService';
|
||||
export * from './NodesManager/useDatabaseObjectInfo';
|
||||
export * from './NodesManager/useNode';
|
||||
export * from './NodesManager/useChildren';
|
||||
export * from './NodesManager/NodeLink';
|
||||
|
||||
export * from './NodesManager/NodeLinkLoader';
|
||||
|
||||
import { navigationTreePlugin } from './manifest';
|
||||
|
||||
export { navigationTreePlugin };
|
||||
|
||||
+3
-3
@@ -17,7 +17,7 @@ import { NavNodeInfoResource, type INodeActions } from '@cloudbeaver/core-naviga
|
||||
import { ProjectInfoResource } from '@cloudbeaver/core-projects';
|
||||
import { NAV_NODE_TYPE_RM_PROJECT } from '@cloudbeaver/core-resource-manager';
|
||||
import { CaptureViewContext } from '@cloudbeaver/core-view';
|
||||
import { ElementsTreeContext, NavigationNodeEditor, NavTreeControlComponent, NavTreeControlProps, TreeNodeMenu } from '@cloudbeaver/plugin-navigation-tree';
|
||||
import { ElementsTreeContext, NavigationNodeEditorLoader, NavTreeControlComponent, NavTreeControlProps, TreeNodeMenuLoader } from '@cloudbeaver/plugin-navigation-tree';
|
||||
|
||||
import { getRmProjectNodeId } from '../../NavNodes/getRmProjectNodeId';
|
||||
import { ResourceManagerService } from '../../ResourceManagerService';
|
||||
@@ -125,14 +125,14 @@ export const NavigationNodeProjectControl: NavTreeControlComponent = observer<Na
|
||||
>
|
||||
<TreeNodeName title={name}>
|
||||
{editing ? (
|
||||
<NavigationNodeEditor node={node} onClose={() => setEditing(false)} />
|
||||
<NavigationNodeEditorLoader node={node} onClose={() => setEditing(false)} />
|
||||
) : (
|
||||
<name-box>{name}</name-box>
|
||||
)}
|
||||
</TreeNodeName>
|
||||
{!editing && !dndPlaceholder && (
|
||||
<portal onClick={handlePortalClick}>
|
||||
<TreeNodeMenu node={node} actions={nodeActions} selected={selected} />
|
||||
<TreeNodeMenuLoader node={node} actions={nodeActions} selected={selected} />
|
||||
</portal>
|
||||
)}
|
||||
</TreeNodeControl>
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ import type { NavNodeInfoResource, ProjectsNavNodeService } from '@cloudbeaver/c
|
||||
import { ProjectsService } from '@cloudbeaver/core-projects';
|
||||
import { NAV_NODE_TYPE_RM_PROJECT, RESOURCES_NODE_PATH } from '@cloudbeaver/core-resource-manager';
|
||||
import { createPath } from '@cloudbeaver/core-utils';
|
||||
import { type IElementsTreeCustomRenderer, type NavigationNodeRendererComponent, useNode, NavigationNodeRenderer, ElementsTreeContext } from '@cloudbeaver/plugin-navigation-tree';
|
||||
import { type IElementsTreeCustomRenderer, type NavigationNodeRendererComponent, useNode, NavigationNodeRendererLoader, ElementsTreeContext } from '@cloudbeaver/plugin-navigation-tree';
|
||||
|
||||
import type { ResourceManagerService } from '../../ResourceManagerService';
|
||||
import { NavigationNodeProjectControl } from './NavigationNodeProjectControl';
|
||||
@@ -100,7 +100,7 @@ const ProjectRenderer: NavigationNodeRendererComponent = observer(function Manag
|
||||
const project = node.nodeType === NAV_NODE_TYPE_RM_PROJECT && singleProject;
|
||||
|
||||
return styled(nestedStyles)(
|
||||
<NavigationNodeRenderer
|
||||
<NavigationNodeRendererLoader
|
||||
node={node}
|
||||
path={path}
|
||||
expanded={expanded}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects
|
||||
import { IResourceManagerParams, ResourceManagerResource, RESOURCES_NODE_PATH } from '@cloudbeaver/core-resource-manager';
|
||||
import { resourceKeyList } from '@cloudbeaver/core-sdk';
|
||||
import { CaptureView } from '@cloudbeaver/core-view';
|
||||
import { NavigationTreeService, ElementsTree, IElementsTreeSettings, createElementsTreeSettings, validateElementsTreeSettings, getNavigationTreeUserSettingsId } from '@cloudbeaver/plugin-navigation-tree';
|
||||
import { NavigationTreeService, ElementsTreeLoader, IElementsTreeSettings, createElementsTreeSettings, validateElementsTreeSettings, getNavigationTreeUserSettingsId } from '@cloudbeaver/plugin-navigation-tree';
|
||||
|
||||
import { ResourceManagerService } from '../ResourceManagerService';
|
||||
import { navigationTreeProjectFilter } from './ProjectsRenderer/navigationTreeProjectFilter';
|
||||
@@ -37,7 +37,7 @@ const styles = css`
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
ElementsTree {
|
||||
ElementsTreeLoader {
|
||||
min-width: 100%;
|
||||
width: max-content;
|
||||
}
|
||||
@@ -170,7 +170,7 @@ export const ResourceManagerTree: React.FC<Props> = observer(function ResourceMa
|
||||
return styled(styles)(
|
||||
<CaptureView view={navTreeService}>
|
||||
<ResourceManagerTreeCaptureViewContext resourceTypeId={resourceTypeId} />
|
||||
<ElementsTree
|
||||
<ElementsTreeLoader
|
||||
root={root}
|
||||
getChildren={navTreeService.getChildren}
|
||||
loadChildren={navTreeService.loadNestedNodes}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { TableViewer } from '@cloudbeaver/plugin-data-viewer';
|
||||
import { TableViewerLoader } from '@cloudbeaver/plugin-data-viewer';
|
||||
|
||||
import type { IResultGroup, IResultTab } from '../ISqlEditorTabState';
|
||||
|
||||
@@ -26,7 +26,7 @@ export const SqlResultSetPanel = observer<Props>(function SqlResultSetPanel({
|
||||
const [valuePresentationId, setValuePresentation] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<TableViewer
|
||||
<TableViewerLoader
|
||||
tableId={group.modelId}
|
||||
resultIndex={resultTab.indexInResultSet}
|
||||
presentationId={presentationId}
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"prebuild": "rimraf lib/* && rimraf tsconfig.tsbuildinfo",
|
||||
"build": "core-cli-build --mode=production --config ../core-cli/configs/webpack.product.config.js",
|
||||
"dev": "core-cli-build serve --mode=development --progress --config=../core-cli/configs/webpack.product.dev.config.js --port=3100",
|
||||
"analyse": "core-cli-build --config ../core-cli/configs/webpack.product.config.js --profile --json > ./lib/stats.json",
|
||||
"preanalyse": "rimraf lib/* && if not exist lib mkdir lib",
|
||||
"analyse": "core-cli-build --mode=production --config ../core-cli/configs/webpack.product.config.js --profile --json > ./lib/stats.json",
|
||||
"postanalyse": "core-cli-analyzer ./lib/stats.json",
|
||||
"lint": "eslint ./src/ --ext .ts,.tsx",
|
||||
"lint-fix": "eslint ./src/ --ext .ts,.tsx --fix",
|
||||
|
||||
Reference in New Issue
Block a user