{
+ const url = `${process.env.REACT_APP_BACKEND}api/dashboard/${store.id}/load-data/${widgetId}`;
+ const resp = await fetch(url);
+ if (resp && resp.ok) {
+ const data = await resp.json();
+ store.setWidgetData(widgetId, data);
+ }
+ return;
+}
diff --git a/frontend/src/dashboard/dashboard.tsx b/frontend/src/dashboard/dashboard.tsx
index 7dfd4e2..47ef169 100644
--- a/frontend/src/dashboard/dashboard.tsx
+++ b/frontend/src/dashboard/dashboard.tsx
@@ -1,13 +1,36 @@
import { observer } from 'mobx-react-lite';
import React from 'react';
-import * as Store from './dashboard-store';
+import * as DashboardStoreNs from './dashboard-store';
+import * as TopRightMenuNs from '../misc-components/top-right-menu';
+import * as WidgetNs from './widget';
+import { DebugInfo } from '../misc-components/debug-info';
-export type Props = { store: Store.IDashboard };
+export type Props = { store: DashboardStoreNs.IDashboard };
export const Dashboard = observer((props: Props): JSX.Element => {
if (!props.store.loaded) {
+ console.debug('Dashboard - store:', JSON.stringify(props.store)); // DEBUG
return Loading... {JSON.stringify(props.store)};
}
- return {JSON.stringify(props.store, null, ' ')};
+ const debugInfo = JSON.stringify(props.store, null, ' ');
+
+ const topRightMenuStore = TopRightMenuNs.Store.create({ visible: false });
+
+ const widgets = props.store.data?.widgets.map((widget) => {
+ return ;
+ });
+
+ const res = (
+
+
+ Назад
+ Дашборд - {props.store.data?.title || props.store.id}
+
+ {widgets}
+
+
+ );
+
+ return res;
});
diff --git a/frontend/src/dashboard/widget.tsx b/frontend/src/dashboard/widget.tsx
new file mode 100644
index 0000000..0008e55
--- /dev/null
+++ b/frontend/src/dashboard/widget.tsx
@@ -0,0 +1,96 @@
+import { observer } from 'mobx-react-lite';
+import React from 'react';
+import * as DashboardStoreNs from './dashboard-store';
+import * as WidgetFactoryNs from './widgets/widget-factory';
+
+export type Props = {
+ store: DashboardStoreNs.IWidget;
+};
+
+/**
+ * Пример данных передаваемых в виджет:
+ *
+ * {
+ * "type": "kanban_by_tree",
+ * "id": "first",
+ * "title": "Первый виджет",
+ * "dataLoaderParams": {
+ * "rootIssueId": 2,
+ * "groups": {
+ * "fromIssues": [
+ * {
+ * "issueId": 3,
+ * "name": "Тест"
+ * }
+ * ],
+ * "fromIssuesIncluded": false,
+ * "showOthers": true
+ * },
+ * "statuses": [
+ * "New",
+ * "Re-opened",
+ * "In Progress",
+ * "Code Review",
+ * "Resolved",
+ * "Testing",
+ * "Feedback",
+ * "Wait Release",
+ * "Pending",
+ * "Closed",
+ * "Rejected"
+ * ],
+ * "tags": {
+ * "tagsKeyName": "tags",
+ * "styledTagsKeyName": "styledTags",
+ * "styles": {
+ * "supertag": "background-color: rgb(128, 0, 0); color: rgb(255, 255, 255);"
+ * },
+ * "defaultStyle": "background-color: rgb(128, 128, 128);"
+ * },
+ * "priorities": [
+ * {
+ * "rules": [
+ * {
+ * "priorityName": "P1",
+ * "style": "background-color: #DB2228;"
+ * },
+ * {
+ * "priorityName": "P2",
+ * "style": "background-color: #FA5E26;"
+ * },
+ * {
+ * "priorityName": "P3",
+ * "style": "background-color: #FDAF19;"
+ * },
+ * {
+ * "priorityName": "P4",
+ * "style": "background-color: #31A8FF;",
+ * "default": true
+ * },
+ * {
+ * "priorityName": "P5",
+ * "style": "background-color: #FFFFFF; border: 0.5px solid #393838; color: #202020;"
+ * }
+ * ],
+ * "targetKey": "priorityStyle"
+ * }
+ * ]
+ * }
+ * }
+ */
+
+export const Widget = observer((props: Props): JSX.Element => {
+ const display = props.store.visible ? 'block' : 'none';
+
+ return (
+
+
+
+ Title - {props.store.title}
+
+
+
+
+
+ );
+});
diff --git a/frontend/src/dashboard/widgets/kanban-by-tree.tsx b/frontend/src/dashboard/widgets/kanban-by-tree.tsx
new file mode 100644
index 0000000..8641284
--- /dev/null
+++ b/frontend/src/dashboard/widgets/kanban-by-tree.tsx
@@ -0,0 +1,17 @@
+import { Instance, onSnapshot } from 'mobx-state-tree';
+import * as DashboardStoreNs from '../dashboard-store';
+import { observer } from 'mobx-react-lite';
+import * as KanbanBoardsNs from '../../kanban-board/kanban-boards';
+import * as KanbanBoardsStoreNs from '../../kanban-board/store';
+
+export type Props = {
+ store: Instance;
+};
+
+export const KanbanByTree = observer((props: Props): JSX.Element => {
+ const store = KanbanBoardsStoreNs.PageStore.create({ loaded: false });
+ onSnapshot(props.store, (state) => {
+ store.setData(state.data.data);
+ });
+ return ;
+});
diff --git a/frontend/src/dashboard/widgets/widget-factory.tsx b/frontend/src/dashboard/widgets/widget-factory.tsx
new file mode 100644
index 0000000..1cb27fb
--- /dev/null
+++ b/frontend/src/dashboard/widgets/widget-factory.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import * as DashboardStoreNs from '../dashboard-store';
+import { Instance } from 'mobx-state-tree';
+import { observer } from 'mobx-react-lite';
+import * as KanbanByTreeWidgetNs from './kanban-by-tree';
+import { DebugInfo } from '../../misc-components/debug-info';
+
+export type Props = {
+ store: Instance;
+};
+
+export const WidgetFactory = observer((props: Props): JSX.Element => {
+ const type = props.store.type;
+
+ switch (type) {
+ case 'kanban_by_tree':
+ return ;
+ default:
+ return (
+
+ );
+ }
+});
diff --git a/frontend/src/misc-components/debug-info.module.css b/frontend/src/misc-components/debug-info.module.css
new file mode 100644
index 0000000..f429782
--- /dev/null
+++ b/frontend/src/misc-components/debug-info.module.css
@@ -0,0 +1,5 @@
+.debugInfo {
+ margin: 3px;
+ padding: 3px;
+ border: 1px solid black;
+}
\ No newline at end of file
diff --git a/frontend/src/misc-components/debug-info.tsx b/frontend/src/misc-components/debug-info.tsx
new file mode 100644
index 0000000..de71c46
--- /dev/null
+++ b/frontend/src/misc-components/debug-info.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import Css from './debug-info.module.css';
+
+export type Props = {
+ value?: string;
+ children?: any;
+};
+
+export const DebugInfo = (props: Props): JSX.Element => {
+ let output: any;
+ if (props.value) {
+ output = {props.value};
+ } else if (props.children) {
+ output = props.children;
+ } else {
+ output = (none)
;
+ }
+ return {output}
;
+};
diff --git a/libs/event-emitter/src/dashboards/dashboard.controller.ts b/libs/event-emitter/src/dashboards/dashboard.controller.ts
index 013f769..980c234 100644
--- a/libs/event-emitter/src/dashboards/dashboard.controller.ts
+++ b/libs/event-emitter/src/dashboards/dashboard.controller.ts
@@ -36,6 +36,17 @@ export class DashboardController {
);
}
+ @Get(':id/load-data/:widgetId')
+ async loadDataForWidget(
+ @Param('id') id: string,
+ @Param('widgetId') widgetId: string,
+ ): Promise {
+ return await getOrAppErrorOrThrow(
+ () => this.dashboardsDataService.loadDataForWidget(id, widgetId),
+ BadRequestErrorHandler,
+ );
+ }
+
@Put(':id')
async save(@Param('id') id: string, @Body() data: any): Promise {
const res = await getOrAppErrorOrThrow(
diff --git a/libs/event-emitter/src/dashboards/dashboards-data.service.ts b/libs/event-emitter/src/dashboards/dashboards-data.service.ts
index 74fe340..484be8b 100644
--- a/libs/event-emitter/src/dashboards/dashboards-data.service.ts
+++ b/libs/event-emitter/src/dashboards/dashboards-data.service.ts
@@ -24,6 +24,7 @@ export class DashboardsDataService {
let isSuccess = false;
for (let i = 0; i < cfg.widgets.length; i++) {
const widget = cfg.widgets[i];
+ if (widget.collapsed) continue;
const loadRes = await this.loadWidgetData(
widget.type,
widget.widgetParams,
@@ -33,13 +34,32 @@ export class DashboardsDataService {
if (loadRes.result) {
isSuccess = true;
loadRes.result.widgetId = widget.id;
- results.push(loadRes.result);
+ results.push({ data: loadRes.result, widget: widget });
}
}
if (!isSuccess) throw createAppError('CANNOT_LOAD_DATA');
return results;
}
+ async loadDataForWidget(
+ id: string,
+ widgetId: string,
+ ): Promise {
+ const cfg = await this.dashboardsService.load(id);
+ const widget = cfg.widgets.find((widget) => {
+ return widget.id == widgetId;
+ });
+ if (!widget) throw createAppError('WIDGET_NOT_FOUND');
+ const loadRes = await this.loadWidgetData(
+ widget.type,
+ widget.widgetParams,
+ widget.dataLoaderParams,
+ cfg,
+ );
+ if (loadRes.result) return { widget: widget, data: loadRes.error };
+ throw createAppError('CANNOT_LOAD_DATA');
+ }
+
async loadWidgetData(
type: string,
widgetParams: DashboardModel.WidgetParams,