Добавлен редкатор дашборда
This commit is contained in:
parent
6925fde0e9
commit
8f36e6cca2
5 changed files with 223 additions and 0 deletions
63
frontend/package-lock.json
generated
63
frontend/package-lock.json
generated
|
|
@ -8,6 +8,7 @@
|
|||
"name": "frontend",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
|
|
@ -2977,6 +2978,30 @@
|
|||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
||||
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
|
||||
},
|
||||
"node_modules/@monaco-editor/loader": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz",
|
||||
"integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==",
|
||||
"dependencies": {
|
||||
"state-local": "^1.0.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"monaco-editor": ">= 0.21.0 < 1"
|
||||
}
|
||||
},
|
||||
"node_modules/@monaco-editor/react": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.6.0.tgz",
|
||||
"integrity": "sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==",
|
||||
"dependencies": {
|
||||
"@monaco-editor/loader": "^1.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"monaco-editor": ">= 0.25.0 < 1",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
|
||||
"version": "5.1.1-v1",
|
||||
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
|
||||
|
|
@ -11945,6 +11970,12 @@
|
|||
"mobx": "^6.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-editor": {
|
||||
"version": "0.44.0",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.44.0.tgz",
|
||||
"integrity": "sha512-5SmjNStN6bSuSE5WPT2ZV+iYn1/yI9sd4Igtk23ChvqB7kDk9lZbB9F5frsuvpB+2njdIeGGFf2G4gbE6rCC9Q==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
|
|
@ -15205,6 +15236,11 @@
|
|||
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
|
||||
"integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="
|
||||
},
|
||||
"node_modules/state-local": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
|
|
@ -19072,6 +19108,22 @@
|
|||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
||||
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
|
||||
},
|
||||
"@monaco-editor/loader": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz",
|
||||
"integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==",
|
||||
"requires": {
|
||||
"state-local": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"@monaco-editor/react": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.6.0.tgz",
|
||||
"integrity": "sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==",
|
||||
"requires": {
|
||||
"@monaco-editor/loader": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"@nicolo-ribaudo/eslint-scope-5-internals": {
|
||||
"version": "5.1.1-v1",
|
||||
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
|
||||
|
|
@ -25596,6 +25648,12 @@
|
|||
"integrity": "sha512-oe82BNgMr408e6DxMDNat8msXQTuyuqzJ97DPupbhchEfjjHyjsmPSwtXHl+nXiW3tybpb/cr5siUClBqKqv+Q==",
|
||||
"requires": {}
|
||||
},
|
||||
"monaco-editor": {
|
||||
"version": "0.44.0",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.44.0.tgz",
|
||||
"integrity": "sha512-5SmjNStN6bSuSE5WPT2ZV+iYn1/yI9sd4Igtk23ChvqB7kDk9lZbB9F5frsuvpB+2njdIeGGFf2G4gbE6rCC9Q==",
|
||||
"peer": true
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
|
|
@ -27772,6 +27830,11 @@
|
|||
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
|
||||
"integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="
|
||||
},
|
||||
"state-local": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="
|
||||
},
|
||||
"statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import React from 'react';
|
|||
import * as DashboardStoreNs from './dashboard-store';
|
||||
import * as TopRightMenuNs from '../misc-components/top-right-menu';
|
||||
import * as WidgetNs from './widget';
|
||||
import * as EditorNs from './editor';
|
||||
|
||||
export type Props = { store: DashboardStoreNs.IDashboard };
|
||||
|
||||
|
|
@ -19,11 +20,20 @@ export const Dashboard = observer((props: Props): JSX.Element => {
|
|||
return <WidgetNs.Widget key={widget.id} store={widget}></WidgetNs.Widget>;
|
||||
});
|
||||
|
||||
const editorStore = EditorNs.Store.create({ dashboardId: props.store.id });
|
||||
const onEditClick = (e: React.MouseEvent) => {
|
||||
if (e.target !== e.currentTarget) return;
|
||||
e.stopPropagation();
|
||||
editorStore.show();
|
||||
};
|
||||
|
||||
const res = (
|
||||
<div>
|
||||
<EditorNs.Editor store={editorStore}></EditorNs.Editor>
|
||||
<TopRightMenuNs.TopRightMenu store={topRightMenuStore}>
|
||||
<a href="/dashboards">Назад</a>
|
||||
<span>Дашборд - {props.store.data?.title || props.store.id}</span>
|
||||
<button onClick={onEditClick}>edit</button>
|
||||
</TopRightMenuNs.TopRightMenu>
|
||||
{widgets}
|
||||
</div>
|
||||
|
|
|
|||
27
frontend/src/dashboard/editor.module.css
Normal file
27
frontend/src/dashboard/editor.module.css
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
.reset {
|
||||
all: initial;
|
||||
}
|
||||
|
||||
.modal {
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
display: none;
|
||||
padding-top: 40px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgb(0, 0, 0);
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.modalContent {
|
||||
height: 80%;
|
||||
overflow: auto;
|
||||
background-color: #fefefe;
|
||||
margin: auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
}
|
||||
122
frontend/src/dashboard/editor.tsx
Normal file
122
frontend/src/dashboard/editor.tsx
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Editor as MonacoEditor } from '@monaco-editor/react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { Instance, onSnapshot, types } from 'mobx-state-tree';
|
||||
import Css from './editor.module.css';
|
||||
|
||||
export const Store = types
|
||||
.model({
|
||||
loaded: false,
|
||||
dashboardId: '',
|
||||
visible: false,
|
||||
data: '',
|
||||
})
|
||||
.actions((self) => {
|
||||
return {
|
||||
setData: (data: string) => {
|
||||
self.loaded = true;
|
||||
self.data = data;
|
||||
},
|
||||
show: () => {
|
||||
if (!self.loaded) LoadDashboardToStore(self as any);
|
||||
self.visible = true;
|
||||
},
|
||||
hide: () => {
|
||||
self.visible = false;
|
||||
},
|
||||
toggleVisible: () => {
|
||||
self.visible = !self.visible;
|
||||
},
|
||||
};
|
||||
})
|
||||
.views((self) => {
|
||||
return {
|
||||
get displayStyle(): React.CSSProperties {
|
||||
return { display: self.visible ? 'block' : 'none' };
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
type IStore = Instance<typeof Store>;
|
||||
|
||||
export async function LoadDashboard(dashboardId: string): Promise<string> {
|
||||
const url = `${process.env.REACT_APP_BACKEND}api/dashboard/${dashboardId}`;
|
||||
const resp = await fetch(url);
|
||||
if (!resp || !resp.ok) return '';
|
||||
const data = await resp.json();
|
||||
const text = JSON.stringify(data, null, ' ');
|
||||
return text;
|
||||
}
|
||||
|
||||
export async function LoadDashboardToStore(store: IStore): Promise<void> {
|
||||
const data = await LoadDashboard(store.dashboardId);
|
||||
if (data) store.setData(data);
|
||||
}
|
||||
|
||||
export async function SaveDashboard(
|
||||
dashboardId: string,
|
||||
data: string,
|
||||
): Promise<void> {
|
||||
const url = `${process.env.REACT_APP_BACKEND}api/dashboard/${dashboardId}`;
|
||||
await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: data,
|
||||
});
|
||||
}
|
||||
|
||||
export async function SaveDashboardFromStore(store: IStore): Promise<void> {
|
||||
await SaveDashboard(store.dashboardId, store.data);
|
||||
}
|
||||
|
||||
export type Props = {
|
||||
store: IStore;
|
||||
};
|
||||
|
||||
export const Editor = observer((props: Props): JSX.Element => {
|
||||
const onCloseClick = (e: React.MouseEvent) => {
|
||||
if (e.target !== e.currentTarget) return;
|
||||
e.stopPropagation();
|
||||
props.store.hide();
|
||||
};
|
||||
const onSaveClick = async (e: React.MouseEvent) => {
|
||||
if (e.target !== e.currentTarget) return;
|
||||
e.stopPropagation();
|
||||
props.store.setData(editorValue);
|
||||
await SaveDashboardFromStore(props.store);
|
||||
alert('Сохранено');
|
||||
};
|
||||
|
||||
const [editorValue, setEditorValue] = useState(props.store.data);
|
||||
onSnapshot(props.store, (state) => {
|
||||
setEditorValue(state.data);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={Css.reset}>
|
||||
<div
|
||||
className={Css.modal}
|
||||
style={props.store.displayStyle}
|
||||
onClick={onCloseClick}
|
||||
>
|
||||
<div className={Css.modalContent}>
|
||||
<h1>
|
||||
<button onClick={onCloseClick}>close</button>
|
||||
<button onClick={onSaveClick}>save</button>
|
||||
Редактор дашборда
|
||||
</h1>
|
||||
<MonacoEditor
|
||||
height="80%"
|
||||
defaultLanguage="json"
|
||||
defaultValue={editorValue}
|
||||
value={editorValue}
|
||||
onChange={(value) => setEditorValue(value || '')}
|
||||
></MonacoEditor>
|
||||
</div>
|
||||
</div>
|
||||
Editor
|
||||
</div>
|
||||
);
|
||||
});
|
||||
Loading…
Reference in a new issue