From 8f36e6cca2091ca756d1812b5f9771a293408f4c Mon Sep 17 00:00:00 2001 From: Pavel Gnedov Date: Tue, 7 Nov 2023 01:09:00 +0700 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=80=D0=B5=D0=B4=D0=BA=D0=B0=D1=82=D0=BE=D1=80=20?= =?UTF-8?q?=D0=B4=D0=B0=D1=88=D0=B1=D0=BE=D1=80=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package-lock.json | 63 ++++++++++++ frontend/package.json | 1 + frontend/src/dashboard/dashboard.tsx | 10 ++ frontend/src/dashboard/editor.module.css | 27 +++++ frontend/src/dashboard/editor.tsx | 122 +++++++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 frontend/src/dashboard/editor.module.css create mode 100644 frontend/src/dashboard/editor.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f2feaa5..f00f493 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -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", diff --git a/frontend/package.json b/frontend/package.json index d5577bd..e0008f5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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", diff --git a/frontend/src/dashboard/dashboard.tsx b/frontend/src/dashboard/dashboard.tsx index 654e996..31bfa3d 100644 --- a/frontend/src/dashboard/dashboard.tsx +++ b/frontend/src/dashboard/dashboard.tsx @@ -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 ; }); + 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 = (
+ Назад Дашборд - {props.store.data?.title || props.store.id} + {widgets}
diff --git a/frontend/src/dashboard/editor.module.css b/frontend/src/dashboard/editor.module.css new file mode 100644 index 0000000..d01f80c --- /dev/null +++ b/frontend/src/dashboard/editor.module.css @@ -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%; +} \ No newline at end of file diff --git a/frontend/src/dashboard/editor.tsx b/frontend/src/dashboard/editor.tsx new file mode 100644 index 0000000..c2953d5 --- /dev/null +++ b/frontend/src/dashboard/editor.tsx @@ -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; + +export async function LoadDashboard(dashboardId: string): Promise { + 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 { + const data = await LoadDashboard(store.dashboardId); + if (data) store.setData(data); +} + +export async function SaveDashboard( + dashboardId: string, + data: string, +): Promise { + 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 { + 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 ( +
+
+
+

+ + + Редактор дашборда +

+ setEditorValue(value || '')} + > +
+
+ Editor +
+ ); +});