From 392443c1469257ca8caebfe1263403565595227b Mon Sep 17 00:00:00 2001 From: wishrem Date: Tue, 28 Nov 2023 23:54:02 +0800 Subject: [PATCH] feat: Replaced mobx with zustand --- frontend/plugins/kubepanel/package.json | 6 +- frontend/plugins/kubepanel/pnpm-lock.yaml | 146 +++--------------- .../src/components/kube/local-date.tsx | 5 +- .../detail/kube-object-detail-info-list.tsx | 2 +- .../kube/object/kube-object-age.tsx | 10 +- .../src/components/kube/reactive-duration.tsx | 90 +++-------- .../config/config-map/config-map.tsx | 56 ++----- .../storage/volume-claim/volume-claim.tsx | 93 +++++------ .../workload/deployment/deployment.tsx | 72 +++------ .../workload/overview/overview.tsx | 32 ++-- .../kube-object/workload/pod/pod.tsx | 124 ++++++--------- .../workload/statefulset/statefulset.tsx | 78 ++++------ .../pages/kubepanel/components/overview.tsx | 53 ------- .../kubepanel/src/pages/kubepanel/index.tsx | 96 ++++++------ .../src/store/k8s/config-map.store.ts | 8 + .../src/store/k8s/configmap.store.ts | 9 -- .../kubepanel/src/store/k8s/data.store.ts | 52 ------- .../src/store/k8s/deployment.store.ts | 52 +++---- .../kubepanel/src/store/k8s/kube.store.ts | 79 +++++++--- .../kubepanel/src/store/k8s/pod.store.ts | 38 +++-- .../kubepanel/src/store/k8s/pvc.store.ts | 9 -- .../src/store/k8s/stateful-set.store.ts | 29 ++++ .../src/store/k8s/statefulset.store.ts | 28 ---- .../src/store/k8s/volume-claim.store.ts | 8 + frontend/plugins/kubepanel/src/store/kube.ts | 6 + .../plugins/kubepanel/src/store/static.ts | 11 -- .../plugins/kubepanel/src/types/state.d.ts | 33 ++++ 27 files changed, 462 insertions(+), 763 deletions(-) delete mode 100644 frontend/plugins/kubepanel/src/pages/kubepanel/components/overview.tsx create mode 100644 frontend/plugins/kubepanel/src/store/k8s/config-map.store.ts delete mode 100644 frontend/plugins/kubepanel/src/store/k8s/configmap.store.ts delete mode 100644 frontend/plugins/kubepanel/src/store/k8s/data.store.ts delete mode 100644 frontend/plugins/kubepanel/src/store/k8s/pvc.store.ts create mode 100644 frontend/plugins/kubepanel/src/store/k8s/stateful-set.store.ts delete mode 100644 frontend/plugins/kubepanel/src/store/k8s/statefulset.store.ts create mode 100644 frontend/plugins/kubepanel/src/store/k8s/volume-claim.store.ts create mode 100644 frontend/plugins/kubepanel/src/store/kube.ts create mode 100644 frontend/plugins/kubepanel/src/types/state.d.ts diff --git a/frontend/plugins/kubepanel/package.json b/frontend/plugins/kubepanel/package.json index 6c753af4c97..96a015f1fd9 100644 --- a/frontend/plugins/kubepanel/package.json +++ b/frontend/plugins/kubepanel/package.json @@ -30,9 +30,6 @@ "immer": "^10.0.3", "js-yaml": "^4.1.0", "lodash": "^4.17.21", - "mobx": "^6.10.2", - "mobx-react": "^9.0.1", - "mobx-utils": "^6.0.8", "moment": "^2.29.4", "moment-timezone": "^0.5.43", "next": "13.5.4", @@ -46,7 +43,8 @@ "type-fest": "^4.4.0", "typed-regex": "^0.0.8", "uuid": "^9.0.1", - "zustand": "^4.3.9" + "zustand": "^4.3.9", + "zustand-computed": "^1.3.7" }, "devDependencies": { "@types/js-yaml": "^4.0.8", diff --git a/frontend/plugins/kubepanel/pnpm-lock.yaml b/frontend/plugins/kubepanel/pnpm-lock.yaml index 81e1e90f8bb..9e38c65031f 100644 --- a/frontend/plugins/kubepanel/pnpm-lock.yaml +++ b/frontend/plugins/kubepanel/pnpm-lock.yaml @@ -65,15 +65,6 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 - mobx: - specifier: ^6.10.2 - version: 6.10.2 - mobx-react: - specifier: ^9.0.1 - version: 9.0.1(mobx@6.10.2)(react-dom@18.2.0)(react@18.2.0) - mobx-utils: - specifier: ^6.0.8 - version: 6.0.8(mobx@6.10.2) moment: specifier: ^2.29.4 version: 2.29.4 @@ -116,6 +107,9 @@ importers: zustand: specifier: ^4.3.9 version: 4.3.9(immer@10.0.3)(react@18.2.0) + zustand-computed: + specifier: ^1.3.7 + version: 1.3.7(react@18.2.0)(zustand@4.3.9) devDependencies: '@types/js-yaml': specifier: ^4.0.8 @@ -168,9 +162,6 @@ importers: ../../packages/client-sdk: dependencies: - '@kubernetes/client-node': - specifier: ^0.18.1 - version: 0.18.1 axios: specifier: ^1.5.1 version: 1.5.1 @@ -187,6 +178,9 @@ importers: specifier: ^9.0.1 version: 9.0.1 devDependencies: + '@kubernetes/client-node': + specifier: ^0.18.1 + version: 0.18.1 '@rollup/plugin-typescript': specifier: ^11.1.4 version: 11.1.5(rollup@2.79.1)(tslib@2.6.2)(typescript@4.9.5) @@ -2504,7 +2498,7 @@ packages: transitivePeerDependencies: - bufferutil - utf-8-validate - dev: false + dev: true /@kubernetes/client-node@0.19.0: resolution: {integrity: sha512-WTOjGuFQ8yeW3+qD6JrAYhpwpoQbe9R8cA/61WCyFrNawSTUgLstHu7EsZRYEs39er3jDn3wCEaczz+VOFlc2Q==} @@ -3038,7 +3032,7 @@ packages: resolution: {integrity: sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==} dependencies: undici-types: 5.26.5 - dev: false + dev: true /@types/node@20.0.0: resolution: {integrity: sha512-cD2uPTDnQQCVpmRefonO98/PPijuOnnEy5oytWJFPY1N9aJCz2wJ5kSGWO+zJoed2cY2JxQh6yBuUq4vIn61hw==} @@ -3095,7 +3089,6 @@ packages: resolution: {integrity: sha512-6UrLjiDUvn40CMrAubXuIVtj2PEfKDffJS7ychvnPU44j+KVeXmdHHTgqcM/dxLUTHxlXHiFM8Skmb8ozGdTnQ==} dependencies: '@types/node': 20.8.7 - dev: false /@typescript-eslint/parser@6.8.0(eslint@8.51.0)(typescript@5.0.2): resolution: {integrity: sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==} @@ -3441,12 +3434,10 @@ packages: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} dependencies: safer-buffer: 2.1.2 - dev: false /assert-plus@1.0.0: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} - dev: false /ast-types-flow@0.0.7: resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} @@ -3496,11 +3487,9 @@ packages: /aws-sign2@0.7.0: resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - dev: false /aws4@1.12.0: resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==} - dev: false /axe-core@4.8.2: resolution: {integrity: sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==} @@ -3539,7 +3528,6 @@ packages: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} dependencies: tweetnacl: 0.14.5 - dev: false /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} @@ -3580,7 +3568,6 @@ packages: /byline@5.0.0: resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} engines: {node: '>=0.10.0'} - dev: false /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} @@ -3607,7 +3594,6 @@ packages: /caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - dev: false /center-align@0.1.3: resolution: {integrity: sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==} @@ -3662,7 +3648,6 @@ packages: /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} - dev: false /clamp@1.0.1: resolution: {integrity: sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA==} @@ -3767,7 +3752,6 @@ packages: /core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - dev: false /cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} @@ -3947,7 +3931,6 @@ packages: engines: {node: '>=0.10'} dependencies: assert-plus: 1.0.0 - dev: false /dayjs@1.11.10: resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} @@ -4086,7 +4069,6 @@ packages: dependencies: jsbn: 0.1.1 safer-buffer: 2.1.2 - dev: false /electron-to-chromium@1.4.566: resolution: {integrity: sha512-mv+fAy27uOmTVlUULy15U3DVJ+jg+8iyKH1bpwboCRhtDC69GKf1PPTZvEIhCyDr81RFqfxZJYrbgp933a1vtg==} @@ -4509,7 +4491,6 @@ packages: /extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - dev: false /extrude-polyline@1.0.6: resolution: {integrity: sha512-fcKIanU/v+tcdgG0+xMbS0C2VZ0/CF3qqxSjHiWfWICh0yFBezPr3SsOhgdzwE5E82plG6p1orEsfSqgldpxVg==} @@ -4522,7 +4503,6 @@ packages: /extsprintf@1.3.0: resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} engines: {'0': node >=0.6.0} - dev: false /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -4645,7 +4625,6 @@ packages: /forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - dev: false /form-data@2.3.3: resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} @@ -4654,7 +4633,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: false /form-data@2.5.1: resolution: {integrity: sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==} @@ -4715,7 +4693,6 @@ packages: engines: {node: '>= 8'} dependencies: minipass: 3.3.6 - dev: false /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -4786,7 +4763,6 @@ packages: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} dependencies: assert-plus: 1.0.0 - dev: false /gl-matrix@3.4.3: resolution: {integrity: sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==} @@ -4922,7 +4898,6 @@ packages: /har-schema@2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} engines: {node: '>=4'} - dev: false /har-validator@5.1.5: resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} @@ -4931,7 +4906,6 @@ packages: dependencies: ajv: 6.12.6 har-schema: 2.0.0 - dev: false /has-ansi@2.0.0: resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} @@ -4946,6 +4920,7 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} + requiresBuild: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -4988,7 +4963,6 @@ packages: assert-plus: 1.0.0 jsprim: 1.4.2 sshpk: 1.17.0 - dev: false /hyphenate-style-name@1.0.4: resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} @@ -5239,7 +5213,6 @@ packages: /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - dev: false /is-weakmap@2.0.1: resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} @@ -5270,11 +5243,9 @@ packages: ws: '*' dependencies: ws: 8.14.2 - dev: false /isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - dev: false /iterator.prototype@1.1.2: resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} @@ -5294,7 +5265,6 @@ packages: /jose@4.15.4: resolution: {integrity: sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==} requiresBuild: true - dev: false optional: true /jquery-mousewheel@3.1.13: @@ -5325,7 +5295,6 @@ packages: /jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - dev: false /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -5340,7 +5309,6 @@ packages: /json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - dev: false /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -5348,7 +5316,6 @@ packages: /json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - dev: false /json2module@0.0.3: resolution: {integrity: sha512-qYGxqrRrt4GbB8IEOy1jJGypkNsjWoIMlZt4bAsmUScCA507Hbc2p1JOhBzqn45u3PWafUgH2OnzyNU7udO/GA==} @@ -5379,7 +5346,6 @@ packages: /jsonpath-plus@7.2.0: resolution: {integrity: sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==} engines: {node: '>=12.0.0'} - dev: false /jsprim@1.4.2: resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} @@ -5389,7 +5355,6 @@ packages: extsprintf: 1.3.0 json-schema: 0.4.0 verror: 1.10.0 - dev: false /jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} @@ -5602,12 +5567,10 @@ packages: engines: {node: '>=8'} dependencies: yallist: 4.0.0 - dev: false /minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - dev: false /minizlib@2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} @@ -5615,13 +5578,11 @@ packages: dependencies: minipass: 3.3.6 yallist: 4.0.0 - dev: false /mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true - dev: false /ml-array-max@1.2.4: resolution: {integrity: sha512-BlEeg80jI0tW6WaPyGxf5Sa4sqvcyY6lbSn5Vcv44lp1I2GR6AWojfUvLnGTNsIXrZ8uqWmo8VcG1WpkI2ONMQ==} @@ -5650,56 +5611,6 @@ packages: ml-array-rescale: 1.3.7 dev: false - /mobx-react-lite@4.0.5(mobx@6.10.2)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-StfB2wxE8imKj1f6T8WWPf4lVMx3cYH9Iy60bbKXEs21+HQ4tvvfIBZfSmMXgQAefi8xYEwQIz4GN9s0d2h7dg==} - peerDependencies: - mobx: ^6.9.0 - react: ^16.8.0 || ^17 || ^18 - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - dependencies: - mobx: 6.10.2 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - use-sync-external-store: 1.2.0(react@18.2.0) - dev: false - - /mobx-react@9.0.1(mobx@6.10.2)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-0idiElBgNMJg20YqGgHvYEnlqIJpPDQaOkxj2dHJIZeqCvUh+zBkBkMkpUFw/uEd1OdPUvT0y+AFBqsWIUAXww==} - peerDependencies: - mobx: ^6.9.0 - react: ^16.8.0 || ^17 || ^18 - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - dependencies: - mobx: 6.10.2 - mobx-react-lite: 4.0.5(mobx@6.10.2)(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /mobx-utils@6.0.8(mobx@6.10.2): - resolution: {integrity: sha512-fPNt0vJnHwbQx9MojJFEnJLfM3EMGTtpy4/qOOW6xueh1mPofMajrbYAUvByMYAvCJnpy1A5L0t+ZVB5niKO4g==} - peerDependencies: - mobx: ^6.0.0 - dependencies: - mobx: 6.10.2 - dev: false - - /mobx@6.10.2: - resolution: {integrity: sha512-B1UGC3ieK3boCjnMEcZSwxqRDMdzX65H/8zOHbuTY8ZhvrIjTUoLRR2TP2bPqIgYRfb3+dUigu8yMZufNjn0LQ==} - dev: false - /mock-property@1.0.2: resolution: {integrity: sha512-GHVKHd3bFiXtvZtp23+8+EQLMeDJWcEVrSA2pOBs1KB5Uh2ww8Q+9fYDljS67k3GzU4DIDBa6+qRIgfZ2Bp+gQ==} engines: {node: '>= 0.4'} @@ -5840,7 +5751,6 @@ packages: /oauth-sign@0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} - dev: false /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -5850,7 +5760,6 @@ packages: resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} engines: {node: '>= 6'} requiresBuild: true - dev: false optional: true /object-hash@3.0.0: @@ -5933,7 +5842,6 @@ packages: resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} engines: {node: ^10.13.0 || >=12.0.0} requiresBuild: true - dev: false optional: true /once@1.4.0: @@ -5954,7 +5862,6 @@ packages: lru-cache: 6.0.0 object-hash: 2.2.0 oidc-token-hash: 5.0.3 - dev: false optional: true /optionator@0.9.3: @@ -6034,7 +5941,6 @@ packages: /performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - dev: false /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} @@ -6182,7 +6088,6 @@ packages: /psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - dev: false /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} @@ -6199,7 +6104,6 @@ packages: /qs@6.5.3: resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} engines: {node: '>=0.6'} - dev: false /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -7066,7 +6970,6 @@ packages: tough-cookie: 2.5.0 tunnel-agent: 0.6.0 uuid: 3.4.0 - dev: false /requireindex@1.1.0: resolution: {integrity: sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==} @@ -7115,7 +7018,6 @@ packages: /rfc4648@1.5.2: resolution: {integrity: sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg==} - dev: false /rfc6902@5.0.1: resolution: {integrity: sha512-tYGfLpKIq9X7lrt4o3IkD9w9bpeAtsejfAqWNR98AoxfTsZqCepKa8eDlRiX8QMiCOD9vMx0/YbKLx0G1nPi5w==} @@ -7133,6 +7035,7 @@ packages: hasBin: true dependencies: glob: 7.2.3 + dev: true /rollup-plugin-copy@3.5.0: resolution: {integrity: sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==} @@ -7210,7 +7113,6 @@ packages: /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: false /safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} @@ -7221,7 +7123,6 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: false /scheduler@0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} @@ -7367,7 +7268,6 @@ packages: jsbn: 0.1.1 safer-buffer: 2.1.2 tweetnacl: 0.14.5 - dev: false /stack-generator@2.0.10: resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} @@ -7401,7 +7301,6 @@ packages: /stream-buffers@3.0.2: resolution: {integrity: sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==} engines: {node: '>= 0.10.0'} - dev: false /streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} @@ -7603,7 +7502,6 @@ packages: minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 - dev: false /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -7648,14 +7546,14 @@ packages: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} dependencies: tmp: 0.2.1 - dev: false + dev: true /tmp@0.2.1: resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} engines: {node: '>=8.17.0'} dependencies: rimraf: 3.0.2 - dev: false + dev: true /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} @@ -7695,7 +7593,6 @@ packages: dependencies: psl: 1.9.0 punycode: 2.3.0 - dev: false /ts-api-utils@1.0.3(typescript@5.0.2): resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} @@ -7738,11 +7635,9 @@ packages: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: safe-buffer: 5.2.1 - dev: false /tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - dev: false /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} @@ -7842,14 +7737,14 @@ packages: /underscore@1.13.6: resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} - dev: false + dev: true /undici-types@5.25.3: resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: false + dev: true /universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} @@ -7924,7 +7819,6 @@ packages: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. hasBin: true - dev: false /uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} @@ -7938,7 +7832,6 @@ packages: assert-plus: 1.0.0 core-util-is: 1.0.2 extsprintf: 1.3.0 - dev: false /viewport-mercator-project@6.2.3: resolution: {integrity: sha512-QQb0/qCLlP4DdfbHHSWVYXpghB2wkLIiiZQnoelOB59mXKQSyZVxjreq1S+gaBJFpcGkWEcyVtre0+2y2DTl/Q==} @@ -8085,7 +7978,6 @@ packages: optional: true utf-8-validate: optional: true - dev: false /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -8114,6 +8006,16 @@ packages: engines: {node: '>=10'} dev: true + /zustand-computed@1.3.7(react@18.2.0)(zustand@4.3.9): + resolution: {integrity: sha512-7Upkc4eVdcLaBfdcMMg06kSBarbkaqM9xdcWCpE4N7edBwbTTyd6IsYcXs8IRvHuRk8JBZrjbDePwQxpti0yKA==} + peerDependencies: + react: ^18.2.0 + zustand: ^4.3.8 + dependencies: + react: 18.2.0 + zustand: 4.3.9(immer@10.0.3)(react@18.2.0) + dev: false + /zustand@4.3.9(immer@10.0.3)(react@18.2.0): resolution: {integrity: sha512-Tat5r8jOMG1Vcsj8uldMyqYKC5IZvQif8zetmLHs9WoZlntTHmIoNM8TpLRY31ExncuUvUOXehd0kvahkuHjDw==} engines: {node: '>=12.7.0'} diff --git a/frontend/plugins/kubepanel/src/components/kube/local-date.tsx b/frontend/plugins/kubepanel/src/components/kube/local-date.tsx index c483d0ec066..dfc5514a67b 100644 --- a/frontend/plugins/kubepanel/src/components/kube/local-date.tsx +++ b/frontend/plugins/kubepanel/src/components/kube/local-date.tsx @@ -1,4 +1,3 @@ -import { observer } from 'mobx-react'; import moment from 'moment-timezone'; export type LocaleDateProps = { @@ -6,6 +5,6 @@ export type LocaleDateProps = { localeTimezone: string; }; -export const LocaleDate = observer(({ date, localeTimezone }: LocaleDateProps) => ( +export const LocaleDate = ({ date, localeTimezone }: LocaleDateProps) => ( <>{`${moment.tz(date, localeTimezone).format()}`} -)); +); diff --git a/frontend/plugins/kubepanel/src/components/kube/object/detail/kube-object-detail-info-list.tsx b/frontend/plugins/kubepanel/src/components/kube/object/detail/kube-object-detail-info-list.tsx index 4e9d5578095..339cd5ae100 100644 --- a/frontend/plugins/kubepanel/src/components/kube/object/detail/kube-object-detail-info-list.tsx +++ b/frontend/plugins/kubepanel/src/components/kube/object/detail/kube-object-detail-info-list.tsx @@ -34,7 +34,7 @@ export const KubeObjectInfoList = ({ hiddenFields = ['uid', 'resourceVersion'], name="Created" value={ <> - + {' ago '} {creationTimestamp && ( diff --git a/frontend/plugins/kubepanel/src/components/kube/object/kube-object-age.tsx b/frontend/plugins/kubepanel/src/components/kube/object/kube-object-age.tsx index 38b9fb960d0..d263f4c3195 100644 --- a/frontend/plugins/kubepanel/src/components/kube/object/kube-object-age.tsx +++ b/frontend/plugins/kubepanel/src/components/kube/object/kube-object-age.tsx @@ -7,7 +7,11 @@ import React from 'react'; import { ReactiveDuration } from '../reactive-duration'; export interface KubeObjectAgeProps { - creationTimestamp?: string; + obj: { + metadata: { + creationTimestamp?: string; + }; + }; /** * Whether the display string should prefer length over precision @@ -16,6 +20,6 @@ export interface KubeObjectAgeProps { compact?: boolean; } -export const KubeObjectAge = ({ creationTimestamp, compact = true }: KubeObjectAgeProps) => ( - +export const KubeObjectAge = ({ obj, compact = true }: KubeObjectAgeProps) => ( + ); diff --git a/frontend/plugins/kubepanel/src/components/kube/reactive-duration.tsx b/frontend/plugins/kubepanel/src/components/kube/reactive-duration.tsx index 55dd320cfc2..709ba75dc8b 100644 --- a/frontend/plugins/kubepanel/src/components/kube/reactive-duration.tsx +++ b/frontend/plugins/kubepanel/src/components/kube/reactive-duration.tsx @@ -3,12 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { observer } from 'mobx-react'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { formatDuration } from '@/k8slens/utilities'; -import type { IResource } from 'mobx-utils'; -import { fromResource } from 'mobx-utils'; -import { _isComputingDerivation } from 'mobx'; export interface ReactiveDurationProps { timestamp: string | undefined; @@ -45,77 +41,27 @@ function computeUpdateInterval(creationTimestampEpoch: number, compact: boolean) return everyMinute; } -export const ReactiveDuration = observer(({ timestamp, compact = true }: ReactiveDurationProps) => { +export const ReactiveDuration = ({ timestamp, compact = true }: ReactiveDurationProps) => { if (!timestamp) { - return <>{''}; + return <><unknown>; } + const [duration, setDuration] = useState(0); + const [ms, setMs] = useState(1000); const timestampSeconds = new Date(timestamp).getTime(); - return ( - <> - {formatDuration( - reactiveNow(computeUpdateInterval(timestampSeconds, compact)) - timestampSeconds, - compact - )} - - ); -}); - -const tickers: Record> = {}; -function reactiveNow(interval?: number | 'frame') { - if (interval === void 0) { - interval = 1000; - } - - if (!_isComputingDerivation()) { - // See #40 - return Date.now(); - } - - // Note: This is the kludge until https://github.com/mobxjs/mobx-utils/issues/306 is fixed - const synchronizationIsEnabled = !process.env.JEST_WORKER_ID; - - if (!tickers[interval] || !synchronizationIsEnabled) { - if (typeof interval === 'number') tickers[interval] = createIntervalTicker(interval); - else tickers[interval] = createAnimationFrameTicker(); - } - - return tickers[interval].current(); -} - -function createIntervalTicker(interval: number) { - let subscriptionHandle: NodeJS.Timeout; - return fromResource( - function (sink) { - sink(Date.now()); - subscriptionHandle = setInterval(function () { - return sink(Date.now()); - }, interval); - }, - function () { - clearInterval(subscriptionHandle); - }, - Date.now() - ); -} - -function createAnimationFrameTicker() { - const frameBasedTicker = fromResource( - function (sink) { - sink(Date.now()); - - function scheduleTick() { - window.requestAnimationFrame(function () { - sink(Date.now()); - if (frameBasedTicker.isAlive()) scheduleTick(); - }); + useEffect(() => { + const interval = setInterval(() => { + const nextMs = computeUpdateInterval(timestampSeconds, compact); + if (ms !== nextMs) { + setMs(computeUpdateInterval(timestampSeconds, compact)); + clearInterval(interval); } - scheduleTick(); - }, - function () {}, - Date.now() - ); + setDuration(Date.now() - timestampSeconds); + }, ms); - return frameBasedTicker; -} + return () => clearInterval(interval); + }, [compact, ms, timestampSeconds]); + + return <>{formatDuration(duration, compact)}; +}; diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/config/config-map/config-map.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/config/config-map/config-map.tsx index 4aff2221c3d..a27aedf5cc2 100644 --- a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/config/config-map/config-map.tsx +++ b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/config/config-map/config-map.tsx @@ -1,9 +1,7 @@ import { KubeObjectAge } from '@/components/kube/object/kube-object-age'; import { ConfigMap } from '@/k8slens/kube-object'; import { ColumnsType } from 'antd/es/table'; -import { observer } from 'mobx-react'; import { useState } from 'react'; -import { CONFIG_MAP_STORE } from '@/store/static'; import { useQuery } from '@tanstack/react-query'; import ConfigMapDetail from './config-map-detail'; import Table from '../../../table/table'; @@ -11,75 +9,55 @@ import ActionButton from '../../../action-button/action-button'; import { deleteResource } from '@/api/delete'; import { Resources } from '@/constants/kube-object'; import { updateResource } from '@/api/update'; -interface DataType { - key: string; - name: string; - keys: string[]; - creationTimestamp?: string; -} +import { fetchData, useConfigMapStore } from '@/store/kube'; -const getData = (configMap: ConfigMap): DataType => { - return { - key: configMap.getName(), - name: configMap.getName(), - keys: configMap.getKeys(), - creationTimestamp: configMap.metadata.creationTimestamp - }; -}; - -const columns: ColumnsType = [ +const columns: ColumnsType = [ { title: 'Name', - dataIndex: 'name', - key: 'name' + key: 'name', + fixed: 'left', + render: (_, configMap) => configMap.getName() }, { title: 'Keys', - dataIndex: 'keys', key: 'keys', - render: (keys: string[]) => keys.join(', ') + render: (_, configMap) => configMap.getKeys().join(', ') }, { title: 'Age', - dataIndex: 'creationTimestamp', key: 'age', - render: (creationTimestamp: string) => + render: (_, configMap) => }, { - dataIndex: 'name', key: 'action', fixed: 'right', - render: (name: string) => ( + render: (_, configMap) => ( configMap.getName() === name)[0]} - onUpdate={(data: string) => updateResource(data, name, Resources.ConfigMaps)} - onDelete={() => deleteResource(name, Resources.ConfigMaps)} + obj={configMap} + onUpdate={(data: string) => updateResource(data, configMap.getName(), Resources.ConfigMaps)} + onDelete={() => deleteResource(configMap.getName(), Resources.ConfigMaps)} /> ) } ]; const ConfigMapOverviewPage = () => { - const [openDrawer, setOpenDrawer] = useState(false); + const { items, replace } = useConfigMapStore(); const [configMap, setConfigMap] = useState(); + const [openDrawer, setOpenDrawer] = useState(false); - useQuery(['configMaps'], () => CONFIG_MAP_STORE.fetchData(), { + useQuery(['configMaps'], () => fetchData(replace, Resources.ConfigMaps), { refetchInterval: 5000 }); - const dataSource = CONFIG_MAP_STORE.items.map(getData); return ( <> ({ + dataSource={items} + onRow={(configMap) => ({ onClick: () => { - const { key } = record; - const configMap = CONFIG_MAP_STORE.items.filter( - (configMap) => configMap.getName() === key - )[0]; setConfigMap(configMap); setOpenDrawer(true); } @@ -94,4 +72,4 @@ const ConfigMapOverviewPage = () => { ); }; -export default observer(ConfigMapOverviewPage); +export default ConfigMapOverviewPage; diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/storage/volume-claim/volume-claim.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/storage/volume-claim/volume-claim.tsx index 9f115b0e19c..2f70f98b426 100644 --- a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/storage/volume-claim/volume-claim.tsx +++ b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/storage/volume-claim/volume-claim.tsx @@ -1,10 +1,8 @@ import { KubeObjectAge } from '@/components/kube/object/kube-object-age'; import { PersistentVolumeClaim, Pod } from '@/k8slens/kube-object'; -import { PERSISTENT_VOLUME_CLAIM_STORE, POD_STORE } from '@/store/static'; import { RequestController } from '@/utils/request-controller'; import { useQuery } from '@tanstack/react-query'; import { ColumnsType } from 'antd/es/table'; -import { observer } from 'mobx-react'; import { useRef, useState } from 'react'; import PersistentVolumeClaimDetail from './volume-claim-detail'; import Table from '../../../table/table'; @@ -12,82 +10,60 @@ import ActionButton from '../../../action-button/action-button'; import { deleteResource } from '@/api/delete'; import { Resources } from '@/constants/kube-object'; import { updateResource } from '@/api/update'; +import { fetchData, usePodStore, useVolumeClaimStore } from '@/store/kube'; -interface DataType { - key: string; - name: string; - storageClass?: string; - storage: string; - podsNames: string[]; - creationTimestamp?: string; - status: string; -} - -const getData = (volumeClaim: PersistentVolumeClaim, pods: Pod[]) => { - return { - key: volumeClaim.getName(), - name: volumeClaim.getName(), - storageClass: volumeClaim.spec.storageClassName, - storage: volumeClaim.getStorage(), - podsNames: volumeClaim.getPods(pods).map((pod) => pod.getName()), - creationTimestamp: volumeClaim.metadata.creationTimestamp, - status: volumeClaim.getStatus() - }; -}; - -const columns: ColumnsType = [ +const columns: ColumnsType<{ volumeClaim: PersistentVolumeClaim; pods: Pod[] }> = [ { title: 'Name', - dataIndex: 'name', - key: 'name' + key: 'name', + fixed: 'left', + render: (_, { volumeClaim }) => volumeClaim.getName() }, { title: 'Storage Class', - dataIndex: 'storageClass', - key: 'storageClass' + key: 'storageClass', + render: (_, { volumeClaim }) => volumeClaim.spec.storageClassName }, { title: 'Size', - dataIndex: 'storage', - key: 'size' + key: 'size', + render: (_, { volumeClaim }) => volumeClaim.getStorage() }, { title: 'Pods', - dataIndex: 'podsNames', key: 'pods', ellipsis: true, - render: (podsNames: string[]) => - podsNames.map((name) => ( + render: (_, { volumeClaim, pods }) => { + const podsNames = volumeClaim.getPods(pods).map((pod) => pod.getName()); + return podsNames.map((name) => ( {name} - )) + )); + } }, { title: 'Age', - dataIndex: 'creationTimestamp', key: 'age', - render: (creationTimestamp: string) => + render: (_, { volumeClaim }) => }, { title: 'Status', - dataIndex: 'status', fixed: 'right', - key: 'status' + key: 'status', + render: (_, { volumeClaim }) => volumeClaim.getStatus() }, { dataIndex: 'name', key: 'action', fixed: 'right', - render: (name: string) => ( + render: (_, { volumeClaim }) => ( volumeClaim.getName() === name - )[0] + obj={volumeClaim} + onUpdate={(data: string) => + updateResource(data, volumeClaim.getName(), Resources.PersistentVolumeClaims) } - onUpdate={(data: string) => updateResource(data, name, Resources.PersistentVolumeClaims)} - onDelete={() => deleteResource(name, Resources.PersistentVolumeClaims)} + onDelete={() => deleteResource(volumeClaim.getName(), Resources.PersistentVolumeClaims)} /> ) } @@ -97,11 +73,16 @@ const PersistentVolumeClaimOverviewPage = () => { const [openDrawer, setOpenDrawer] = useState(false); const [volumeClaim, setVolumeClaim] = useState(); const requestController = useRef(new RequestController({ timeoutDuration: 5000 })); + const { items: volumeClaims, replace: replaceVolumeClaims } = useVolumeClaimStore(); + const { items: pods, replace: replacePods } = usePodStore(); useQuery( ['persistentVolumeClaims', 'pods'], () => { - const tasks = [PERSISTENT_VOLUME_CLAIM_STORE.fetchData, POD_STORE.fetchData]; + const tasks = [ + () => fetchData(replaceVolumeClaims, Resources.PersistentVolumeClaims), + () => fetchData(replacePods, Resources.Pods) + ]; return requestController.current.runTasks(tasks); }, { @@ -109,21 +90,19 @@ const PersistentVolumeClaimOverviewPage = () => { } ); - const dataSource = PERSISTENT_VOLUME_CLAIM_STORE.items.map((volumeClaim) => - getData(volumeClaim, POD_STORE.items) - ); + const dataSource = volumeClaims.map((volumeClaim) => ({ + volumeClaim, + pods + })); + return ( <>
({ + onRow={({ volumeClaim }) => ({ onClick: () => { - const { key } = record; - const volumeClaim = PERSISTENT_VOLUME_CLAIM_STORE.items.filter( - (volumeClaim) => volumeClaim.getName() === key - )[0]; setVolumeClaim(volumeClaim); setOpenDrawer(true); } @@ -131,7 +110,7 @@ const PersistentVolumeClaimOverviewPage = () => { /> setOpenDrawer(false)} /> @@ -139,4 +118,4 @@ const PersistentVolumeClaimOverviewPage = () => { ); }; -export default observer(PersistentVolumeClaimOverviewPage); +export default PersistentVolumeClaimOverviewPage; diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/deployment/deployment.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/deployment/deployment.tsx index b06e7a86ef8..84362583e08 100644 --- a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/deployment/deployment.tsx +++ b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/deployment/deployment.tsx @@ -1,11 +1,9 @@ import { KubeObjectAge } from '@/components/kube/object/kube-object-age'; -import { BaseKubeObjectCondition, Deployment, DeploymentStatus } from '@/k8slens/kube-object'; -import { DEPLOYMENT_STORE } from '@/store/static'; +import { Deployment } from '@/k8slens/kube-object'; import { getConditionColor } from '@/utils/condtion-color'; import { useQuery } from '@tanstack/react-query'; import { Tooltip } from 'antd'; import { ColumnsType } from 'antd/es/table'; -import { observer } from 'mobx-react'; import { useState } from 'react'; import DeploymentDetail from './deployment-detail'; import Table from '../../../table/table'; @@ -13,70 +11,52 @@ import ActionButton from '../../../action-button/action-button'; import { deleteResource } from '@/api/delete'; import { Resources } from '@/constants/kube-object'; import { updateResource } from '@/api/update'; +import { fetchData, useDeploymentStore } from '@/store/kube'; -interface DataType { - key: string; - name: string; - pods: string; - replicas: number; - creationTimestamp?: string; - conditions: BaseKubeObjectCondition[]; -} - -const getData = (dep: Deployment): DataType => { - const { replicas = 0, availableReplicas = 0 } = dep.status ?? {}; - return { - key: dep.getName(), - name: dep.getName(), - pods: `${availableReplicas}/${replicas}`, - replicas: dep.getReplicas(), - creationTimestamp: dep.metadata.creationTimestamp, - conditions: dep.getConditions() - }; -}; - -const columns: ColumnsType = [ +const columns: ColumnsType = [ { title: 'Name', - dataIndex: 'name', - key: 'name' + key: 'name', + fixed: 'left', + render: (_, dep) => dep.getName() }, { title: 'Pods', - dataIndex: 'pods', - key: 'pods' + key: 'pods', + render: (_, dep) => { + const { replicas = 0, availableReplicas = 0 } = dep.status ?? {}; + return `${availableReplicas}/${replicas}`; + } }, { title: 'Replicas', - dataIndex: 'replicas', - key: 'replicas' + key: 'replicas', + render: (_, dep) => dep.getReplicas() }, { title: 'Age', dataIndex: 'creationTimestamp', key: 'age', - render: (creationTimestamp: string) => + render: (_, dep) => }, { title: 'Conditions', - dataIndex: 'conditions', key: 'conditions', - render: (conditions: BaseKubeObjectCondition[]) => - conditions.map(({ type, message }) => ( + render: (_, dep) => + dep.getConditions().map(({ type, message }) => ( {type} )) }, { - dataIndex: 'name', key: 'action', fixed: 'right', - render: (name: string) => ( + render: (_, dep) => ( dep.getName() === name)[0]} - onUpdate={(data: string) => updateResource(data, name, Resources.Deployments)} - onDelete={() => deleteResource(name, Resources.Deployments)} + obj={dep} + onUpdate={(data: string) => updateResource(data, dep.getName(), Resources.Deployments)} + onDelete={() => deleteResource(dep.getName(), Resources.Deployments)} /> ) } @@ -85,22 +65,20 @@ const columns: ColumnsType = [ const DeploymentOverviewPage = () => { const [openDrawer, setOpenDrawer] = useState(false); const [dep, setDep] = useState(); + const { items, replace } = useDeploymentStore(); - useQuery(['deployments'], () => DEPLOYMENT_STORE.fetchData(), { + useQuery(['deployments'], () => fetchData(replace, Resources.Deployments), { refetchInterval: 5000 }); - const dataSource = DEPLOYMENT_STORE.items.map(getData); return ( <>
({ + dataSource={items} + onRow={(dep) => ({ onClick: () => { - const { key } = record; - const dep = DEPLOYMENT_STORE.items.filter((dep) => dep.getName() === key)[0]; setDep(dep); setOpenDrawer(true); } @@ -111,4 +89,4 @@ const DeploymentOverviewPage = () => { ); }; -export default observer(DeploymentOverviewPage); +export default DeploymentOverviewPage; diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/overview/overview.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/overview/overview.tsx index 4ee8e9b5a3b..af7eedff14b 100644 --- a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/overview/overview.tsx +++ b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/overview/overview.tsx @@ -1,20 +1,34 @@ import { Flex } from 'antd'; import WorkloadStatusOverview from './status-overview'; import { convertToPieChartStatusData } from '@/utils/pie-chart'; -import { DEPLOYMENT_STORE, POD_STORE, STATEFUL_SET_STORE } from '@/store/static'; import { entries, startCase } from 'lodash'; -import { observer } from 'mobx-react'; import { useRef } from 'react'; import { RequestController } from '@/utils/request-controller'; import { useQuery } from '@tanstack/react-query'; +import { + fetchData, + getDeploymentsStatuses, + getStatefulSetsStatuses, + useDeploymentStore, + usePodStore, + useStatefulSetStore +} from '@/store/kube'; +import { Resources } from '@/constants/kube-object'; const OverviewPage = () => { const requestController = useRef(new RequestController({ timeoutDuration: 10000 })); + const { items: pods, replace: podReplace, getStatuses: getPodStatuses } = usePodStore(); + const { items: deps, replace: deploymentReplace } = useDeploymentStore(); + const { items: stats, replace: statefulSetReplace } = useStatefulSetStore(); useQuery( ['pods', 'deployments', 'statefulSets'], () => { - const tasks = [POD_STORE.fetchData, DEPLOYMENT_STORE.fetchData, STATEFUL_SET_STORE.fetchData]; + const tasks = [ + () => fetchData(podReplace, Resources.Pods), + () => fetchData(deploymentReplace, Resources.Deployments), + () => fetchData(statefulSetReplace, Resources.StatefulSets) + ]; return requestController.current.runTasks(tasks); }, { @@ -23,13 +37,9 @@ const OverviewPage = () => { ); const statuses = { - Pod: convertToPieChartStatusData(POD_STORE.getPodsStatuses()), - Deployment: convertToPieChartStatusData( - DEPLOYMENT_STORE.getDeploymentsStatuses((labels) => POD_STORE.getByLabel(labels)) - ), - StatefulSet: convertToPieChartStatusData( - STATEFUL_SET_STORE.getStatefulSetsStatuses((ownerId) => POD_STORE.getPodsByOwnerId(ownerId)) - ) + Pod: convertToPieChartStatusData(getPodStatuses), + Deployment: convertToPieChartStatusData(getDeploymentsStatuses(deps, pods)), + StatefulSet: convertToPieChartStatusData(getStatefulSetsStatuses(stats, pods)) }; const overviewStatuses = entries(statuses).map(([key, value]) => ({ @@ -45,4 +55,4 @@ const OverviewPage = () => { ); }; -export default observer(OverviewPage); +export default OverviewPage; diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/pod/pod.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/pod/pod.tsx index dd61e2a0d85..6801d396bf7 100644 --- a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/pod/pod.tsx +++ b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/pod/pod.tsx @@ -1,14 +1,12 @@ -import { Pod, PodContainerStatus } from '@/k8slens/kube-object'; +import { Pod } from '@/k8slens/kube-object'; import { Tooltip } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { keys } from 'lodash'; import ContainerStatusBrick from './container-status-brick'; import { KubeObjectAge } from '@/components/kube/object/kube-object-age'; -import { observer } from 'mobx-react'; import { renderContainerStateTooltipTitle } from './container-status'; import { PodStatusMessage } from '@/constants/pod'; import PodStatus from './pod-status'; -import { POD_STORE } from '@/store/static'; import { useQuery } from '@tanstack/react-query'; import { useState } from 'react'; import PodDetail from './pod-detail'; @@ -17,83 +15,51 @@ import ActionButton from '../../../action-button/action-button'; import { deleteResource } from '@/api/delete'; import { Resources } from '@/constants/kube-object'; import { updateResource } from '@/api/update'; +import { fetchData, usePodStore } from '@/store/kube'; -interface ContainerDataType { - name: string; - state: string; - status: PodContainerStatus | null | undefined; -} - -interface DataType { - key: string; - name: string; - containers: Array; - restarts: number; - ownerRefs: Array<{ - name: string; - kind: string; - }>; - qosClass: string; - creationTimestamp?: string; - status: string; -} - -const getData = (pod: Pod): DataType => { - return { - key: pod.getName(), - name: pod.getName(), - containers: pod.getContainers().map((container) => { - const status = pod.getContainerStatuses().find((status) => status.name === container.name); - const state = status ? keys(status?.state ?? {})[0] : ''; - return { - name: container.name, - state, - status - }; - }), - restarts: pod.getRestartsCount(), - ownerRefs: pod.getOwnerRefs(), - qosClass: pod.getQosClass(), - creationTimestamp: pod.metadata.creationTimestamp, - status: pod.getStatusMessage() - }; -}; - -const columns: ColumnsType = [ +const columns: ColumnsType = [ { title: 'Name', - dataIndex: 'name', key: 'name', - fixed: 'left' + fixed: 'left', + render: (_, pod) => pod.getName() }, { title: 'Containers', - dataIndex: 'containers', key: 'containers', - render: (containers: Array) => ( -
- {containers.map(({ name, state, status }) => ( - - {/* wrapper */} - - - - - ))} -
- ) + render: (_, pod) => { + const containers = pod.getContainers().map((container) => { + const status = pod.getContainerStatuses().find((status) => status.name === container.name); + const state = status ? keys(status?.state ?? {})[0] : ''; + return { + name: container.name, + state, + status + }; + }); + return ( +
+ {containers.map(({ name, state, status }) => ( + + {/* wrapper */} + + + + + ))} +
+ ); + } }, { title: 'Restarts', - dataIndex: 'restarts', - key: 'restarts' + key: 'restarts', + render: (_, pod) => pod.getRestartsCount() }, { title: 'Controlled By', - dataIndex: 'ownerRefs', key: 'controlled-by', - render: (ownerRefs: Array<{ name: string; kind: string }>) => - ownerRefs.map(({ name, kind }) => {kind}) + render: (_, pod) => pod.getOwnerRefs().map(({ name, kind }) => {kind}) }, { title: 'QoS', @@ -104,26 +70,24 @@ const columns: ColumnsType = [ title: 'Age', dataIndex: 'creationTimestamp', key: 'age', - render: (creationTimestamp: string) => + render: (_, pod) => }, { title: 'Status', - dataIndex: 'status', fixed: 'right', key: 'status', filters: PodStatusMessage.map((value) => ({ text: value, value })), - onFilter: (value, record) => record.status === value, - render: (status: string) => + onFilter: (value, pod) => pod.getStatusMessage() === value, + render: (_, pod) => }, { key: 'action', - dataIndex: 'name', fixed: 'right', - render: (name: string) => ( + render: (_, pod) => ( pod.getName() === name)[0]} - onDelete={() => deleteResource(name, Resources.Pods)} - onUpdate={(data: string) => updateResource(data, name, Resources.Pods)} + obj={pod} + onDelete={() => deleteResource(pod.getName(), Resources.Pods)} + onUpdate={(data: string) => updateResource(data, pod.getName(), Resources.Pods)} /> ) } @@ -132,22 +96,20 @@ const columns: ColumnsType = [ const PodOverviewPage = () => { const [openDrawer, setOpenDrawer] = useState(false); const [pod, setPod] = useState(); + const { items, replace } = usePodStore(); - useQuery(['pods'], () => POD_STORE.fetchData(), { + useQuery(['pods'], () => fetchData(replace, Resources.Pods), { refetchInterval: 5000 }); - const dataSource = POD_STORE.items.map(getData); return ( <>
({ + dataSource={items} + onRow={(pod) => ({ onClick: () => { - const { key } = record; - const pod = POD_STORE.items.filter((pod) => pod.getName() === key)[0]; setPod(pod); setOpenDrawer(true); } @@ -158,4 +120,4 @@ const PodOverviewPage = () => { ); }; -export default observer(PodOverviewPage); +export default PodOverviewPage; diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/statefulset/statefulset.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/statefulset/statefulset.tsx index 4f836f26086..8f21e4632d4 100644 --- a/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/statefulset/statefulset.tsx +++ b/frontend/plugins/kubepanel/src/pages/kubepanel/components/kube-object/workload/statefulset/statefulset.tsx @@ -1,9 +1,7 @@ import { KubeObjectAge } from '@/components/kube/object/kube-object-age'; import { StatefulSet } from '@/k8slens/kube-object'; -import { POD_STORE, STATEFUL_SET_STORE } from '@/store/static'; import { useQuery } from '@tanstack/react-query'; import { ColumnsType } from 'antd/es/table'; -import { observer } from 'mobx-react'; import { useRef, useState } from 'react'; import StatefulSetDetail from './statefulset-detail'; import { RequestController } from '@/utils/request-controller'; @@ -12,57 +10,41 @@ import ActionButton from '../../../action-button/action-button'; import { deleteResource } from '@/api/delete'; import { Resources } from '@/constants/kube-object'; import { updateResource } from '@/api/update'; +import { fetchData, getPodsByOwnerId, usePodStore, useStatefulSetStore } from '@/store/kube'; -interface DataType { - key: string; - name: string; - pods: string; - replicas: number; - creationTimestamp?: string; -} - -const getData = (statefulSet: StatefulSet): DataType => { - const { readyReplicas = 0, currentReplicas = 0, replicas = 0 } = statefulSet.status ?? {}; - return { - key: statefulSet.getName(), - name: statefulSet.getName(), - pods: `${readyReplicas}/${replicas}`, - replicas: statefulSet.getReplicas(), - creationTimestamp: statefulSet.metadata.creationTimestamp - }; -}; - -const columns: ColumnsType = [ +const columns: ColumnsType = [ { title: 'Name', - dataIndex: 'name', - key: 'name' + key: 'name', + fixed: 'left', + render: (_, stat) => stat.getName() }, { title: 'Pods', - dataIndex: 'pods', - key: 'pods' + key: 'pods', + render: (_, stat) => { + const { readyReplicas = 0, replicas = 0 } = stat.status ?? {}; + return `${readyReplicas}/${replicas}`; + } }, { title: 'Replicas', - dataIndex: 'replicas', - key: 'replicas' + key: 'replicas', + render: (_, stat) => stat.getReplicas() }, { title: 'Age', - dataIndex: 'creationTimestamp', key: 'age', - render: (creationTimestamp: string) => + render: (_, stat) => }, { - dataIndex: 'name', key: 'action', fixed: 'right', - render: (name: string) => ( + render: (_, stat) => ( statefulSet.getName() === name)[0]} - onUpdate={(data: string) => updateResource(data, name, Resources.StatefulSets)} - onDelete={() => deleteResource(name, Resources.StatefulSets)} + obj={stat} + onUpdate={(data: string) => updateResource(data, stat.getName(), Resources.StatefulSets)} + onDelete={() => deleteResource(stat.getName(), Resources.StatefulSets)} /> ) } @@ -70,13 +52,18 @@ const columns: ColumnsType = [ const StatefulSetOverviewPage = () => { const [openDrawer, setOpenDrawer] = useState(false); - const [statefulSet, setStatefulSet] = useState(); + const [stat, setStat] = useState(); + const { items: pods, replace: replacePods } = usePodStore(); + const { items: stats, replace: replaceStats } = useStatefulSetStore(); const requestController = useRef(new RequestController({ timeoutDuration: 5000 })); useQuery( ['statefulSets', 'pods'], () => { - const task = [STATEFUL_SET_STORE.fetchData, POD_STORE.fetchData]; + const task = [ + () => fetchData(replaceStats, Resources.StatefulSets), + () => fetchData(replacePods, Resources.Pods) + ]; return requestController.current.runTasks(task); }, { @@ -84,27 +71,22 @@ const StatefulSetOverviewPage = () => { } ); - const dataSource = STATEFUL_SET_STORE.items.map(getData); return ( <>
({ + dataSource={stats} + onRow={(stat) => ({ onClick: () => { - const { key } = record; - const statefulSet = STATEFUL_SET_STORE.items.filter( - (statefulSet) => statefulSet.getName() === key - )[0]; - setStatefulSet(statefulSet); + setStat(stat); setOpenDrawer(true); } })} /> setOpenDrawer(false)} /> @@ -112,4 +94,4 @@ const StatefulSetOverviewPage = () => { ); }; -export default observer(StatefulSetOverviewPage); +export default StatefulSetOverviewPage; diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/components/overview.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/components/overview.tsx deleted file mode 100644 index b7eaf83086b..00000000000 --- a/frontend/plugins/kubepanel/src/pages/kubepanel/components/overview.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import AppLayout from './layout'; -import OverviewPage from './kube-object/workload/overview/overview'; -import { useState } from 'react'; -import { SideNavItemKey } from './sidebar/sidebar'; -import PodOverviewPage from './kube-object/workload/pod/pod'; -import DeploymentOverviewPage from './kube-object/workload/deployment/deployment'; -import StatefulSetOverviewPage from './kube-object/workload/statefulset/statefulset'; -import ConfigMapOverviewPage from './kube-object/config/config-map/config-map'; -import PersistentVolumeClaimOverviewPage from './kube-object/storage/volume-claim/volume-claim'; -import { FloatButton } from 'antd'; -import CreateResourceModal from './modal/create-resource-modal'; -import { PlusOutlined } from '@ant-design/icons'; - -const switchPage = (key: SideNavItemKey): React.ReactNode => { - switch (key) { - case SideNavItemKey.Overview: - return ; - case SideNavItemKey.Pod: - return ; - case SideNavItemKey.Deployment: - return ; - case SideNavItemKey.StatefulSet: - return ; - case SideNavItemKey.ConfigMap: - return ; - case SideNavItemKey.PersistentVolumeClaim: - return ; - default: - return ; - } -}; - -const Home = () => { - const [sideNavItemKey, setSideNavItemKey] = useState(SideNavItemKey.Overview); - const [openCreateResourceModal, setOpenCreateResourceModal] = useState(false); - - return ( - setSideNavItemKey(key)}> - {switchPage(sideNavItemKey)} - } - type="primary" - onClick={() => setOpenCreateResourceModal(true)} - /> - setOpenCreateResourceModal(false)} - /> - - ); -}; - -export default Home; diff --git a/frontend/plugins/kubepanel/src/pages/kubepanel/index.tsx b/frontend/plugins/kubepanel/src/pages/kubepanel/index.tsx index f48356d0ae6..a8e692fbab9 100644 --- a/frontend/plugins/kubepanel/src/pages/kubepanel/index.tsx +++ b/frontend/plugins/kubepanel/src/pages/kubepanel/index.tsx @@ -1,55 +1,53 @@ -import { useLoading } from '@/hooks/useLoading'; -import { useToast } from '@/hooks/useToast'; -import { - CONFIG_MAP_STORE, - DEPLOYMENT_STORE, - PERSISTENT_VOLUME_CLAIM_STORE, - POD_STORE, - STATEFUL_SET_STORE -} from '@/store/static'; -import { RequestController } from '@/utils/request-controller'; -import { CreateToastFnReturn } from '@chakra-ui/react'; -import { isError } from 'lodash'; -import { useEffect, useRef } from 'react'; -import OverviewPage from './components/overview'; -import { observer } from 'mobx-react'; +import AppLayout from './components/layout'; +import OverviewPage from './components/kube-object/workload/overview/overview'; +import { useState } from 'react'; +import { SideNavItemKey } from './components/sidebar/sidebar'; +import PodOverviewPage from './components/kube-object/workload/pod/pod'; +import DeploymentOverviewPage from './components/kube-object/workload/deployment/deployment'; +import StatefulSetOverviewPage from './components/kube-object/workload/statefulset/statefulset'; +import ConfigMapOverviewPage from './components/kube-object/config/config-map/config-map'; +import PersistentVolumeClaimOverviewPage from './components/kube-object/storage/volume-claim/volume-claim'; +import { FloatButton } from 'antd'; +import CreateResourceModal from './components/modal/create-resource-modal'; +import { PlusOutlined } from '@ant-design/icons'; -const Home = observer(() => { - const { isLoading, setIsLoading, Loading } = useLoading(); - const { toast } = useToast(); - - const requestController = useRef(new RequestController({ timeoutDuration: 5000 })); - - useEffect(() => { - (async () => { - const res = await fetchData(requestController.current); - toastErrors(res, toast); - setIsLoading(false); - })(); - }); - - return <>{isLoading ? : }; -}); - -const fetchData = (requestController: RequestController) => { - const tasks = [ - POD_STORE.fetchData, - DEPLOYMENT_STORE.fetchData, - STATEFUL_SET_STORE.fetchData, - PERSISTENT_VOLUME_CLAIM_STORE.fetchData, - CONFIG_MAP_STORE.fetchData - ]; - return requestController.runTasks(tasks); +const switchPage = (key: SideNavItemKey): React.ReactNode => { + switch (key) { + case SideNavItemKey.Overview: + return ; + case SideNavItemKey.Pod: + return ; + case SideNavItemKey.Deployment: + return ; + case SideNavItemKey.StatefulSet: + return ; + case SideNavItemKey.ConfigMap: + return ; + case SideNavItemKey.PersistentVolumeClaim: + return ; + default: + return ; + } }; -const toastErrors = (res: any[], toast: CreateToastFnReturn) => { - if (isError(res)) { - toast({ - title: 'Error', - description: res.message, - status: 'error' - }); - } +const Home = () => { + const [sideNavItemKey, setSideNavItemKey] = useState(SideNavItemKey.Overview); + const [openCreateResourceModal, setOpenCreateResourceModal] = useState(false); + + return ( + setSideNavItemKey(key)}> + {switchPage(sideNavItemKey)} + } + type="primary" + onClick={() => setOpenCreateResourceModal(true)} + /> + setOpenCreateResourceModal(false)} + /> + + ); }; export default Home; diff --git a/frontend/plugins/kubepanel/src/store/k8s/config-map.store.ts b/frontend/plugins/kubepanel/src/store/k8s/config-map.store.ts new file mode 100644 index 00000000000..15c40351cd2 --- /dev/null +++ b/frontend/plugins/kubepanel/src/store/k8s/config-map.store.ts @@ -0,0 +1,8 @@ +import { ConfigMap } from '@/k8slens/kube-object'; +import { create } from 'zustand'; +import { createKubeStoreSlice } from './kube.store'; +import { ConfigMapStore } from '@/types/state'; + +export const useConfigMapStore = create()((...a) => ({ + ...createKubeStoreSlice(ConfigMap.kind)(...a) +})); diff --git a/frontend/plugins/kubepanel/src/store/k8s/configmap.store.ts b/frontend/plugins/kubepanel/src/store/k8s/configmap.store.ts deleted file mode 100644 index 542d1e97536..00000000000 --- a/frontend/plugins/kubepanel/src/store/k8s/configmap.store.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ConfigMap } from '@/k8slens/kube-object'; -import { ItemStore } from './data.store'; -import { Resources } from '@/constants/kube-object'; - -export class ConfigMapStore extends ItemStore { - constructor() { - super(Resources.ConfigMaps); - } -} diff --git a/frontend/plugins/kubepanel/src/store/k8s/data.store.ts b/frontend/plugins/kubepanel/src/store/k8s/data.store.ts deleted file mode 100644 index 84121072e5c..00000000000 --- a/frontend/plugins/kubepanel/src/store/k8s/data.store.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { entries, isArray } from 'lodash'; -import { action, makeObservable, observable } from 'mobx'; -import { KubeObject } from '@/k8slens/kube-object'; -import { Resources } from '@/constants/kube-object'; -import { getResource } from '@/api/list'; - -export class ItemStore { - @observable items = observable.array([], { deep: false }); - private readonly resource: Resources; - - constructor(resource: Resources) { - this.resource = resource; - makeObservable(this); - } - - @action - replaceItems(items: K | Array) { - if (isArray(items)) { - this.items.replace(items); - } else { - this.items.replace([items]); - } - } - - getByLabel(labels: string[] | Partial>): K[] { - if (Array.isArray(labels)) { - return this.items.filter((item: K) => { - const itemLabels = item.getLabels(); - - return labels.every((label) => itemLabels.includes(label)); - }); - } else { - return this.items.filter((item: K) => { - const itemLabels = item.metadata.labels || {}; - - return entries(labels).every(([key, value]) => itemLabels[key] === value); - }); - } - } - - // function() can't be used as a variable, it'll cause an 'This is undefined' error - // we want to consider it as a variable and pass it into RequestController.runTasks - fetchData = async () => { - try { - const items = await getResource(this.resource); - this.replaceItems(items); - return items; - } catch (err) { - return Promise.reject(err); - } - }; -} diff --git a/frontend/plugins/kubepanel/src/store/k8s/deployment.store.ts b/frontend/plugins/kubepanel/src/store/k8s/deployment.store.ts index 6c5ad66165d..bfeb13ed028 100644 --- a/frontend/plugins/kubepanel/src/store/k8s/deployment.store.ts +++ b/frontend/plugins/kubepanel/src/store/k8s/deployment.store.ts @@ -1,34 +1,28 @@ -import { Deployment, PodStatusPhase } from '@/k8slens/kube-object'; -import { GetByLabel } from './pod.store'; -import { ItemStore } from './data.store'; -import { Resources } from '@/constants/kube-object'; +import { Deployment, Pod, PodStatusPhase } from '@/k8slens/kube-object'; +import { createKubeStoreSlice, getByLabel } from './kube.store'; +import { DeploymentStore } from '@/types/state'; +import { create } from 'zustand'; -const getChildPods = (getByLabel: GetByLabel, deployment: Deployment) => { - return getByLabel(deployment.getTemplateLabels()).filter( - (pod) => pod.getNs() === deployment.getNs() - ); -}; +export function getDeploymentsStatuses(deps: Deployment[], pods: Pod[]) { + const status = { running: 0, failed: 0, pending: 0 }; -export class DeploymentStore extends ItemStore { - constructor() { - super(Resources.Deployments); - } + deps.forEach((deployment) => { + const statuses = new Set( + getByLabel(pods, deployment.getTemplateLabels()).map((pod) => pod.getStatus()) + ); - getDeploymentsStatuses(getByLabel: GetByLabel) { - const status = { running: 0, failed: 0, pending: 0 }; + if (statuses.has(PodStatusPhase.FAILED)) { + status.failed++; + } else if (statuses.has(PodStatusPhase.PENDING)) { + status.pending++; + } else { + status.running++; + } + }); - this.items.forEach((deployment) => { - const statuses = new Set(getChildPods(getByLabel, deployment).map((pod) => pod.getStatus())); - - if (statuses.has(PodStatusPhase.FAILED)) { - status.failed++; - } else if (statuses.has(PodStatusPhase.PENDING)) { - status.pending++; - } else { - status.running++; - } - }); - - return status; - } + return status; } + +export const useDeploymentStore = create()((...a) => ({ + ...createKubeStoreSlice(Deployment.kind)(...a) +})); diff --git a/frontend/plugins/kubepanel/src/store/k8s/kube.store.ts b/frontend/plugins/kubepanel/src/store/k8s/kube.store.ts index 11c41fd8cfc..a034295bae2 100644 --- a/frontend/plugins/kubepanel/src/store/k8s/kube.store.ts +++ b/frontend/plugins/kubepanel/src/store/k8s/kube.store.ts @@ -1,26 +1,69 @@ +import { getResource } from '@/api/list'; +import { Resources } from '@/constants/kube-object'; import { KubeObject } from '@/k8slens/kube-object'; -import { randomUUID } from 'crypto'; -import { observable } from 'mobx'; +import { KubeStore } from '@/types/state'; +import { entries } from 'lodash'; +import { StateCreator } from 'zustand'; -type cmp = (a: Object, b: Object) => number; +export function createKubeStoreSlice( + kind: K['kind'] +): StateCreator> { + return (set) => ({ + items: [], + kind, + isLoaded: false, + setIsLoaded(isLoaded) { + set({ isLoaded }); + }, + modify(item: K) { + set((state) => { + const arr = state.items; + const idx = arr.findIndex((i) => i.getName() === item.getName()); + if (idx === -1) { + arr.push(item); + } else { + arr[idx] = item; + } + return { items: arr }; + }); + }, + remove(item: K) { + set((state) => { + const items = state.items.filter((i) => i.getName() !== item.getName()); + return { items }; + }); + }, + replace(items) { + set({ items }); + } + }); +} -export abstract class KubeStore { - readonly storeName: string; // use for debug only - @observable private items; +export function getByLabel( + items: K[], + labels: string[] | Partial> +): K[] { + if (Array.isArray(labels)) { + return items.filter((item: K) => { + const itemLabels = item.getLabels(); - constructor(storeName?: string) { - this.storeName = storeName ?? `KubeStore-${randomUUID()}`; - this.items = observable.array([], { name: this.storeName, deep: false }); - } + return labels.every((label) => itemLabels.includes(label)); + }); + } else { + return items.filter((item: K) => { + const itemLabels = item.metadata.labels || {}; - public getItems(sortBy?: cmp): Object[] { - if (sortBy) { - return [...this.items].sort(sortBy); - } - return this.items; + return entries(labels).every(([key, value]) => itemLabels[key] === value); + }); } +} - public getItemByIndex(index: number): Object { - return this.items[index]; - } +// it's used to transition and will be removed very soon +export async function fetchData( + setter: KubeStore['replace'], + resource: Resources +) { + const items = await getResource(resource); + setter(items); + return items; } diff --git a/frontend/plugins/kubepanel/src/store/k8s/pod.store.ts b/frontend/plugins/kubepanel/src/store/k8s/pod.store.ts index 176e5d30ac1..5a2e3974505 100644 --- a/frontend/plugins/kubepanel/src/store/k8s/pod.store.ts +++ b/frontend/plugins/kubepanel/src/store/k8s/pod.store.ts @@ -1,23 +1,27 @@ import { Pod } from '@/k8slens/kube-object'; +import { PodStore, StatusesComputed } from '@/types/state'; +import { create } from 'zustand'; +import { createKubeStoreSlice } from './kube.store'; import { countBy } from 'lodash'; -import { ItemStore } from './data.store'; -import { Resources } from '@/constants/kube-object'; +import { computed } from 'zustand-computed'; -export type GetPodsByOwnerId = (ownerId: string) => Pod[]; -export type GetByLabel = (labels: string[] | Partial>) => Pod[]; +const computeState = (state: PodStore): StatusesComputed => { + return { + getStatuses: countBy(state.items, (pod) => pod.getStatus()) + }; +}; -export class PodStore extends ItemStore { - constructor() { - super(Resources.Pods); - } +export const usePodStore = create()( + computed( + (...a) => ({ + ...createKubeStoreSlice(Pod.kind)(...a) + }), + computeState + ) +); - getPodsByOwnerId(workloadId: string): Pod[] { - return this.items.filter((pod) => { - return pod.getOwnerRefs().find((owner) => owner.uid === workloadId); - }); - } - - getPodsStatuses() { - return countBy(this.items.map((pod) => pod.getStatus()).sort()); - } +export function getPodsByOwnerId(pods: Pod[], workloadId: string): Pod[] { + return pods.filter((pod) => { + return pod.getOwnerRefs().find((owner) => owner.uid === workloadId); + }); } diff --git a/frontend/plugins/kubepanel/src/store/k8s/pvc.store.ts b/frontend/plugins/kubepanel/src/store/k8s/pvc.store.ts deleted file mode 100644 index 9db15a41ba1..00000000000 --- a/frontend/plugins/kubepanel/src/store/k8s/pvc.store.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { PersistentVolumeClaim } from '@/k8slens/kube-object'; -import { ItemStore } from './data.store'; -import { Resources } from '@/constants/kube-object'; - -export class PersistentVolumeClaimStore extends ItemStore { - constructor() { - super(Resources.PersistentVolumeClaims); - } -} diff --git a/frontend/plugins/kubepanel/src/store/k8s/stateful-set.store.ts b/frontend/plugins/kubepanel/src/store/k8s/stateful-set.store.ts new file mode 100644 index 00000000000..b3ab341ecb1 --- /dev/null +++ b/frontend/plugins/kubepanel/src/store/k8s/stateful-set.store.ts @@ -0,0 +1,29 @@ +import { Pod, PodStatusPhase, StatefulSet } from '@/k8slens/kube-object'; +import { getPodsByOwnerId } from './pod.store'; +import { create } from 'zustand'; +import { StatefulSetStore } from '@/types/state'; +import { createKubeStoreSlice } from './kube.store'; + +export function getStatefulSetsStatuses(statefulSets: StatefulSet[], pods: Pod[]) { + const status = { running: 0, failed: 0, pending: 0 }; + + for (const statefulSet of statefulSets) { + const statuses = new Set( + getPodsByOwnerId(pods, statefulSet.getId()).map((pod) => pod.getStatus()) + ); + + if (statuses.has(PodStatusPhase.FAILED)) { + status.failed++; + } else if (statuses.has(PodStatusPhase.PENDING)) { + status.pending++; + } else { + status.running++; + } + } + + return status; +} + +export const useStatefulSetStore = create()((...a) => ({ + ...createKubeStoreSlice(StatefulSet.kind)(...a) +})); diff --git a/frontend/plugins/kubepanel/src/store/k8s/statefulset.store.ts b/frontend/plugins/kubepanel/src/store/k8s/statefulset.store.ts deleted file mode 100644 index 27f0dd1969a..00000000000 --- a/frontend/plugins/kubepanel/src/store/k8s/statefulset.store.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { PodStatusPhase, StatefulSet } from '@/k8slens/kube-object'; -import { GetPodsByOwnerId } from './pod.store'; -import { ItemStore } from './data.store'; -import { Resources } from '@/constants/kube-object'; - -export class StatefulSetStore extends ItemStore { - constructor() { - super(Resources.StatefulSets); - } - - getStatefulSetsStatuses(getPodsByOwnerId: GetPodsByOwnerId) { - const status = { running: 0, failed: 0, pending: 0 }; - - for (const statefulSet of this.items) { - const statuses = new Set(getPodsByOwnerId(statefulSet.getId()).map((pod) => pod.getStatus())); - - if (statuses.has(PodStatusPhase.FAILED)) { - status.failed++; - } else if (statuses.has(PodStatusPhase.PENDING)) { - status.pending++; - } else { - status.running++; - } - } - - return status; - } -} diff --git a/frontend/plugins/kubepanel/src/store/k8s/volume-claim.store.ts b/frontend/plugins/kubepanel/src/store/k8s/volume-claim.store.ts new file mode 100644 index 00000000000..64b66960aaf --- /dev/null +++ b/frontend/plugins/kubepanel/src/store/k8s/volume-claim.store.ts @@ -0,0 +1,8 @@ +import { PersistentVolumeClaim } from '@/k8slens/kube-object'; +import { createKubeStoreSlice } from './kube.store'; +import { VolumeClaimStore } from '@/types/state'; +import { create } from 'zustand'; + +export const useVolumeClaimStore = create()((...a) => ({ + ...createKubeStoreSlice(PersistentVolumeClaim.kind)(...a) +})); diff --git a/frontend/plugins/kubepanel/src/store/kube.ts b/frontend/plugins/kubepanel/src/store/kube.ts new file mode 100644 index 00000000000..8f7ce5e9a9f --- /dev/null +++ b/frontend/plugins/kubepanel/src/store/kube.ts @@ -0,0 +1,6 @@ +export * from './k8s/pod.store'; +export * from './k8s/deployment.store'; +export * from './k8s/stateful-set.store'; +export * from './k8s/config-map.store'; +export * from './k8s/volume-claim.store'; +export * from './k8s/kube.store'; diff --git a/frontend/plugins/kubepanel/src/store/static.ts b/frontend/plugins/kubepanel/src/store/static.ts index 5bf30906139..66cbeb2d284 100644 --- a/frontend/plugins/kubepanel/src/store/static.ts +++ b/frontend/plugins/kubepanel/src/store/static.ts @@ -1,15 +1,4 @@ import { getInitData } from '@/api/platform'; -import { ConfigMapStore } from './k8s/configmap.store'; -import { DeploymentStore } from './k8s/deployment.store'; -import { PodStore } from './k8s/pod.store'; -import { PersistentVolumeClaimStore } from './k8s/pvc.store'; -import { StatefulSetStore } from './k8s/statefulset.store'; - -export const POD_STORE = new PodStore(); -export const DEPLOYMENT_STORE = new DeploymentStore(); -export const STATEFUL_SET_STORE = new StatefulSetStore(); -export const CONFIG_MAP_STORE = new ConfigMapStore(); -export const PERSISTENT_VOLUME_CLAIM_STORE = new PersistentVolumeClaimStore(); export let SEALOS_DOMAIN: string; diff --git a/frontend/plugins/kubepanel/src/types/state.d.ts b/frontend/plugins/kubepanel/src/types/state.d.ts new file mode 100644 index 00000000000..a7b133b2940 --- /dev/null +++ b/frontend/plugins/kubepanel/src/types/state.d.ts @@ -0,0 +1,33 @@ +import { + ConfigMap, + Deployment, + KubeObject, + PersistentVolumeClaim, + Pod, + StatefulSet + } from '@/k8slens/kube-object'; + + type KubeStoreState = { + items: Array; + kind: K['kind']; + isLoaded: boolean; + }; + type KubeStoreAction = { + modify: (item: K) => void; + remove: (item: K) => void; + replace: (items: K[]) => void; + setIsLoaded: (isLoaded: boolean) => void; + }; + + type KubeStore = KubeStoreState & KubeStoreAction; + + type StatusesComputed = { + getStatuses: Record; + }; + + type PodStore = KubeStore; + type DeploymentStore = KubeStore; + type StatefulSetStore = KubeStore; + type ConfigMapStore = KubeStore; + type VolumeClaimStore = KubeStore; + \ No newline at end of file