From 337603e1897f87b5946265baa28ee148255b8d0d Mon Sep 17 00:00:00 2001 From: Zzde Date: Mon, 15 Jul 2024 10:48:29 +0800 Subject: [PATCH] feat(dashboard): edit & update config in dashboard (#1032) --- dashboard-ui-v2/package.json | 2 +- dashboard-ui-v2/pnpm-lock.yaml | 225 ++---------------- dashboard-ui-v2/src/assets/cm-256.png | Bin 0 -> 9925 bytes .../src/components/config-modal.tsx | 135 +++++++++++ dashboard-ui-v2/src/components/index.ts | 12 +- dashboard-ui-v2/src/components/layout.tsx | 88 +++---- dashboard-ui-v2/src/components/log-modal.tsx | 10 +- dashboard-ui-v2/src/hooks/cm-api.ts | 21 ++ dashboard-ui-v2/src/hooks/use-api.ts | 6 +- dashboard-ui-v2/src/icons/index.tsx | 9 +- dashboard-ui-v2/src/locales/zh-CN.ts | 4 + dashboard-ui-v2/src/types/index.ts | 9 +- dashboard-ui-v2/src/utils/index.ts | 4 +- pkg/dashboard/api.go | 2 + pkg/dashboard/cm.go | 54 +++++ 15 files changed, 326 insertions(+), 255 deletions(-) create mode 100644 dashboard-ui-v2/src/assets/cm-256.png create mode 100644 dashboard-ui-v2/src/components/config-modal.tsx create mode 100644 dashboard-ui-v2/src/hooks/cm-api.ts create mode 100644 pkg/dashboard/cm.go diff --git a/dashboard-ui-v2/package.json b/dashboard-ui-v2/package.json index 471e06ffec..8be948b976 100644 --- a/dashboard-ui-v2/package.json +++ b/dashboard-ui-v2/package.json @@ -14,6 +14,7 @@ "@ant-design/icons": "^5.3.7", "@ant-design/pro-components": "^2.7.10", "@monaco-editor/react": "^4.6.0", + "@react-hookz/web": "^24.0.4", "@xterm/addon-fit": "^0.10.0", "antd": "^5.18.0", "kubernetes-types": "^1.30.0", @@ -23,7 +24,6 @@ "react-dom": "^18.2.0", "react-intl": "^6.6.8", "react-router-dom": "^6.23.1", - "react-use": "^17.5.0", "react-use-websocket": "3.0.0", "swr": "^2.2.5", "xterm": "^5.3.0", diff --git a/dashboard-ui-v2/pnpm-lock.yaml b/dashboard-ui-v2/pnpm-lock.yaml index fc5f7d51f6..c7d4284276 100644 --- a/dashboard-ui-v2/pnpm-lock.yaml +++ b/dashboard-ui-v2/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@monaco-editor/react': specifier: ^4.6.0 version: 4.6.0(monaco-editor@0.50.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-hookz/web': + specifier: ^24.0.4 + version: 24.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@xterm/addon-fit': specifier: ^0.10.0 version: 0.10.0(@xterm/xterm@5.5.0) @@ -44,9 +47,6 @@ importers: react-router-dom: specifier: ^6.23.1 version: 6.23.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-use: - specifier: ^17.5.0 - version: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-use-websocket: specifier: 3.0.0 version: 3.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -663,6 +663,20 @@ packages: react: '>=16.9.0' react-dom: '>=16.9.0' + '@react-hookz/deep-equal@1.0.4': + resolution: {integrity: sha512-N56fTrAPUDz/R423pag+n6TXWbvlBZDtTehaGFjK0InmN+V2OFWLE/WmORhmn6Ce7dlwH5+tQN1LJFw3ngTJVg==} + + '@react-hookz/web@24.0.4': + resolution: {integrity: sha512-DcIM6JiZklDyHF6CRD1FTXzuggAkQ+3Ncq2Wln7Kdih8GV6ZIeN9JfS6ZaQxpQUxan8/4n0J2V/R7nMeiSrb2Q==} + engines: {node: '>=18.0.0'} + peerDependencies: + js-cookie: ^3.0.5 + react: ^16.8 || ^17 || ^18 + react-dom: ^16.8 || ^17 || ^18 + peerDependenciesMeta: + js-cookie: + optional: true + '@remix-run/router@1.16.1': resolution: {integrity: sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==} engines: {node: '>=14.0.0'} @@ -765,9 +779,6 @@ packages: '@types/hoist-non-react-statics@3.3.5': resolution: {integrity: sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==} - '@types/js-cookie@2.2.7': - resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==} - '@types/lodash@4.17.5': resolution: {integrity: sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==} @@ -858,9 +869,6 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 - '@xobotyi/scrollbar-width@1.9.5': - resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} - '@xterm/addon-fit@0.10.0': resolution: {integrity: sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==} peerDependencies: @@ -981,13 +989,6 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} - css-in-js-utils@3.1.0: - resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} - - css-tree@1.1.3: - resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} - engines: {node: '>=8.0.0'} - csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -1017,9 +1018,6 @@ packages: electron-to-chromium@1.4.798: resolution: {integrity: sha512-by9J2CiM9KPGj9qfp5U4FcPSbXJG7FNzqnYaY4WLzX+v2PHieVGmnsA4dxfpGE3QEC7JofpPZmn7Vn1B9NR2+Q==} - error-stack-parser@2.1.4: - resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} @@ -1094,15 +1092,6 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-loops@1.1.3: - resolution: {integrity: sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==} - - fast-shallow-equal@1.0.0: - resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} - - fastest-stable-stringify@2.0.2: - resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} - fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -1175,9 +1164,6 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - hyphenate-style-name@1.1.0: - resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} - ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} @@ -1197,9 +1183,6 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inline-style-prefixer@7.0.0: - resolution: {integrity: sha512-I7GEdScunP1dQ6IM2mQWh6v0mOYdYmH3Bp31UecKdrcUgcURTcctSe1IECdUznSHKSmsHtjrT3CwCPI1pyxfUQ==} - intl-messageformat@10.5.14: resolution: {integrity: sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==} @@ -1222,9 +1205,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - js-cookie@2.2.1: - resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1284,9 +1264,6 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - mdn-data@2.0.14: - resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1308,12 +1285,6 @@ packages: ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - nano-css@5.6.1: - resolution: {integrity: sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw==} - peerDependencies: - react: '*' - react-dom: '*' - nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1682,24 +1653,12 @@ packages: peerDependencies: react: '>=16.8' - react-universal-interface@0.6.2: - resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==} - peerDependencies: - react: '*' - tslib: '*' - react-use-websocket@3.0.0: resolution: {integrity: sha512-BInlbhXYrODBPKIplDAmI0J1VPM+1KhCLN09o+dzgQ8qMyrYs4t5kEYmCrTqyRuMTmpahylHFZWQXpfYyDkqOw==} peerDependencies: react: '>= 16.8.0' react-dom: '>= 16.8.0' - react-use@17.5.0: - resolution: {integrity: sha512-PbfwSPMwp/hoL847rLnm/qkjg3sTRCvn6YhUZiHaUa3FA6/aNoFX79ul5Xt70O1rK+9GxSVqkY0eTwMdsR/bWg==} - peerDependencies: - react: '*' - react-dom: '*' - react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -1733,9 +1692,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rtl-css-js@1.16.1: - resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} - run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -1746,10 +1702,6 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} - screenfull@5.2.0: - resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} - engines: {node: '>=0.10.0'} - scroll-into-view-if-needed@3.1.0: resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} @@ -1762,10 +1714,6 @@ packages: engines: {node: '>=10'} hasBin: true - set-harmonic-interval@1.0.1: - resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} - engines: {node: '>=6.9'} - shallowequal@1.1.0: resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} @@ -1785,26 +1733,6 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} - source-map@0.5.6: - resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} - engines: {node: '>=0.10.0'} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - stack-generator@2.0.10: - resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} - - stackframe@1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - - stacktrace-gps@3.1.2: - resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} - - stacktrace-js@2.0.2: - resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} - state-local@1.0.7: resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} @@ -1838,10 +1766,6 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - throttle-debounce@3.0.1: - resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} - engines: {node: '>=10'} - throttle-debounce@5.0.0: resolution: {integrity: sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==} engines: {node: '>=12.22'} @@ -1866,9 +1790,6 @@ packages: peerDependencies: typescript: '>=4.2.0' - ts-easing@0.2.0: - resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} - tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} @@ -2658,6 +2579,14 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@react-hookz/deep-equal@1.0.4': {} + + '@react-hookz/web@24.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-hookz/deep-equal': 1.0.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@remix-run/router@1.16.1': {} '@rollup/rollup-android-arm-eabi@4.18.0': @@ -2736,8 +2665,6 @@ snapshots: '@types/react': 18.3.3 hoist-non-react-statics: 3.3.2 - '@types/js-cookie@2.2.7': {} - '@types/lodash@4.17.5': {} '@types/node@20.14.6': @@ -2855,8 +2782,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@xobotyi/scrollbar-width@1.9.5': {} - '@xterm/addon-fit@0.10.0(@xterm/xterm@5.5.0)': dependencies: '@xterm/xterm': 5.5.0 @@ -3022,15 +2947,6 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-in-js-utils@3.1.0: - dependencies: - hyphenate-style-name: 1.1.0 - - css-tree@1.1.3: - dependencies: - mdn-data: 2.0.14 - source-map: 0.6.1 - csstype@3.1.3: {} dayjs@1.11.11: {} @@ -3051,10 +2967,6 @@ snapshots: electron-to-chromium@1.4.798: {} - error-stack-parser@2.1.4: - dependencies: - stackframe: 1.3.4 - esbuild@0.20.2: optionalDependencies: '@esbuild/aix-ppc64': 0.20.2 @@ -3177,12 +3089,6 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-loops@1.1.3: {} - - fast-shallow-equal@1.0.0: {} - - fastest-stable-stringify@2.0.2: {} - fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -3257,8 +3163,6 @@ snapshots: dependencies: react-is: 16.13.1 - hyphenate-style-name@1.1.0: {} - ignore@5.3.1: {} import-fresh@3.3.0: @@ -3275,11 +3179,6 @@ snapshots: inherits@2.0.4: {} - inline-style-prefixer@7.0.0: - dependencies: - css-in-js-utils: 3.1.0 - fast-loops: 1.1.3 - intl-messageformat@10.5.14: dependencies: '@formatjs/ecma402-abstract': 2.0.0 @@ -3299,8 +3198,6 @@ snapshots: isexe@2.0.0: {} - js-cookie@2.2.1: {} - js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -3350,8 +3247,6 @@ snapshots: dependencies: yallist: 3.1.1 - mdn-data@2.0.14: {} - merge2@1.4.1: {} micromatch@4.0.7: @@ -3371,19 +3266,6 @@ snapshots: ms@2.1.2: {} - nano-css@5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - css-tree: 1.1.3 - csstype: 3.1.3 - fastest-stable-stringify: 2.0.2 - inline-style-prefixer: 7.0.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - rtl-css-js: 1.16.1 - stacktrace-js: 2.0.2 - stylis: 4.3.2 - nanoid@3.3.7: {} natural-compare@1.4.0: {} @@ -3835,35 +3717,11 @@ snapshots: '@remix-run/router': 1.16.1 react: 18.3.1 - react-universal-interface@0.6.2(react@18.3.1)(tslib@2.6.3): - dependencies: - react: 18.3.1 - tslib: 2.6.3 - react-use-websocket@3.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-use@17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@types/js-cookie': 2.2.7 - '@xobotyi/scrollbar-width': 1.9.5 - copy-to-clipboard: 3.3.3 - fast-deep-equal: 3.1.3 - fast-shallow-equal: 1.0.0 - js-cookie: 2.2.1 - nano-css: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-universal-interface: 0.6.2(react@18.3.1)(tslib@2.6.3) - resize-observer-polyfill: 1.5.1 - screenfull: 5.2.0 - set-harmonic-interval: 1.0.1 - throttle-debounce: 3.0.1 - ts-easing: 0.2.0 - tslib: 2.6.3 - react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -3907,10 +3765,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.18.0 fsevents: 2.3.3 - rtl-css-js@1.16.1: - dependencies: - '@babel/runtime': 7.24.7 - run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -3921,8 +3775,6 @@ snapshots: dependencies: loose-envify: 1.4.0 - screenfull@5.2.0: {} - scroll-into-view-if-needed@3.1.0: dependencies: compute-scroll-into-view: 3.1.0 @@ -3931,8 +3783,6 @@ snapshots: semver@7.6.2: {} - set-harmonic-interval@1.0.1: {} - shallowequal@1.1.0: {} shebang-command@2.0.0: @@ -3945,27 +3795,6 @@ snapshots: source-map-js@1.2.0: {} - source-map@0.5.6: {} - - source-map@0.6.1: {} - - stack-generator@2.0.10: - dependencies: - stackframe: 1.3.4 - - stackframe@1.3.4: {} - - stacktrace-gps@3.1.2: - dependencies: - source-map: 0.5.6 - stackframe: 1.3.4 - - stacktrace-js@2.0.2: - dependencies: - error-stack-parser: 2.1.4 - stack-generator: 2.0.10 - stacktrace-gps: 3.1.2 - state-local@1.0.7: {} string-convert@0.2.1: {} @@ -3994,8 +3823,6 @@ snapshots: text-table@0.2.0: {} - throttle-debounce@3.0.1: {} - throttle-debounce@5.0.0: {} tinycolor2@1.6.0: {} @@ -4012,8 +3839,6 @@ snapshots: dependencies: typescript: 5.4.5 - ts-easing@0.2.0: {} - tslib@2.6.3: {} type-check@0.4.0: diff --git a/dashboard-ui-v2/src/assets/cm-256.png b/dashboard-ui-v2/src/assets/cm-256.png new file mode 100644 index 0000000000000000000000000000000000000000..23f43174944dca18f78f5f478588ed42f985ff22 GIT binary patch literal 9925 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 z void }) => ReactNode +}> = memo(({ children }) => { + const [isModalOpen, setIsModalOpen] = useState(false) + + const showModal = () => { + setIsModalOpen(true) + } + const handleOk = () => { + setIsModalOpen(false) + } + const handleCancel = () => { + setIsModalOpen(false) + } + + const [updated, setUpdated] = useState(false) + + const { data, isLoading, mutate } = useConfig() + const [state, actions] = useUpdateConfig() + const [config, setConfig] = useState('') + useEffect(() => { + if (data?.data) { + setConfig( + YAML.stringify(data?.data?.['config.yaml']) + .split('\n') + .slice(1) + .join('\n'), + ) + } + }, [data]) + + return ( + <> + {children({ onClick: showModal })} + {isModalOpen ? ( + ( + + + + + + )} + onOk={handleOk} + onCancel={handleCancel} + > + { + if (v) { + setConfig(v) + setUpdated(true) + } + }} + /> + + ) : null} + + ) +}) + +export default ConfigModal diff --git a/dashboard-ui-v2/src/components/index.ts b/dashboard-ui-v2/src/components/index.ts index 10b602f3df..c3cb83f2de 100644 --- a/dashboard-ui-v2/src/components/index.ts +++ b/dashboard-ui-v2/src/components/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import ConfigModal from '@/components/config-modal' import Containers from '@/components/containers' import EventTable from '@/components/event-table' import Layout from '@/components/layout' @@ -26,14 +27,15 @@ import ResourceList from '@/components/resource-list' import YamlModal from '@/components/yaml-modal' export { - Layout, - ResourceDetail, - ResourceList, - PodBasic, + ConfigModal, Containers, - YamlModal, EventTable, + Layout, + PodBasic, PodsTable, PVBasic, PVCBasic, + ResourceDetail, + ResourceList, + YamlModal, } diff --git a/dashboard-ui-v2/src/components/layout.tsx b/dashboard-ui-v2/src/components/layout.tsx index 00aac04091..4d9ba3f323 100644 --- a/dashboard-ui-v2/src/components/layout.tsx +++ b/dashboard-ui-v2/src/components/layout.tsx @@ -22,12 +22,14 @@ import { ConfigProvider, Menu, MenuProps, + Space, } from 'antd' import enUS from 'antd/locale/en_US' import zhCN from 'antd/locale/zh_CN' import { FormattedMessage, IntlProvider } from 'react-intl' import { Link, useLocation } from 'react-router-dom' +import ConfigModal from './config-modal' import { DSIcon, PODIcon, PVCIcon, PVIcon, SCIcon } from '@/icons' import en from '@/locales/en-US' import cn from '@/locales/zh-CN' @@ -86,44 +88,50 @@ export default function Layout(props: { children: ReactNode }) { }, []) return ( - -
-

JuiceFS CSI

-
- { - // open a new tab to the JuiceFS CSI documentation - window.open( - 'https://juicefs.com/docs/csi/introduction/', - '_blank', - ) - }} - /> - -
-
- + + +
+

JuiceFS CSI

+ + { + // open a new tab to the JuiceFS CSI documentation + window.open( + 'https://juicefs.com/docs/csi/introduction/', + '_blank', + ) + }} + /> + + + {({ onClick }) => ( + + )} + + + +
-
-
+ + ) } diff --git a/dashboard-ui-v2/src/components/log-modal.tsx b/dashboard-ui-v2/src/components/log-modal.tsx index 307d19f5f6..4eb7989d74 100644 --- a/dashboard-ui-v2/src/components/log-modal.tsx +++ b/dashboard-ui-v2/src/components/log-modal.tsx @@ -36,7 +36,7 @@ const LogModal: React.FC<{ const [editor, setEditor] = useState( null, ) - const [state, doFetch] = useDownloadPodLogs(namespace, name, container) + const [state, actions] = useDownloadPodLogs(namespace, name, container) useWebsocket( `/api/v1/ws/pod/${namespace}/${name}/${container}/${type}`, @@ -55,6 +55,10 @@ const LogModal: React.FC<{ if (!editor) return const model = editor.getModel() if (!model) return + const visibleLine = editor.getVisibleRanges()[0] + if (visibleLine.endLineNumber + 5 < model.getLineCount()) { + return + } editor.revealLine(model.getLineCount()) }, [data, editor]) @@ -87,9 +91,9 @@ const LogModal: React.FC<{ {type === 'logs' ? ( <>