├── .gitignore
├── public
├── icon.ico
├── icon.icns
├── dmgbackground.tiff
└── assets
│ ├── container.png
│ ├── kaptn4ico.png
│ ├── Artboard-1.png
│ ├── kraneSetupImg.png
│ ├── kraneDashboardImg.png
│ ├── kraneMetricsImg.png
│ ├── kraneQuickStartImg2.png
│ ├── Artboard 1.svg
│ ├── deploy-2.svg
│ ├── pod.svg
│ ├── rs.svg
│ ├── node.svg
│ └── container.svg
├── src
├── .DS_Store
├── Pages
│ ├── .DS_Store
│ └── assets
│ │ ├── container.png
│ │ ├── kaptn4ico.png
│ │ ├── kraneSetupImg.png
│ │ ├── kraneMetricsImg.png
│ │ ├── kraneDashboardImg.png
│ │ ├── kraneQuickStartImg2.png
│ │ ├── deploy-2.svg
│ │ ├── pod.svg
│ │ ├── rs.svg
│ │ ├── node.svg
│ │ └── container.svg
├── components
│ ├── assets
│ │ ├── container.png
│ │ ├── kaptn4ico.png
│ │ ├── kraneSetupImg.png
│ │ ├── kraneMetricsImg.png
│ │ ├── kraneDashboardImg.png
│ │ ├── kraneQuickStartImg2.png
│ │ ├── deploy-2.svg
│ │ ├── pod.svg
│ │ ├── rs.svg
│ │ └── node.svg
│ ├── Topbar.tsx
│ ├── Terminal.tsx
│ ├── commands.tsx
│ ├── SetupCommandLine.tsx
│ ├── Sidebar.tsx
│ ├── DashboardCommandLine.tsx
│ ├── PodCpuChart.tsx
│ ├── NodeCpuChart.tsx
│ ├── PodMemoryChart.tsx
│ └── NodeMemoryChart.tsx
├── main.tsx
├── index.css
├── App.tsx
└── theme.ts
├── babel.config.js
├── .editorconfig
├── __tests__
├── components
│ ├── Topbar.test.js
│ ├── Sidebar.test.js
│ ├── Terminal.test.js
│ └── CommandLine.test.js
└── pages
│ ├── Dashboard.test.js
│ └── Krane.test.js
├── tsconfig.json
├── vite.config.ts
├── index.html
├── electron-builder.json
├── CHANGELOG.md
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build/
3 | dist/
4 | .DS_Store
5 | **/.DS_Store
--------------------------------------------------------------------------------
/public/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/public/icon.ico
--------------------------------------------------------------------------------
/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/.DS_Store
--------------------------------------------------------------------------------
/public/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/public/icon.icns
--------------------------------------------------------------------------------
/src/Pages/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/Pages/.DS_Store
--------------------------------------------------------------------------------
/public/dmgbackground.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/public/dmgbackground.tiff
--------------------------------------------------------------------------------
/public/assets/container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/public/assets/container.png
--------------------------------------------------------------------------------
/public/assets/kaptn4ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/public/assets/kaptn4ico.png
--------------------------------------------------------------------------------
/public/assets/Artboard-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/public/assets/Artboard-1.png
--------------------------------------------------------------------------------
/src/Pages/assets/container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/Pages/assets/container.png
--------------------------------------------------------------------------------
/src/Pages/assets/kaptn4ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/Pages/assets/kaptn4ico.png
--------------------------------------------------------------------------------
/public/assets/kraneSetupImg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/public/assets/kraneSetupImg.png
--------------------------------------------------------------------------------
/public/assets/kraneDashboardImg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/public/assets/kraneDashboardImg.png
--------------------------------------------------------------------------------
/public/assets/kraneMetricsImg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/public/assets/kraneMetricsImg.png
--------------------------------------------------------------------------------
/src/Pages/assets/kraneSetupImg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/Pages/assets/kraneSetupImg.png
--------------------------------------------------------------------------------
/src/components/assets/container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/components/assets/container.png
--------------------------------------------------------------------------------
/src/components/assets/kaptn4ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/components/assets/kaptn4ico.png
--------------------------------------------------------------------------------
/public/assets/kraneQuickStartImg2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/public/assets/kraneQuickStartImg2.png
--------------------------------------------------------------------------------
/src/Pages/assets/kraneMetricsImg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/Pages/assets/kraneMetricsImg.png
--------------------------------------------------------------------------------
/src/Pages/assets/kraneDashboardImg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/Pages/assets/kraneDashboardImg.png
--------------------------------------------------------------------------------
/src/Pages/assets/kraneQuickStartImg2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/Pages/assets/kraneQuickStartImg2.png
--------------------------------------------------------------------------------
/src/components/assets/kraneSetupImg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/components/assets/kraneSetupImg.png
--------------------------------------------------------------------------------
/src/components/assets/kraneMetricsImg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/components/assets/kraneMetricsImg.png
--------------------------------------------------------------------------------
/src/components/assets/kraneDashboardImg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/components/assets/kraneDashboardImg.png
--------------------------------------------------------------------------------
/src/components/assets/kraneQuickStartImg2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/kaptn/HEAD/src/components/assets/kraneQuickStartImg2.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ['@babel/preset-env', {targets: {node: 'current'}}],
4 | ['@babel/preset-react', {
5 | "runtime": "automatic"
6 | }],
7 | '@babel/preset-typescript',
8 | ],
9 | };
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 2
8 | indent_style = space
9 | insert_final_newline = true
10 | max_line_length = 80
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | max_line_length = 0
15 | trim_trailing_whitespace = false
16 |
17 | [COMMIT_EDITMSG]
18 | max_line_length = 0
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import App from './App';
4 | import './index.css';
5 | import { ProSidebarProvider } from 'react-pro-sidebar';
6 |
7 | ReactDOM.createRoot(document.getElementById('root')).render(
8 |
tag here ensures proper spacing
14 | props.response.forEach((el) => {
15 | const paredResponse: JSX.Element[] = el.response
16 | .split("\n")
17 | .map(function (item: string) {
18 | return (
19 |
20 | {item}
21 |
22 | );
23 | });
24 | commandLog.push(
25 |
26 |
32 | {props.shortDir} $ {el.command}
33 |
34 | <>{paredResponse}>
35 |
36 | );
37 | key++;
38 | });
39 |
40 | const handleClearLog = () => {
41 | props.setResponse([]);
42 | };
43 |
44 | let clearButtonDiv;
45 | if (commandLog.length > 0) {
46 | clearButtonDiv = (
47 |
64 | CLEAR
65 |
66 | );
67 | }
68 |
69 | return (
70 |
87 | {commandLog}
88 | {clearButtonDiv}
89 |
90 | );
91 | };
92 |
93 | export default Terminal;
94 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Changelog for kaptn v2.0.1 and earlier.
2 |
3 |
4 | ## Version 2.0.1 -
5 |
6 | - Adds interactive, expandable visx graphs for pods' and nodes' historical cpu and memory usage.
7 |
8 | - Adds variable refresh rate.
9 |
10 | - Adds various other bugs fixes and additions including: Fixes bug with user directory in CLI
11 |
12 |
13 | ## Version 2.0.0 -
14 |
15 | - **Kaptn Krane Cluster Manager:**
16 | View live and historical metrics, and scale, delete or restart resources like pods, nodes, and deployments in our revolutionary, easy-to-use interface that harnesses the power of kubectl commands. Features including filtering by namespace, sorting by cpu and memory percent, one-click control of your clusters, and much more makes taking command of Kubernetes easier than ever before!
17 |
18 | - **New Start Page:**
19 | We've completely revamped the start page, including the addition of installation checks and quickstart links. Now you can troubleshoot problems more quickly, and get right into your workflow.
20 |
21 | - Adds various other bugs fixes and additions including: clear terminal log button, redesign of CLI, Instant Help Desk, Learning Center and much more!
22 |
23 | ## Version 1.2.0 -
24 |
25 | - Adds ability to use kubectl commands without choosing a working directory.
26 |
27 | ## Version 1.1.0 -
28 |
29 | - **_Now available for Mac, Windows, and Linux_**
30 |
31 | - **Cluster Metrics Visualizer:**
32 | Easily sync your Kaptn workspace to Grafana and Prometheus to allow for clear and real-time visualization of your clusters' health. Utilize our quick set-up if you are not already connected, and consider Kaptn your only stop for working with and monitoring your Kubernetes clusters.
33 |
34 | - **Instant Help Desk:**
35 | Get help information on demand and at the click of a button with the Instant Help Desk. Now you can get more info about any command or type without leaving the command line, and losing the code you've already written.
36 |
37 | - **Kaptn Learning Center:**
38 | Inside the Easy Setup page you can now find the Learning Center with resources you need to learn Kubernetes. You can follow tutorials, read articles and documentation, and master Kubernetes faster than ever.
39 |
40 | - **Light/Dark Mode:**
41 | Whether it's eye strain, or just personal preference, we know engineers can be selective about their work environments. So we created a Light/Dark mode that allows you to work with your favorite color combination. Now you can focus on coding with no distractions to your workflow.
42 |
43 | This update also includes various bugs fixes, including:
44 |
45 | - Bug where kubectl commands could not be used on some Mac operating systems.
46 |
--------------------------------------------------------------------------------
/src/components/commands.tsx:
--------------------------------------------------------------------------------
1 | const commands = [
2 | { title: 'create', category: 'Beginners Commands' },
3 | { title: 'expose', category: 'Beginners Commands' },
4 | { title: 'run', category: 'Beginners Commands' },
5 | { title: 'set', category: 'Beginners Commands' },
6 | { title: 'explain', category: 'Intermediate Commands' },
7 | { title: 'get', category: 'Intermediate Commands' },
8 | { title: 'edit', category: 'Intermediate Commands' },
9 | { title: 'delete', category: 'Intermediate Commands' },
10 | { title: 'rollout', category: 'Deploy Commands' },
11 | { title: 'scale', category: 'Deploy Commands' },
12 | { title: 'autoscale', category: 'Deploy Commands' },
13 | { title: 'certificate', category: 'Cluster Management Commands' },
14 | { title: 'cluster-info', category: 'Cluster Management Commands' },
15 | { title: 'top', category: 'Cluster Management Commands' },
16 | { title: 'cordon', category: 'Cluster Management Commands' },
17 | { title: 'uncordon', category: 'Cluster Management Commands' },
18 | { title: 'drain', category: 'Cluster Management Commands' },
19 | { title: 'taint', category: 'Cluster Management Commands' },
20 | { title: 'describe', category: 'Troubleshoot/Debug Commands' },
21 | { title: 'logs', category: 'Troubleshoot/Debug Commands' },
22 | { title: 'attach', category: 'Troubleshoot/Debug Commands' },
23 | { title: 'exec', category: 'Troubleshoot/Debug Commands' },
24 | { title: 'port-forward', category: 'Troubleshoot/Debug Commands' },
25 | { title: 'proxy', category: 'Troubleshoot/Debug Commands' },
26 | { title: 'cp', category: 'Troubleshoot/Debug Commands' },
27 | { title: 'auth', category: 'Troubleshoot/Debug Commands' },
28 | { title: 'debug', category: 'Troubleshoot/Debug Commands' },
29 | { title: 'diff', category: 'Advanced Commands' },
30 | { title: 'apply', category: 'Advanced Commands' },
31 | { title: 'patch', category: 'Advanced Commands' },
32 | { title: 'replace', category: 'Advanced Commands' },
33 | { title: 'wait', category: 'Advanced Commands' },
34 | { title: 'kustomize', category: 'Advanced Commands' },
35 | { title: 'label', category: 'Settings Commands' },
36 | { title: 'annotate', category: 'Settings Commands' },
37 | { title: 'completion', category: 'Settings Commands' },
38 | { title: 'alpha', category: 'Other Commands' },
39 | { title: 'api-resources', category: 'Other Commands' },
40 | { title: 'api-versions', category: 'Other Commands' },
41 | { title: 'config', category: 'Other Commands' },
42 | { title: 'plugin', category: 'Other Commands' },
43 | { title: 'version', category: 'Other Commands' },
44 | ];
45 |
46 | export default commands
--------------------------------------------------------------------------------
/src/components/SetupCommandLine.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | InputAdornment,
4 | Button,
5 | TextField,
6 | useTheme,
7 | Box,
8 | Grid,
9 | } from "@mui/material";
10 | // import { clipboard } from 'electron';
11 |
12 | const CommandLine = (props) => {
13 | const theme = useTheme();
14 |
15 | // Add/remove functionality in text box
16 | const handleChange = (e) => {
17 | let newUserInput = "";
18 |
19 | if (e.nativeEvent.inputType === "deleteContentBackward") {
20 | newUserInput = props.userInput.slice(0, props.userInput.length - 1);
21 | } else {
22 | newUserInput =
23 | props.userInput + e.target.value[e.target.value.length - 1];
24 | }
25 | props.setUserInput(newUserInput);
26 | };
27 |
28 | const handleClear = (e) => {
29 | let userInput2 = "";
30 |
31 | props.setUserInput(userInput2);
32 | props.setVerb(userInput2);
33 | props.setType(userInput2);
34 | props.setName(userInput2);
35 | props.setFlags([]);
36 | };
37 |
38 | // const handlePaste = (event) => {
39 | // let userInput = event.clipboardData.items[0].getAsString();
40 | // // console.log(userInput);
41 | // props.setUserInput(userInput);
42 | // // props.setVerb(userInput);
43 | // // props.setType(userInput);
44 | // // props.setName(userInput);
45 | // // props.setFlags([]);
46 | // };
47 |
48 | return (
49 |
57 | {" "}
58 |
139 |
140 | );
141 | };
142 |
143 | export default CommandLine;
144 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react",
3 | "productName": "Kaptn",
4 | "private": true,
5 | "version": "2.0.5",
6 | "description": "Powerful K8s Development Tool",
7 | "main": "main.js",
8 | "scripts": {
9 | "start": "NODE_ENV='development' electron .",
10 | "dev": "concurrently \"NODE_ENV='development' nodemon server/server.js\" \"NODE_ENV='development' vite dev\" --host 0.0.0.0",
11 | "build": "vite build",
12 | "build:mwl": "vite build && npm run pack",
13 | "build64": "vite build && npm run pack64",
14 | "build:windows": "npm run build && electron-builder --windows --x64",
15 | "build:winportable": "npm run build && electron-builder --win portable --x64",
16 | "build:mac": "npm run build && electron-builder --mac",
17 | "build:linux": "npm run build && electron-builder --linux --x64",
18 | "postinstall": "electron-builder install-app-deps",
19 | "pack": "electron-builder -mwl",
20 | "pack64": "electron-builder -mwl --x64",
21 | "preview": "vite preview",
22 | "vite": "vite",
23 | "test": "jest"
24 | },
25 | "jest": {
26 | "testEnvironment": "jsdom"
27 | },
28 | "author": {
29 | "name": "Kaptn",
30 | "url": "https://kaptn.io/"
31 | },
32 | "contributors": [
33 | "Brecht Horn",
34 | "Olivia Hodel",
35 | "Hwi Won Choi",
36 | "Natalie Cordoves",
37 | "Yining Wang"
38 | ],
39 | "license": "MIT",
40 | "dependencies": {
41 | "@emotion/react": "^11.10.6",
42 | "@emotion/styled": "^11.10.6",
43 | "@mui/icons-material": "^5.11.16",
44 | "@mui/material": "^5.12.0",
45 | "@visx/curve": "^3.3.0",
46 | "@visx/event": "^3.3.0",
47 | "@visx/gradient": "^3.3.0",
48 | "@visx/grid": "^3.3.0",
49 | "@visx/scale": "^3.3.0",
50 | "@visx/shape": "^3.3.0",
51 | "@visx/tooltip": "^3.3.0",
52 | "@visx/vendor": "^3.3.0",
53 | "concurrently": "^8.0.1",
54 | "fix-path": "^3.0.0",
55 | "nodemon": "^2.0.22",
56 | "react": "^18.2.0",
57 | "react-dom": "^18.2.0",
58 | "react-pro-sidebar": "^1.0.0",
59 | "react-router-dom": "^6.11.0"
60 | },
61 | "devDependencies": {
62 | "@babel/core": "^7.21.8",
63 | "@babel/plugin-transform-runtime": "^7.21.4",
64 | "@babel/preset-env": "^7.21.5",
65 | "@babel/preset-react": "^7.18.6",
66 | "@babel/preset-typescript": "^7.21.5",
67 | "@testing-library/jest-dom": "^5.16.5",
68 | "@testing-library/react": "^14.0.0",
69 | "@types/node": "^18.16.0",
70 | "@types/react": "^18.0.38",
71 | "@types/react-dom": "^18.0.11",
72 | "@vitejs/plugin-react": "^3.1.0",
73 | "babel-jest": "^29.5.0",
74 | "electron": "^24.0.0",
75 | "electron-builder": "^24.4.0",
76 | "electron-packager": "^17.1.1",
77 | "esbuild": "^0.19.3",
78 | "https": "^1.0.0",
79 | "jest": "^29.5.0",
80 | "jest-environment-jsdom": "^29.5.0",
81 | "typescript": "^5.0.4",
82 | "vite": "^4.2.0"
83 | },
84 | "build": {
85 | "appId": "Kaptn",
86 | "productName": "Kaptn",
87 | "asar": true,
88 | "win": {
89 | "target": "nsis",
90 | "icon": "./public/icon.ico",
91 | "requestedExecutionLevel": "asInvoker"
92 | },
93 | "nsis": {
94 | "oneClick": false,
95 | "createDesktopShortcut": true,
96 | "createStartMenuShortcut": true,
97 | "shortcutName": "Kaptn",
98 | "perMachine": false,
99 | "deleteAppDataOnUninstall": true,
100 | "allowElevation": false,
101 | "allowToChangeInstallationDirectory": true,
102 | "menuCategory": true,
103 | "installerLanguages": [
104 | "en_US"
105 | ]
106 | },
107 | "mac": {
108 | "target": "dmg",
109 | "icon": "./public/icon.icns",
110 | "category": "public.app-category.developer-tools"
111 | },
112 | "dmg": {
113 | "icon": "./public/icon.icns",
114 | "title": "Kaptn",
115 | "background": "./public/dmgbackground.tiff"
116 | },
117 | "linux": {
118 | "icon": "./public/icon.icns",
119 | "target": "AppImage"
120 | },
121 | "directories": {
122 | "buildResources": "assets"
123 | },
124 | "files": [
125 | "package.json",
126 | "dist/**/*",
127 | "node_modules",
128 | "main.js"
129 | ]
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700&display=swap");
2 |
3 | body {
4 | top: 0px;
5 | right: 0px;
6 | bottom: 0px;
7 | left: 0px;
8 | margin: 0px;
9 | padding: 0px;
10 | overflow-x: hidden;
11 | }
12 |
13 | /* killport 300 link turns to hand when hovered */
14 | #killport:hover {
15 | cursor: pointer;
16 | }
17 |
18 | /* make metrics install link a cursor on hover */
19 | #metrics_install_link:hover {
20 | cursor: pointer;
21 | }
22 |
23 | /* Scrollbar */
24 | ::-webkit-scrollbar {
25 | width: 8px;
26 | height: 7px;
27 | }
28 |
29 | /* Track */
30 | ::-webkit-scrollbar-track {
31 | background: rgba(210, 223, 61, 0.129);
32 | }
33 |
34 | /* Handle */
35 | ::-webkit-scrollbar-thumb {
36 | background: #716b88;
37 | border-radius: 2.5px;
38 | }
39 |
40 | /* Handle on Hover */
41 | ::-webkit-scrollbar-track:hover {
42 | background: rgb(160, 160, 195, 0.2);
43 | }
44 |
45 | a {
46 | color: inherit;
47 | text-decoration: none;
48 | }
49 |
50 | #sidebarTopDiv {
51 | display: flex;
52 | flex-direction: column;
53 | position: fixed;
54 | top: 30px;
55 | left: 0px;
56 | width: 36px;
57 | height: 100%;
58 | /* margin: 0 30px 0 0; */
59 | padding: 30px 0px 0 0;
60 | /* background-color: red; */
61 | border-right: 10px;
62 | border-color: white;
63 | /* z-index: -1000; */
64 | }
65 |
66 | .menuIcons:hover {
67 | background-color: #8383de70;
68 | border-radius: 9px;
69 | }
70 |
71 | #runButt:hover {
72 | /* border: 1.5px solid #685aef !important; */
73 | /* color: #685aef !important; */
74 | filter: brightness(120%);
75 | -webkit-filter: brightness(120%);
76 | }
77 |
78 | #clear-button:hover {
79 | /* border: 1.5px solid #685aef !important; */
80 | /* color: #685aef !important; */
81 | filter: brightness(120%);
82 | -webkit-filter: brightness(120%);
83 | border: 1px solid gray !important;
84 | }
85 |
86 | #nodeButt:hover {
87 | border: 1.2px solid #685aef !important;
88 | color: #685aef !important;
89 | }
90 |
91 | #podButt:hover {
92 | border: 1.3px solid #685aef !important;
93 | color: #685aef !important;
94 | }
95 |
96 | #podsSmallStat {
97 | font-size: 10px;
98 | justify-content: center !important;
99 | text-align: center;
100 | }
101 |
102 | /* #podsSmallStat:hover { */
103 | /* color: white !important; */
104 | /* } */
105 |
106 | #subtitle {
107 | text-shadow: 1px 1px 5px rgb(0, 0, 0, 0.3) !important;
108 | }
109 |
110 | #yaml {
111 | transform: none;
112 | -webkit-transform: scale(0.9);
113 | -moz-transform: scale(0.9);
114 | }
115 |
116 | #terminal {
117 | overflow-anchor: none !important;
118 | }
119 |
120 | .clusterStatusIcons {
121 | transform: scale(6.8);
122 | }
123 |
124 | .clusterLoadingIcon {
125 | transform: scale(9.8);
126 | }
127 |
128 | .button3D-pushable {
129 | position: relative;
130 | border: none;
131 | background: transparent;
132 | padding: 0;
133 | cursor: pointer;
134 | outline-offset: 4px;
135 | transition: filter 250ms;
136 | user-select: none;
137 | -webkit-user-select: none;
138 | touch-action: manipulation;
139 | /* margin: 10px 10px 0 10px */
140 | }
141 |
142 | .button3D-shadow {
143 | position: absolute;
144 | top: 0;
145 | left: 0;
146 | width: 100%;
147 | height: 100%;
148 | border-radius: 7px;
149 | background: hsl(0deg 0% 0% / 0.08);
150 | will-change: transform;
151 | transform: translateY(6px);
152 | transition: transform 600ms cubic-bezier(0.3, 0.7, 0.4, 1);
153 | }
154 |
155 | .button3D-edge {
156 | position: absolute;
157 | top: 0;
158 | left: 0;
159 | width: 100%;
160 | height: 100%;
161 | border-radius: 7px;
162 | /* background: linear-gradient(
163 | to left,
164 | hsl(239, 40%, 25%) 0%,
165 | hsl(239, 40%, 30%) 8%,
166 | hsl(239, 40%, 30%) 92%,
167 | hsl(239, 40%, 25%) 100%
168 | ); */
169 | }
170 |
171 | .button3D-front {
172 | display: block;
173 | position: relative;
174 | padding: 12px 27px;
175 | border-radius: 7px;
176 | /* border: 1px solid rgb(29, 38, 127); */
177 | font-size: 0.9rem;
178 | color: white;
179 | /* background: hsl(239, 38%, 51%); */
180 | will-change: transform;
181 | transform: translateY(-6px);
182 | transition: transform 600ms cubic-bezier(0.3, 0.7, 0.4, 1);
183 | filter: brightness(100%);
184 | -webkit-filter: brightness(100%);
185 | font-family: Roboto;
186 | font-weight: 500;
187 | }
188 |
189 | .button3D-pushable:hover {
190 | filter: brightness(110%);
191 | -webkit-filter: brightness(110%);
192 | }
193 |
194 | .button3D-pushable:hover .button3D-front {
195 | transform: translateY(-4px);
196 | transition: transform 250ms cubic-bezier(0.3, 0.7, 0.4, 1.5);
197 | }
198 |
199 | .button3D-pushable:active .button3D-front {
200 | transform: translateY(-2px);
201 | transition: transform 34ms;
202 | }
203 |
204 | .button3D-pushable:hover .button3D-shadow {
205 | transform: translateY(4px);
206 | transition: transform 250ms cubic-bezier(0.3, 0.7, 0.4, 1.5);
207 | }
208 |
209 | .button3D-pushable:active .button3D-shadow {
210 | transform: translateY(1px);
211 | transition: transform 34ms;
212 | }
213 |
214 | .button3D-pushable:focus:not(:focus-visible) {
215 | outline: none;
216 | }
217 |
218 |
--------------------------------------------------------------------------------
/src/components/Sidebar.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useContext } from "react";
3 | import { IconButton, useTheme } from "@mui/material";
4 | import { Link } from "react-router-dom";
5 | import {
6 | AutoFixHigh,
7 | ExitToAppOutlined,
8 | LightMode,
9 | BarChart,
10 | DarkMode,
11 | } from "@mui/icons-material";
12 | import { ColorModeContext } from "../theme";
13 | import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip";
14 | import { styled } from "@mui/material/styles";
15 | import PrecisionManufacturingIcon from "@mui/icons-material/PrecisionManufacturing";
16 | import TerminalIcon from "@mui/icons-material/Terminal";
17 |
18 | function SideNav(props) {
19 | // Color theme is toggled here with the light/dark mode menu item
20 | const theme = useTheme();
21 | const colorMode = useContext(ColorModeContext);
22 |
23 | const LightTooltip = styled(({ className, ...props }: TooltipProps) => (
24 |
25 | ))(({ theme }) => ({
26 | [`& .${tooltipClasses.tooltip}`]: {
27 | backgroundColor: theme.palette.mode === "dark" ? "#5c4d9a" : "#8383de",
28 | color: "white",
29 | fontSize: 11,
30 | },
31 | }));
32 |
33 | return (
34 |
179 | );
180 | }
181 |
182 | export default SideNav;
183 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import Start from "./Pages/Start.jsx";
2 | import Cluster from "./Pages/Cluster.jsx";
3 | import { HashRouter, Route, Routes } from "react-router-dom";
4 | import Dashboard from "./Pages/Dashboard";
5 | import KlusterManager from "./Pages/Krane";
6 | import Topbar from "./components/Topbar.js";
7 | import Setup from "./Pages/Setup.js";
8 | import { ColorModeContext, useMode } from "./theme.js";
9 | import { CssBaseline, ThemeProvider } from "@mui/material";
10 | import React, { useState } from "react";
11 |
12 | function App() {
13 | const [theme, colorMode] = useMode();
14 |
15 | const [promGrafCheckStatus, setPromGrafCheckStatus] = useState("checking");
16 | const [grafVersion, setGrafVersion] = useState("");
17 | const [promVersion, setPromVersion] = useState("");
18 |
19 | const [podsStatsObj, setPodsStatsObj] = useState({});
20 | const [nodesStatsObj, setNodesStatsObj] = useState({});
21 | const [intervalArray, setIntervalArray] = useState([]);
22 |
23 |
24 | // Hash router is used here to optimize for static file serving from Electron
25 | // More information here: https://reactrouter.com/en/main/router-components/hash-router
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
52 | }
53 | />
54 |
71 | }
72 | />
73 |
91 | }
92 | />
93 |
111 | }
112 | />
113 |
130 | }
131 | />
132 |
133 |
134 |
135 |
136 |
137 |
138 | );
139 | }
140 |
141 | export default App;
142 |
--------------------------------------------------------------------------------
/src/theme.ts:
--------------------------------------------------------------------------------
1 | import { createContext, useState, useMemo } from 'react';
2 | import { createTheme } from '@mui/material/styles';
3 |
4 | // set up color scheme using tailwind shades extension
5 | const tokens = (mode) => ({
6 | ...(mode === 'dark'
7 | ? {
8 | gray: {
9 | 100: '#5c4d9a',
10 | 200: '#5c4d9a',
11 | 300: '#5c4d9a',
12 | 400: '#5c4d9a',
13 | 500: '#5c4d9a',
14 | 600: '#5c4d9a',
15 | 700: '#5c4d9a',
16 | 800: '#5c4d9a',
17 | 900: '#5c4d9a',
18 | },
19 |
20 | // a primary dark color
21 | primary: {
22 | 100: '#d0ced7',
23 | 200: '#a09caf',
24 | 300: '#8f85fb',
25 | 400: 'lightblue',
26 | 500: '#120838',
27 | 600: '#0e062d',
28 | 700: '#0b0522',
29 | 800: '#070316',
30 | 900: '#04020b',
31 | },
32 |
33 | // an accent
34 | greenAccent: {
35 | 100: '#d3f0d8',
36 | 200: '#a7e1b1',
37 | 300: '#7ad18a',
38 | 400: '#4ec263',
39 | 500: '#22b33c',
40 | 600: '#1b8f30',
41 | 700: '#146b24',
42 | 800: '#0e4818',
43 | 900: '#07240c',
44 | },
45 |
46 | // a dff accent
47 | yellowAccent: {
48 | 100: '#f8f7d6',
49 | 200: '#f1eeac',
50 | 300: '#e9e683',
51 | 400: '#e2dd59',
52 | 500: '#dbd530',
53 | 600: '#afaa26',
54 | 700: '#83801d',
55 | 800: '#585513',
56 | 900: '#2c2b0a',
57 | },
58 |
59 | // another accent
60 | pinkAccent: {
61 | 100: '#fbe4f7',
62 | 200: '#f8c9ef',
63 | 300: '#f4afe8',
64 | 400: '#f194e0',
65 | 500: '#ed79d8',
66 | 600: '#be61ad',
67 | 700: '#8e4982',
68 | 800: '#5f3056',
69 | 900: '#2f182b',
70 | },
71 | }
72 | : {
73 | gray: {
74 | 100: '#252627',
75 | 200: '#4a4c4e',
76 | 300: '#6e7274',
77 | 400: '#93989b',
78 | 500: '#b8bec2',
79 | 600: '#c6cbce',
80 | 700: '#d4d8da',
81 | 800: '#e3e5e7',
82 | 900: '#f1f2f3',
83 | },
84 |
85 | // a primary dark color
86 | primary: {
87 | 100: '#595ce3',
88 | 200: '#070316',
89 | 300: '#0b0522',
90 | 400: '#0e062d',
91 | 500: '#120838',
92 | 600: '#413960',
93 | 700: '#716b88',
94 | 800: '#a09caf',
95 | 900: '#d0ced7',
96 | },
97 |
98 | // an accent
99 | greenAccent: {
100 | 100: '#07240c',
101 | 200: '#0e4818',
102 | 300: '#146b24',
103 | 400: '#1b8f30',
104 | 500: '#22b33c',
105 | 600: '#4ec263',
106 | 700: '#7ad18a',
107 | 800: '#a7e1b1',
108 | 900: '#d3f0d8',
109 | },
110 |
111 | // a dff accent
112 | yellowAccent: {
113 | 100: '#2c2b0a',
114 | 200: '#585513',
115 | 300: '#83801d',
116 | 400: '#afaa26',
117 | 500: '#dbd530',
118 | 600: '#e2dd59',
119 | 700: '#e9e683',
120 | 800: '#f1eeac',
121 | 900: '#f8f7d6',
122 | },
123 |
124 | // another accent
125 | pinkAccent: {
126 | 100: '#2f182b',
127 | 200: '#5f3056',
128 | 300: '#8e4982',
129 | 400: '#be61ad',
130 | 500: '#ed79d8',
131 | 600: '#f194e0',
132 | 700: '#f4afe8',
133 | 800: '#f8c9ef',
134 | 900: '#fbe4f7',
135 | },
136 | }),
137 | });
138 |
139 | // mui theme settings
140 | const themeSettings = (mode) => {
141 | const colors = tokens(mode);
142 |
143 | return {
144 | palette: {
145 | mode: mode,
146 | ...(mode === 'dark'
147 | ? {
148 | primary: {
149 | main: colors.primary[300],
150 | },
151 | secondary: {
152 | main: colors.greenAccent[500],
153 | },
154 | neutral: {
155 | dark: colors.gray[700],
156 | main: colors.gray[500],
157 | light: colors.gray[100],
158 | },
159 | background: {
160 | default: colors.primary[500],
161 | },
162 | }
163 | : {
164 | primary: {
165 | main: colors.primary[100],
166 | },
167 | secondary: {
168 | main: colors.primary[500],
169 | },
170 | neutral: {
171 | dark: colors.gray[700],
172 | main: colors.gray[500],
173 | light: colors.gray[100],
174 | },
175 | background: {
176 | default: '#f6f4fe',
177 | },
178 | }),
179 | },
180 | typography: {
181 | fontFamily: ['Roboto', 'sans-serif'].join(','),
182 | fontSize: 12,
183 | h1: {
184 | fontFamily: ['Roboto', 'sans-serif'].join(','),
185 | fontSize: 40,
186 | },
187 | h2: {
188 | fontFamily: ['Roboto', 'sans-serif'].join(','),
189 | fontSize: 32,
190 | },
191 | h3: {
192 | fontFamily: ['Roboto', 'sans-serif'].join(','),
193 | fontSize: 24,
194 | },
195 | h4: {
196 | fontFamily: ['Roboto', 'sans-serif'].join(','),
197 | fontSize: 20,
198 | },
199 | h5: {
200 | fontFamily: ['Roboto', 'sans-serif'].join(','),
201 | fontSize: 16,
202 | },
203 | h6: {
204 | fontFamily: ['Roboto', 'sans-serif'].join(','),
205 | fontSize: 14,
206 | },
207 | },
208 | };
209 | };
210 |
211 | // create a context so that we can have easy access to the condition of whether it's dark or light,
212 | // and allow us to provide the function that changes it
213 | const ColorModeContext = createContext({
214 | toggleColorMode: () => {},
215 | });
216 |
217 | const useMode = () => {
218 | const [mode, setMode] = useState<'light' | 'dark'>('dark');
219 |
220 | const colorMode = useMemo(
221 | () => ({
222 | toggleColorMode: () =>
223 | setMode((prev) => (prev === 'light' ? 'dark' : 'light')),
224 | }),
225 | []
226 | );
227 | // create the theme from material UI and passing mode into our theme setting
228 | const theme = useMemo(() => createTheme(themeSettings(mode)), [mode]);
229 |
230 | return [theme, colorMode] as const;
231 | };
232 |
233 | export { useMode, ColorModeContext, tokens };
234 |
--------------------------------------------------------------------------------
/public/assets/deploy-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
106 |
--------------------------------------------------------------------------------
/src/Pages/assets/deploy-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
106 |
--------------------------------------------------------------------------------
/src/components/assets/deploy-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
106 |
--------------------------------------------------------------------------------
/public/assets/pod.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
117 |
--------------------------------------------------------------------------------
/src/Pages/assets/pod.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
117 |
--------------------------------------------------------------------------------
/src/components/assets/pod.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
117 |
--------------------------------------------------------------------------------
/public/assets/rs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
116 |
--------------------------------------------------------------------------------
/src/Pages/assets/rs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
116 |
--------------------------------------------------------------------------------
/src/components/assets/rs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
116 |
--------------------------------------------------------------------------------
/src/components/DashboardCommandLine.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { InputAdornment, Button, TextField, useTheme } from "@mui/material";
3 | import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip";
4 | import { styled, lighten, darken } from "@mui/system";
5 | // import { clipboard } from 'electron';
6 |
7 | const LightTooltip = styled(({ className, ...props }: TooltipProps) => (
8 |
9 | ))(({ theme }) => ({
10 | [`& .${tooltipClasses.tooltip}`]: {
11 | backgroundColor: theme.palette.mode === "dark" ? "#5c4d9a" : "#8383de",
12 | color: "white",
13 | fontSize: 11,
14 | },
15 | }));
16 |
17 | const DashboardCommandLine = (props) => {
18 | const theme = useTheme();
19 |
20 | // Add/remove functionality in text box
21 | const handleChange = (e) => {
22 | let newUserInput = "";
23 |
24 | if (e.nativeEvent.inputType === "deleteContentBackward") {
25 | newUserInput = props.userInput.slice(0, props.userInput.length - 1);
26 | } else {
27 | newUserInput =
28 | props.userInput + e.target.value[e.target.value.length - 1];
29 | }
30 | props.setUserInput(newUserInput);
31 | };
32 |
33 | const handleClear = (e) => {
34 | let userInput2 = "";
35 |
36 | props.setUserInput(userInput2);
37 | props.setVerb(userInput2);
38 | props.setType(userInput2);
39 | props.setName(userInput2);
40 | props.setFlags([]);
41 | };
42 |
43 | return (
44 |
54 |
62 |
70 |
82 |
91 | Working Directory
92 |
93 |
124 |
125 |
126 |
127 |
135 |
147 |
159 |
168 | Input Command
169 |
170 |
251 |
252 |
253 | );
254 | };
255 |
256 | export default DashboardCommandLine;
257 |
--------------------------------------------------------------------------------
/public/assets/node.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
98 |
--------------------------------------------------------------------------------
/src/Pages/assets/node.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
98 |
--------------------------------------------------------------------------------
/src/components/assets/node.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
98 |
--------------------------------------------------------------------------------
/src/components/PodCpuChart.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useCallback, useEffect } from "react";
2 | import { AreaClosed, Line, Bar } from "@visx/shape";
3 | import { curveMonotoneX } from "@visx/curve";
4 | import { GridRows, GridColumns } from "@visx/grid";
5 | import { scaleTime, scaleLinear } from "@visx/scale";
6 | import {
7 | withTooltip,
8 | Tooltip,
9 | TooltipWithBounds,
10 | defaultStyles,
11 | } from "@visx/tooltip";
12 | import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip";
13 | import { localPoint } from "@visx/event";
14 | import { LinearGradient } from "@visx/gradient";
15 | import { max, extent, bisector } from "@visx/vendor/d3-array";
16 | import { timeFormat } from "@visx/vendor/d3-time-format";
17 | import { useTheme } from "@mui/material";
18 |
19 | interface podStats {
20 | date: string;
21 | cpu: number;
22 | memory: number;
23 | memoryDisplay: string;
24 | }
25 |
26 | type TooltipData = podStats;
27 |
28 | // util
29 | const formatDate = timeFormat("%m/%d/%y @ %H:%M:%S");
30 |
31 | // accessors
32 | const getDate = (d: podStats) => new Date(d.date);
33 | const getCpuValue = (d: podStats) => d.cpu;
34 | const bisectDate = bisector((d) => new Date(d.date)).left;
35 |
36 | export type AreaProps = {
37 | podsStatsObj: any;
38 | selectedPod: any;
39 | width: number;
40 | height: number;
41 | margin?: { top: number; right: number; bottom: number; left: number };
42 | };
43 |
44 | export default withTooltip(
45 | ({
46 | podsStatsObj,
47 | selectedPod,
48 | width,
49 | height,
50 | margin = { top: 0, right: 0, bottom: 0, left: 0 },
51 | showTooltip,
52 | hideTooltip,
53 | tooltipData,
54 | tooltipTop = 0,
55 | tooltipLeft = 0,
56 | }: AreaProps & WithTooltipProvidedProps) => {
57 | if (width < 10) return null;
58 |
59 | let selectedPodStats = podsStatsObj[`${selectedPod[0]["name"]}`];
60 |
61 | const theme = useTheme();
62 |
63 | const background = theme.palette.mode === "dark" ? "#0e0727" : "#eeebfb";
64 | const background2 = theme.palette.mode === "dark" ? "#120838" : "#eeebfb";
65 | const accentColor =
66 | theme.palette.mode === "dark" ? "white" : "#7b76c2"
67 | const textColor = theme.palette.mode === "dark" ? "white" : "grey";
68 |
69 | const tooltipStyles = {
70 | ...defaultStyles,
71 | background,
72 | border: "1px solid white",
73 | color: textColor,
74 | };
75 |
76 | // bounds
77 | const innerWidth = width - margin.left - margin.right;
78 | const innerHeight = height - margin.top - margin.bottom;
79 |
80 | // scales
81 | const dateScale = useMemo(
82 | () =>
83 | scaleTime({
84 | range: [margin.left, innerWidth + margin.left],
85 | domain: extent(selectedPodStats, getDate) as [Date, Date],
86 | }),
87 | [innerWidth, margin.left]
88 | );
89 | const CpuValueScale = useMemo(
90 | () =>
91 | scaleLinear({
92 | range: [innerHeight + margin.top, margin.top],
93 | domain: [
94 | -20,
95 | (max(selectedPodStats, getCpuValue) || 0) + innerHeight / 3,
96 | ],
97 | nice: true,
98 | }),
99 | [margin.top, innerHeight]
100 | );
101 |
102 | // tooltip handler
103 | const handleTooltip = useCallback(
104 | (
105 | event:
106 | | React.TouchEvent
107 | | React.MouseEvent
108 | ) => {
109 | const { x } = localPoint(event) || { x: 0 };
110 | const x0 = dateScale.invert(x);
111 | const index = bisectDate(selectedPodStats, x0, 1);
112 | const d0 = selectedPodStats[index - 1];
113 | const d1 = selectedPodStats[index];
114 | let d = d0;
115 | if (d1 && getDate(d1)) {
116 | d =
117 | x0.valueOf() - getDate(d0).valueOf() >
118 | getDate(d1).valueOf() - x0.valueOf()
119 | ? d1
120 | : d0;
121 | }
122 | showTooltip({
123 | tooltipData: d,
124 | tooltipLeft: x,
125 | tooltipTop: CpuValueScale(getCpuValue(d)),
126 | });
127 | },
128 | [showTooltip, CpuValueScale, dateScale]
129 | );
130 |
131 | return (
132 |
133 |
227 | {tooltipData && (
228 |
229 |
241 | {`${getCpuValue(tooltipData)}m`}
242 |
243 |
260 | {formatDate(getDate(tooltipData))}
261 |
262 |
263 | )}
264 |
265 | );
266 | }
267 | );
268 |
--------------------------------------------------------------------------------
/src/components/NodeCpuChart.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useCallback, useEffect } from "react";
2 | import { AreaClosed, Line, Bar } from "@visx/shape";
3 | import { curveMonotoneX } from "@visx/curve";
4 | import { GridRows, GridColumns } from "@visx/grid";
5 | import { scaleTime, scaleLinear } from "@visx/scale";
6 | import {
7 | withTooltip,
8 | Tooltip,
9 | TooltipWithBounds,
10 | defaultStyles,
11 | } from "@visx/tooltip";
12 | import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip";
13 | import { localPoint } from "@visx/event";
14 | import { LinearGradient } from "@visx/gradient";
15 | import { max, extent, bisector } from "@visx/vendor/d3-array";
16 | import { timeFormat } from "@visx/vendor/d3-time-format";
17 | import { useTheme } from "@mui/material";
18 |
19 | interface nodeStats {
20 | date: string;
21 | cpu: number;
22 | memory: number;
23 | memoryDisplay: string;
24 | }
25 |
26 | type TooltipData = nodeStats;
27 |
28 | // util
29 | const formatDate = timeFormat("%m/%d/%y @ %H:%M:%S");
30 |
31 | // accessors
32 | const getDate = (d: nodeStats) => new Date(d.date);
33 | const getCpuValue = (d: nodeStats) => d.cpu;
34 | const bisectDate = bisector((d) => new Date(d.date)).left;
35 |
36 | export type AreaProps = {
37 | nodesStatsObj: any;
38 | selectedNode: any;
39 | width: number;
40 | height: number;
41 | margin?: { top: number; right: number; bottom: number; left: number };
42 | };
43 |
44 | export default withTooltip(
45 | ({
46 | nodesStatsObj,
47 | selectedNode,
48 | width,
49 | height,
50 | margin = { top: 0, right: 0, bottom: 0, left: 0 },
51 | showTooltip,
52 | hideTooltip,
53 | tooltipData,
54 | tooltipTop = 0,
55 | tooltipLeft = 0,
56 | }: AreaProps & WithTooltipProvidedProps) => {
57 | if (width < 10) return null;
58 |
59 | let selectedNodeStats = nodesStatsObj[`${selectedNode[0]["name"]}`];
60 |
61 | const theme = useTheme();
62 |
63 | const background = theme.palette.mode === "dark" ? "#0e0727" : "#eeebfb";
64 | const background2 = theme.palette.mode === "dark" ? "#120838" : "#eeebfb";
65 | const accentColor = theme.palette.mode === "dark" ? "white" : "#7b76c2"
66 | const textColor = theme.palette.mode === "dark" ? "white" : "grey";
67 |
68 | const tooltipStyles = {
69 | ...defaultStyles,
70 | background,
71 | border: "1px solid white",
72 | color: textColor,
73 | };
74 |
75 | // bounds
76 | const innerWidth = width - margin.left - margin.right;
77 | const innerHeight = height - margin.top - margin.bottom;
78 |
79 | // scales
80 | const dateScale = useMemo(
81 | () =>
82 | scaleTime({
83 | range: [margin.left, innerWidth + margin.left],
84 | domain: extent(selectedNodeStats, getDate) as [Date, Date],
85 | }),
86 | [innerWidth, margin.left]
87 | );
88 | const CpuValueScale = useMemo(
89 | () =>
90 | scaleLinear({
91 | range: [innerHeight + margin.top, margin.top],
92 | domain: [
93 | -5,
94 | //@ts-ignore
95 | (max(selectedNodeStats, getCpuValue) + 200 || 0) + innerHeight / 3,
96 | ],
97 | nice: true,
98 | }),
99 | [margin.top, innerHeight]
100 | );
101 |
102 | // tooltip handler
103 | const handleTooltip = useCallback(
104 | (
105 | event:
106 | | React.TouchEvent
107 | | React.MouseEvent
108 | ) => {
109 | const { x } = localPoint(event) || { x: 0 };
110 | const x0 = dateScale.invert(x);
111 | const index = bisectDate(selectedNodeStats, x0, 1);
112 | const d0 = selectedNodeStats[index - 1];
113 | const d1 = selectedNodeStats[index];
114 | let d = d0;
115 | if (d1 && getDate(d1)) {
116 | d =
117 | x0.valueOf() - getDate(d0).valueOf() >
118 | getDate(d1).valueOf() - x0.valueOf()
119 | ? d1
120 | : d0;
121 | }
122 | showTooltip({
123 | tooltipData: d,
124 | tooltipLeft: x,
125 | tooltipTop: CpuValueScale(getCpuValue(d)),
126 | });
127 | },
128 | [showTooltip, CpuValueScale, dateScale]
129 | );
130 |
131 | return (
132 |
133 |
227 | {tooltipData && (
228 |
229 |
241 | {`${getCpuValue(tooltipData)}m`}
242 |
243 |
260 | {formatDate(getDate(tooltipData))}
261 |
262 |
263 | )}
264 |
265 | );
266 | }
267 | );
268 |
--------------------------------------------------------------------------------
/src/components/PodMemoryChart.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useCallback, useEffect } from "react";
2 | import { AreaClosed, Line, Bar } from "@visx/shape";
3 | import { curveMonotoneX } from "@visx/curve";
4 | import { GridRows, GridColumns } from "@visx/grid";
5 | import { scaleTime, scaleLinear } from "@visx/scale";
6 | import {
7 | withTooltip,
8 | Tooltip,
9 | TooltipWithBounds,
10 | defaultStyles,
11 | } from "@visx/tooltip";
12 | import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip";
13 | import { localPoint } from "@visx/event";
14 | import { LinearGradient } from "@visx/gradient";
15 | import { max, extent, bisector } from "@visx/vendor/d3-array";
16 | import { timeFormat } from "@visx/vendor/d3-time-format";
17 | import { useTheme } from "@mui/material";
18 |
19 | interface podStats {
20 | date: string;
21 | cpu: number;
22 | memory: number;
23 | memoryDisplay: string;
24 | }
25 |
26 | type TooltipData = podStats;
27 |
28 | // util
29 | const formatDate = timeFormat("%m/%d/%y @ %H:%M:%S");
30 |
31 | // accessors
32 | const getDate = (d: podStats) => new Date(d.date);
33 | const getMemoryValue = (d: podStats) => d.memory;
34 | const getMemoryDisplayValue = (d: podStats) => d.memoryDisplay;
35 | const bisectDate = bisector((d) => new Date(d.date)).left;
36 |
37 | export type AreaProps = {
38 | podsStatsObj: any;
39 | selectedPod: any;
40 | width: number;
41 | height: number;
42 | margin?: { top: number; right: number; bottom: number; left: number };
43 | };
44 |
45 | export default withTooltip(
46 | ({
47 | podsStatsObj,
48 | selectedPod,
49 | width,
50 | height,
51 | margin = { top: 0, right: 0, bottom: 0, left: 0 },
52 | showTooltip,
53 | hideTooltip,
54 | tooltipData,
55 | tooltipTop = 0,
56 | tooltipLeft = 0,
57 | }: AreaProps & WithTooltipProvidedProps) => {
58 | if (width < 10) return null;
59 |
60 | const theme = useTheme();
61 |
62 | const background = theme.palette.mode === "dark" ? "#0e0727" : "#eeebfb";
63 | const background2 = theme.palette.mode === "dark" ? "#120838" : "#eeebfb";
64 | const accentColor = theme.palette.mode === "dark" ? "white" : "#7b76c2"
65 | const textColor = theme.palette.mode === "dark" ? "white" : "grey";
66 |
67 | const tooltipStyles = {
68 | ...defaultStyles,
69 | background,
70 | border: "1px solid white",
71 | color: textColor,
72 | };
73 |
74 | // bounds
75 | const innerWidth = width - margin.left - margin.right;
76 | const innerHeight = height - margin.top - margin.bottom;
77 | let selectedPodStats = podsStatsObj[`${selectedPod[0]["name"]}`];
78 |
79 | // scales
80 | const dateScale = useMemo(
81 | () =>
82 | scaleTime({
83 | range: [margin.left, innerWidth + margin.left],
84 | domain: extent(selectedPodStats, getDate) as [Date, Date],
85 | }),
86 | [innerWidth, margin.left]
87 | );
88 | const memoryValueScale = useMemo(
89 | () =>
90 | scaleLinear({
91 | range: [innerHeight + margin.top, margin.top],
92 | domain: [
93 | -5,
94 | (max(selectedPodStats, getMemoryValue) || 0) + innerHeight / 0.001,
95 | ],
96 | nice: true,
97 | }),
98 | [margin.top, innerHeight]
99 | );
100 |
101 | // tooltip handler
102 | const handleTooltip = useCallback(
103 | (
104 | event:
105 | | React.TouchEvent
106 | | React.MouseEvent
107 | ) => {
108 | const { x } = localPoint(event) || { x: 0 };
109 | const x0 = dateScale.invert(x);
110 | const index = bisectDate(selectedPodStats, x0, 1);
111 | const d0 = selectedPodStats[index - 1];
112 | const d1 = selectedPodStats[index];
113 | let d = d0;
114 | if (d1 && getDate(d1)) {
115 | d =
116 | x0.valueOf() - getDate(d0).valueOf() >
117 | getDate(d1).valueOf() - x0.valueOf()
118 | ? d1
119 | : d0;
120 | }
121 | showTooltip({
122 | tooltipData: d,
123 | tooltipLeft: x,
124 | tooltipTop: memoryValueScale(getMemoryValue(d)),
125 | });
126 | },
127 | [showTooltip, memoryValueScale, dateScale]
128 | );
129 |
130 | return (
131 |
132 |
226 | {tooltipData && (
227 |
228 |
240 | {`${getMemoryDisplayValue(tooltipData)}`}
241 |
242 |
259 | {formatDate(getDate(tooltipData))}
260 |
261 |
262 | )}
263 |
264 | );
265 | }
266 | );
267 |
--------------------------------------------------------------------------------
/src/components/NodeMemoryChart.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useCallback, useEffect } from "react";
2 | import { AreaClosed, Line, Bar } from "@visx/shape";
3 | import { curveMonotoneX } from "@visx/curve";
4 | import { GridRows, GridColumns } from "@visx/grid";
5 | import { scaleTime, scaleLinear } from "@visx/scale";
6 | import {
7 | withTooltip,
8 | Tooltip,
9 | TooltipWithBounds,
10 | defaultStyles,
11 | } from "@visx/tooltip";
12 | import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip";
13 | import { localPoint } from "@visx/event";
14 | import { LinearGradient } from "@visx/gradient";
15 | import { max, extent, bisector } from "@visx/vendor/d3-array";
16 | import { timeFormat } from "@visx/vendor/d3-time-format";
17 | import { useTheme } from "@mui/material";
18 |
19 | interface nodeStats {
20 | date: string;
21 | cpu: number;
22 | memory: number;
23 | memoryDisplay: string;
24 | }
25 |
26 | type TooltipData = nodeStats;
27 |
28 | // util
29 | const formatDate = timeFormat("%m/%d/%y @ %H:%M:%S");
30 |
31 | // accessors
32 | const getDate = (d: nodeStats) => new Date(d.date);
33 | const getMemoryValue = (d: nodeStats) => d.memory;
34 | const getMemoryDisplayValue = (d: nodeStats) => d.memoryDisplay;
35 | const bisectDate = bisector((d) => new Date(d.date)).left;
36 |
37 | export type AreaProps = {
38 | nodesStatsObj: any;
39 | selectedNode: any;
40 | width: number;
41 | height: number;
42 | margin?: { top: number; right: number; bottom: number; left: number };
43 | };
44 |
45 | export default withTooltip(
46 | ({
47 | nodesStatsObj,
48 | selectedNode,
49 | width,
50 | height,
51 | margin = { top: 0, right: 0, bottom: 0, left: 0 },
52 | showTooltip,
53 | hideTooltip,
54 | tooltipData,
55 | tooltipTop = 0,
56 | tooltipLeft = 0,
57 | }: AreaProps & WithTooltipProvidedProps) => {
58 | if (width < 10) return null;
59 |
60 | const theme = useTheme();
61 |
62 | const background = theme.palette.mode === "dark" ? "#0e0727" : "#eeebfb";
63 | const background2 = theme.palette.mode === "dark" ? "#120838" : "#eeebfb";
64 | const accentColorMemory = theme.palette.mode === "dark" ? "white" : "#7b76c2"
65 | const textColor = theme.palette.mode === "dark" ? "white" : "grey";
66 |
67 | const tooltipStyles = {
68 | ...defaultStyles,
69 | background,
70 | border: "1px solid white",
71 | color: textColor,
72 | };
73 |
74 | // bounds
75 | const innerWidth = width - margin.left - margin.right;
76 | const innerHeight = height - margin.top - margin.bottom;
77 | let selectedNodeStats = nodesStatsObj[`${selectedNode[0]["name"]}`];
78 |
79 | // scales
80 | const dateScale = useMemo(
81 | () =>
82 | scaleTime({
83 | range: [margin.left, innerWidth + margin.left],
84 | domain: extent(selectedNodeStats, getDate) as [Date, Date],
85 | }),
86 | [innerWidth, margin.left]
87 | );
88 | const memoryValueScale = useMemo(
89 | () =>
90 | scaleLinear({
91 | range: [innerHeight + margin.top, margin.top],
92 | domain: [
93 | -5,
94 | (max(selectedNodeStats, getMemoryValue) || 0) +
95 | innerHeight / 0.00005,
96 | ],
97 | nice: true,
98 | }),
99 | [margin.top, innerHeight]
100 | );
101 |
102 | // tooltip handler
103 | const handleTooltip = useCallback(
104 | (
105 | event:
106 | | React.TouchEvent
107 | | React.MouseEvent
108 | ) => {
109 | const { x } = localPoint(event) || { x: 0 };
110 | const x0 = dateScale.invert(x);
111 | const index = bisectDate(selectedNodeStats, x0, 1);
112 | const d0 = selectedNodeStats[index - 1];
113 | const d1 = selectedNodeStats[index];
114 | let d = d0;
115 | if (d1 && getDate(d1)) {
116 | d =
117 | x0.valueOf() - getDate(d0).valueOf() >
118 | getDate(d1).valueOf() - x0.valueOf()
119 | ? d1
120 | : d0;
121 | }
122 | showTooltip({
123 | tooltipData: d,
124 | tooltipLeft: x,
125 | tooltipTop: memoryValueScale(getMemoryValue(d)),
126 | });
127 | },
128 | [showTooltip, memoryValueScale, dateScale]
129 | );
130 |
131 | return (
132 |
133 |
227 | {tooltipData && (
228 |
229 | 90
250 | // ? "#cf4848"
251 | // : "yellow",
252 | }}
253 | >
254 | {`${getMemoryDisplayValue(tooltipData)}`}
255 |
256 |
273 | {formatDate(getDate(tooltipData))}
274 |
275 |
276 | )}
277 |
278 | );
279 | }
280 | );
281 |
--------------------------------------------------------------------------------
/public/assets/container.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
126 |
--------------------------------------------------------------------------------
/src/Pages/assets/container.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
126 |
--------------------------------------------------------------------------------