[CB] Create project form with options tab (#4386)

* dbeaver/pro#9525 register project-info plugin

* adds project info to the context menu

* adds project info form

* adds project options part info tab content

* separates logic with bootstraps

* removes unused locales

* pr fixes

* removes global and shared checkboxes

---------

Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
sergeyteleshev
2026-06-09 19:52:04 +02:00
committed by GitHub
co-authored by Evgenia
parent a16c138bba
commit 96ba18da83
27 changed files with 649 additions and 0 deletions
@@ -0,0 +1,53 @@
{
"name": "@cloudbeaver/plugin-project-info",
"type": "module",
"sideEffects": [
"./lib/module.js",
"./lib/index.js",
"src/**/*.css",
"src/**/*.scss",
"public/**/*"
],
"version": "0.1.0",
"description": "",
"license": "Apache-2.0",
"exports": {
".": "./lib/index.js",
"./module": "./lib/module.js"
},
"scripts": {
"build": "tsc -b",
"clean": "rimraf --glob lib",
"lint": "eslint ./src/ --ext .ts,.tsx",
"test": "dbeaver-test",
"validate-dependencies": "core-cli-validate-dependencies"
},
"dependencies": {
"@cloudbeaver/core-blocks": "workspace:*",
"@cloudbeaver/core-data-context": "workspace:*",
"@cloudbeaver/core-di": "workspace:*",
"@cloudbeaver/core-events": "workspace:*",
"@cloudbeaver/core-executor": "workspace:*",
"@cloudbeaver/core-localization": "workspace:*",
"@cloudbeaver/core-navigation-tree": "workspace:*",
"@cloudbeaver/core-projects": "workspace:*",
"@cloudbeaver/core-ui": "workspace:*",
"@cloudbeaver/core-utils": "workspace:*",
"@cloudbeaver/core-view": "workspace:*",
"@cloudbeaver/plugin-navigation-tree": "workspace:*",
"mobx": "^6",
"mobx-react-lite": "^4",
"react": "^19",
"react-dom": "^19",
"tslib": "^2"
},
"devDependencies": {
"@cloudbeaver/core-cli": "workspace:*",
"@cloudbeaver/tsconfig": "workspace:*",
"@dbeaver/cli": "workspace:*",
"@types/react": "^19",
"rimraf": "^6",
"typescript": "^5",
"typescript-plugin-css-modules": "^5"
}
}
@@ -0,0 +1,37 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { LocalizationService } from '@cloudbeaver/core-localization';
@injectable(() => [LocalizationService])
export class LocaleService extends Bootstrap {
constructor(private readonly localizationService: LocalizationService) {
super();
}
override register(): void {
this.localizationService.addProvider(this.provider.bind(this));
}
private async provider(locale: string) {
switch (locale) {
case 'ru':
return (await import('./locales/ru.js')).default;
case 'it':
return (await import('./locales/it.js')).default;
case 'zh':
return (await import('./locales/zh.js')).default;
case 'fr':
return (await import('./locales/fr.js')).default;
case 'vi':
return (await import('./locales/vi.js')).default;
default:
return (await import('./locales/en.js')).default;
}
}
}
@@ -0,0 +1,51 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { DATA_CONTEXT_NAV_NODE, isProjectNode } from '@cloudbeaver/core-navigation-tree';
import { ActionService, MenuService } from '@cloudbeaver/core-view';
import { ACTION_PROJECT_INFO } from './actions/ACTION_PROJECT_INFO.js';
import { ProjectInfoFormService } from './ProjectInfoForm/ProjectInfoFormService.js';
@injectable(() => [ActionService, MenuService, ProjectInfoFormService])
export class ProjectInfoBootstrap extends Bootstrap {
constructor(
private readonly actionService: ActionService,
private readonly menuService: MenuService,
private readonly projectInfoFormService: ProjectInfoFormService,
) {
super();
}
override register() {
this.menuService.addCreator({
root: true,
contexts: [DATA_CONTEXT_NAV_NODE],
getItems: (context, items) => [...items, ACTION_PROJECT_INFO],
});
this.actionService.addHandler({
id: 'action-project-info-handler',
actions: [ACTION_PROJECT_INFO],
contexts: [DATA_CONTEXT_NAV_NODE],
isActionApplicable: context => {
const node = context.get(DATA_CONTEXT_NAV_NODE);
return isProjectNode(node);
},
handler: context => {
const node = context.get(DATA_CONTEXT_NAV_NODE);
const projectId = node?.projectId;
if (projectId) {
this.projectInfoFormService.open(projectId);
}
},
});
}
}
@@ -0,0 +1,13 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import type { IFormProps } from '@cloudbeaver/core-ui';
import type { IProjectInfoFormState } from './IProjectInfoFormState.js';
export interface IProjectInfoFormProps extends IFormProps<IProjectInfoFormState> {}
@@ -0,0 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export interface IProjectInfoFormState {
projectId: string;
}
@@ -0,0 +1,47 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { observer } from 'mobx-react-lite';
import { ColoredContainer, Container, Group, GroupTitle, InputField, Textarea, useAutoLoad, useTranslate } from '@cloudbeaver/core-blocks';
import type { TabContainerPanelComponent } from '@cloudbeaver/core-ui';
import type { IProjectInfoFormProps } from '../IProjectInfoFormProps.js';
import { getProjectInfoFormOptionsPart } from './getProjectInfoFormOptionsPart.js';
export const ProjectInfoFormOptions: TabContainerPanelComponent<IProjectInfoFormProps> = observer(function ProjectInfoFormOptions({ formState }) {
const translate = useTranslate();
const part = getProjectInfoFormOptionsPart(formState);
useAutoLoad(ProjectInfoFormOptions, part);
if (!part.state) {
return null;
}
return (
<ColoredContainer wrap overflow parent gap>
<Container medium gap>
<Group form gap>
<GroupTitle>{translate('plugin_project_info_form_options_info')}</GroupTitle>
<InputField name="name" state={part.state} readOnly>
{translate('plugin_project_info_form_options_field_name')}
</InputField>
<InputField name="id" state={part.state} readOnly>
{translate('plugin_project_info_form_options_field_id')}
</InputField>
{part.state.description && (
<Textarea name="description" state={part.state} readOnly>
{translate('plugin_project_info_form_options_field_description')}
</Textarea>
)}
</Group>
</Container>
</ColoredContainer>
);
});
@@ -0,0 +1,47 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
import type { ProjectInfoResource } from '@cloudbeaver/core-projects';
import { FormPart, type IFormState } from '@cloudbeaver/core-ui';
import type { IProjectInfoFormState } from '../IProjectInfoFormState.js';
import type { IProjectInfoOptionsSchema } from './ProjectInfoOptionsSchema.js';
const getDefaultState = (): IProjectInfoOptionsSchema => ({
description: '',
id: '',
name: '',
});
export class ProjectInfoFormOptionsPart extends FormPart<IProjectInfoOptionsSchema, IProjectInfoFormState> {
constructor(
formState: IFormState<IProjectInfoFormState>,
private readonly projectInfoResource: ProjectInfoResource,
) {
super(formState, getDefaultState());
}
override isOutdated(): boolean {
return this.projectInfoResource.isOutdated(this.formState.state.projectId);
}
protected override async loader(): Promise<void> {
const projectInfo = await this.projectInfoResource.load(this.formState.state.projectId);
this.setInitialState({
description: projectInfo.description,
id: projectInfo.id,
name: projectInfo.name,
});
}
protected override async saveChanges(
_data: IFormState<IProjectInfoFormState>,
_contexts: IExecutionContextProvider<IFormState<IProjectInfoFormState>>,
): Promise<void> {}
}
@@ -0,0 +1,28 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { ProjectInfoFormService } from '../ProjectInfoFormService.js';
import { ProjectInfoFormOptions } from './ProjectInfoFormOptions.js';
@injectable(() => [ProjectInfoFormService])
export class ProjectInfoFormOptionsTabBootstrap extends Bootstrap {
constructor(private readonly projectInfoFormService: ProjectInfoFormService) {
super();
}
override register(): void {
this.projectInfoFormService.parts.add({
key: 'options',
name: 'plugin_project_info_form_tab_options',
order: 1,
panel: () => ProjectInfoFormOptions,
});
}
}
@@ -0,0 +1,17 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { schema } from '@cloudbeaver/core-utils';
export const ProjectInfoOptionsSchema = schema.object({
id: schema.string().nonempty(),
name: schema.string().nonempty(),
description: schema.string().optional(),
});
export type IProjectInfoOptionsSchema = schema.infer<typeof ProjectInfoOptionsSchema>;
@@ -0,0 +1,25 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createDataContext, DATA_CONTEXT_DI_PROVIDER } from '@cloudbeaver/core-data-context';
import { ProjectInfoResource } from '@cloudbeaver/core-projects';
import type { IFormState } from '@cloudbeaver/core-ui';
import type { IProjectInfoFormState } from '../IProjectInfoFormState.js';
import { ProjectInfoFormOptionsPart } from './ProjectInfoFormOptionsPart.js';
const DATA_CONTEXT_PROJECT_INFO_FORM_OPTIONS_PART = createDataContext<ProjectInfoFormOptionsPart>('project-info-form-options-part');
export function getProjectInfoFormOptionsPart(formState: IFormState<IProjectInfoFormState>): ProjectInfoFormOptionsPart {
return formState.getPart(DATA_CONTEXT_PROJECT_INFO_FORM_OPTIONS_PART, context => {
const di = context.get(DATA_CONTEXT_DI_PROVIDER)!;
const projectInfoResource = di.getService(ProjectInfoResource);
return new ProjectInfoFormOptionsPart(formState, projectInfoResource);
});
}
@@ -0,0 +1,27 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { observer } from 'mobx-react-lite';
import { ColoredContainer, Loader } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { ProjectInfoFormService } from './ProjectInfoFormService.js';
import { ProjectInfoFormPanel } from './ProjectInfoFormPanel.js';
export const ProjectInfoForm: React.FC = observer(function ProjectInfoForm() {
const service = useService(ProjectInfoFormService);
return (
<ColoredContainer>
<Loader suspense>
{service.formState && <ProjectInfoFormPanel formState={service.formState} />}
</Loader>
</ColoredContainer>
);
});
@@ -0,0 +1,33 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { observer } from 'mobx-react-lite';
import { useService } from '@cloudbeaver/core-di';
import { TabList, TabPanelList, TabsState } from '@cloudbeaver/core-ui';
import type { IProjectInfoFormProps } from './IProjectInfoFormProps.js';
import { ProjectInfoFormService } from './ProjectInfoFormService.js';
export const ProjectInfoFormPanel = observer<IProjectInfoFormProps>(function ProjectInfoFormPanel({ formState }) {
const service = useService(ProjectInfoFormService);
return (
<TabsState container={service.parts} localState={formState.parts} formState={formState}>
<div className="tw:flex tw:flex-col tw:flex-1 tw:h-full tw:overflow-auto theme-background-secondary theme-text-on-secondary">
<div className="tw:relative tw:flex tw:items-end tw:border-b-2 theme-border-color-background theme-background-secondary theme-text-on-secondary">
<div className="tw:flex-1 tw:overflow-hidden tw:pt-2">
<TabList disabled={formState.isDisabled} underline big />
</div>
</div>
<div className="tw:relative tw:flex tw:flex-1 tw:flex-col tw:overflow-auto theme-background-secondary theme-border-color-background">
<TabPanelList />
</div>
</div>
</TabsState>
);
});
@@ -0,0 +1,60 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { action, makeObservable, observable } from 'mobx';
import { importLazyComponent } from '@cloudbeaver/core-blocks';
import { injectable, IServiceProvider } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { LocalizationService } from '@cloudbeaver/core-localization';
import { FormBaseService, FormMode, FormState, OptionsPanelService } from '@cloudbeaver/core-ui';
import type { IProjectInfoFormState } from './IProjectInfoFormState.js';
const ProjectInfoForm = importLazyComponent(() => import('./ProjectInfoForm.js').then(m => m.ProjectInfoForm));
const formGetter = () => ProjectInfoForm;
@injectable(() => [LocalizationService, NotificationService, OptionsPanelService, IServiceProvider])
export class ProjectInfoFormService extends FormBaseService<IProjectInfoFormState> {
formState: FormState<IProjectInfoFormState> | null;
constructor(
localizationService: LocalizationService,
notificationService: NotificationService,
private readonly optionsPanelService: OptionsPanelService,
private readonly serviceProvider: IServiceProvider,
) {
super(localizationService, notificationService, 'ProjectInfoForm');
this.formState = null;
makeObservable(this, {
formState: observable.shallow,
open: action.bound,
close: action.bound,
});
}
async open(projectId: string): Promise<boolean> {
const opened = await this.optionsPanelService.open(formGetter);
if (opened) {
this.formState?.dispose();
this.formState = new FormState<IProjectInfoFormState>(this.serviceProvider, this, { projectId }).setMode(FormMode.Edit);
}
return opened;
}
async close(): Promise<void> {
this.formState?.dispose();
this.formState = null;
await this.optionsPanelService.close();
}
}
@@ -0,0 +1,14 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createAction } from '@cloudbeaver/core-view';
export const ACTION_PROJECT_INFO = createAction('project-info', {
label: 'plugin_project_info_tree_menu_item_title',
tooltip: 'plugin_project_info_tree_menu_item_title',
});
@@ -0,0 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import './module.js';
export { ProjectInfoFormService } from './ProjectInfoForm/ProjectInfoFormService.js';
@@ -0,0 +1,8 @@
export default [
['plugin_project_info_tree_menu_item_title', 'Project Info'],
['plugin_project_info_form_tab_options', 'Options'],
['plugin_project_info_form_options_info', 'Info'],
['plugin_project_info_form_options_field_name', 'Name'],
['plugin_project_info_form_options_field_id', 'ID'],
['plugin_project_info_form_options_field_description', 'Description'],
];
@@ -0,0 +1,8 @@
export default [
['plugin_project_info_tree_menu_item_title', 'Info du projet'],
['plugin_project_info_form_tab_options', 'Options'],
['plugin_project_info_form_options_info', 'Informations'],
['plugin_project_info_form_options_field_name', 'Nom'],
['plugin_project_info_form_options_field_id', 'Identifiant'],
['plugin_project_info_form_options_field_description', 'Description'],
];
@@ -0,0 +1,8 @@
export default [
['plugin_project_info_tree_menu_item_title', 'Info del progetto'],
['plugin_project_info_form_tab_options', 'Opzioni'],
['plugin_project_info_form_options_info', 'Informazioni'],
['plugin_project_info_form_options_field_name', 'Nome'],
['plugin_project_info_form_options_field_id', 'ID'],
['plugin_project_info_form_options_field_description', 'Descrizione'],
];
@@ -0,0 +1,8 @@
export default [
['plugin_project_info_tree_menu_item_title', 'Информация о проекте'],
['plugin_project_info_form_tab_options', 'Опции'],
['plugin_project_info_form_options_info', 'Информация'],
['plugin_project_info_form_options_field_name', 'Название'],
['plugin_project_info_form_options_field_id', 'Идентификатор'],
['plugin_project_info_form_options_field_description', 'Описание'],
];
@@ -0,0 +1,8 @@
export default [
['plugin_project_info_tree_menu_item_title', 'Thông tin dự án'],
['plugin_project_info_form_tab_options', 'Tùy chọn'],
['plugin_project_info_form_options_info', 'Thông tin'],
['plugin_project_info_form_options_field_name', 'Tên'],
['plugin_project_info_form_options_field_id', 'ID'],
['plugin_project_info_form_options_field_description', 'Mô tả'],
];
@@ -0,0 +1,8 @@
export default [
['plugin_project_info_tree_menu_item_title', '项目信息'],
['plugin_project_info_form_tab_options', '选项'],
['plugin_project_info_form_options_info', '信息'],
['plugin_project_info_form_options_field_name', '名称'],
['plugin_project_info_form_options_field_id', '标识符'],
['plugin_project_info_form_options_field_description', '描述'],
];
@@ -0,0 +1,25 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2026 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { Bootstrap, ModuleRegistry } from '@cloudbeaver/core-di';
import { LocaleService } from './LocaleService.js';
import { ProjectInfoFormOptionsTabBootstrap } from './ProjectInfoForm/Options/ProjectInfoFormOptionsTabBootstrap.js';
import { ProjectInfoFormService } from './ProjectInfoForm/ProjectInfoFormService.js';
import { ProjectInfoBootstrap } from './ProjectInfoBootstrap.js';
export default ModuleRegistry.add({
name: '@cloudbeaver/plugin-project-info',
configure: serviceCollection => {
serviceCollection
.addSingleton(Bootstrap, ProjectInfoBootstrap)
.addSingleton(Bootstrap, LocaleService)
.addSingleton(Bootstrap, ProjectInfoFormOptionsTabBootstrap)
.addSingleton(ProjectInfoFormService);
},
});
@@ -0,0 +1,64 @@
{
"extends": "@cloudbeaver/tsconfig/tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib",
"tsBuildInfoFile": "lib/tsconfig.tsbuildinfo",
"composite": true
},
"references": [
{
"path": "../../common-typescript/@dbeaver/cli"
},
{
"path": "../core-blocks"
},
{
"path": "../core-cli"
},
{
"path": "../core-data-context"
},
{
"path": "../core-di"
},
{
"path": "../core-events"
},
{
"path": "../core-executor"
},
{
"path": "../core-localization"
},
{
"path": "../core-navigation-tree"
},
{
"path": "../core-projects"
},
{
"path": "../core-ui"
},
{
"path": "../core-utils"
},
{
"path": "../core-view"
},
{
"path": "../plugin-navigation-tree"
}
],
"include": [
"__custom_mocks__/**/*",
"src/**/*",
"src/**/*.json",
"src/**/*.css",
"src/**/*.scss"
],
"exclude": [
"**/node_modules",
"lib/**/*"
]
}
@@ -96,6 +96,7 @@
"@cloudbeaver/plugin-object-viewer-nav-tree-link": "workspace:*",
"@cloudbeaver/plugin-product": "workspace:*",
"@cloudbeaver/plugin-product-information-administration": "workspace:*",
"@cloudbeaver/plugin-project-info": "workspace:*",
"@cloudbeaver/plugin-projects": "workspace:*",
"@cloudbeaver/plugin-resource-manager": "workspace:*",
"@cloudbeaver/plugin-resource-manager-administration": "workspace:*",
@@ -112,6 +112,7 @@ import pluginDataViewerConditionalFormatting from '@cloudbeaver/plugin-data-view
import pluginConnectionView from '@cloudbeaver/plugin-connection-view/module';
import pluginConnectionPreferences from '@cloudbeaver/plugin-connection-preferences/module';
import pluginScriptExport from '@cloudbeaver/plugin-script-export/module';
import pluginProjectInfo from '@cloudbeaver/plugin-project-info/module';
const core = [
coreRouting, // important, should be first because the router starts in load phase first after all plugins register phase
@@ -223,4 +224,5 @@ export const commonSet = [
pluginDataViewerConditionalFormatting,
pluginConnectionView,
pluginConnectionPreferences,
pluginProjectInfo,
];
@@ -235,6 +235,9 @@
{
"path": "../plugin-product-information-administration"
},
{
"path": "../plugin-project-info"
},
{
"path": "../plugin-projects"
},
+32
View File
@@ -3748,6 +3748,37 @@ __metadata:
languageName: unknown
linkType: soft
"@cloudbeaver/plugin-project-info@workspace:*, @cloudbeaver/plugin-project-info@workspace:packages/plugin-project-info":
version: 0.0.0-use.local
resolution: "@cloudbeaver/plugin-project-info@workspace:packages/plugin-project-info"
dependencies:
"@cloudbeaver/core-blocks": "workspace:*"
"@cloudbeaver/core-cli": "workspace:*"
"@cloudbeaver/core-data-context": "workspace:*"
"@cloudbeaver/core-di": "workspace:*"
"@cloudbeaver/core-events": "workspace:*"
"@cloudbeaver/core-executor": "workspace:*"
"@cloudbeaver/core-localization": "workspace:*"
"@cloudbeaver/core-navigation-tree": "workspace:*"
"@cloudbeaver/core-projects": "workspace:*"
"@cloudbeaver/core-ui": "workspace:*"
"@cloudbeaver/core-utils": "workspace:*"
"@cloudbeaver/core-view": "workspace:*"
"@cloudbeaver/plugin-navigation-tree": "workspace:*"
"@cloudbeaver/tsconfig": "workspace:*"
"@dbeaver/cli": "workspace:*"
"@types/react": "npm:^19"
mobx: "npm:^6"
mobx-react-lite: "npm:^4"
react: "npm:^19"
react-dom: "npm:^19"
rimraf: "npm:^6"
tslib: "npm:^2"
typescript: "npm:^5"
typescript-plugin-css-modules: "npm:^5"
languageName: unknown
linkType: soft
"@cloudbeaver/plugin-projects@workspace:*, @cloudbeaver/plugin-projects@workspace:packages/plugin-projects":
version: 0.0.0-use.local
resolution: "@cloudbeaver/plugin-projects@workspace:packages/plugin-projects"
@@ -4012,6 +4043,7 @@ __metadata:
"@cloudbeaver/plugin-object-viewer-nav-tree-link": "workspace:*"
"@cloudbeaver/plugin-product": "workspace:*"
"@cloudbeaver/plugin-product-information-administration": "workspace:*"
"@cloudbeaver/plugin-project-info": "workspace:*"
"@cloudbeaver/plugin-projects": "workspace:*"
"@cloudbeaver/plugin-resource-manager": "workspace:*"
"@cloudbeaver/plugin-resource-manager-administration": "workspace:*"