diff --git a/ui/package-lock.json b/ui/package-lock.json
index 02095f1..38c278f 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -41,7 +41,8 @@
"eslint": "^8.57.1",
"jest": "^29.1.2",
"typescript": "^4.8.3",
- "vite": "^6.3.5"
+ "vite": "^6.3.5",
+ "vite-plugin-svgr": "^4.3.0"
},
"engines": {
"node": ">=22.0.0"
@@ -2412,6 +2413,42 @@
"node": ">=14.0.0"
}
},
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
+ "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.40.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz",
@@ -2776,6 +2813,291 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/@svgr/babel-plugin-add-jsx-attribute": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
+ "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
+ "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
+ "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
+ "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-svg-dynamic-title": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
+ "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-svg-em-dimensions": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
+ "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-transform-react-native-svg": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz",
+ "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-transform-svg-component": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
+ "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-preset": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz",
+ "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
+ "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
+ "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
+ "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
+ "@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
+ "@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
+ "@svgr/babel-plugin-transform-react-native-svg": "8.1.0",
+ "@svgr/babel-plugin-transform-svg-component": "8.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/core": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
+ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.21.3",
+ "@svgr/babel-preset": "8.1.0",
+ "camelcase": "^6.2.0",
+ "cosmiconfig": "^8.1.3",
+ "snake-case": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/@svgr/core/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/@svgr/core/node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@svgr/core/node_modules/cosmiconfig": {
+ "version": "8.3.6",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+ "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0",
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@svgr/core/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@svgr/hast-util-to-babel-ast": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
+ "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.21.3",
+ "entities": "^4.4.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/@svgr/plugin-jsx": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz",
+ "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.21.3",
+ "@svgr/babel-preset": "8.1.0",
+ "@svgr/hast-util-to-babel-ast": "8.0.0",
+ "svg-parser": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@svgr/core": "*"
+ }
+ },
"node_modules/@tokenizer/inflate": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz",
@@ -4247,6 +4569,17 @@
"csstype": "^3.0.2"
}
},
+ "node_modules/dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -4286,6 +4619,19 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -4626,6 +4972,13 @@
"node": ">=4.0"
}
},
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -6372,6 +6725,16 @@
"loose-envify": "cli.js"
}
},
+ "node_modules/lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -6632,6 +6995,17 @@
"node": ">= 0.4.0"
}
},
+ "node_modules/no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -7621,6 +7995,17 @@
"npm": ">= 3.0.0"
}
},
+ "node_modules/snake-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
+ "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/socks": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz",
@@ -7880,6 +8265,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/svg-parser": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
+ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/tar": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
@@ -8312,6 +8704,21 @@
}
}
},
+ "node_modules/vite-plugin-svgr": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz",
+ "integrity": "sha512-Jy9qLB2/PyWklpYy0xk0UU3TlU0t2UMpJXZvf+hWII1lAmRHrOUKi11Uw8N3rxoNk7atZNYO3pR3vI1f7oi+6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^5.1.3",
+ "@svgr/core": "^8.1.0",
+ "@svgr/plugin-jsx": "^8.1.0"
+ },
+ "peerDependencies": {
+ "vite": ">=2.6.0"
+ }
+ },
"node_modules/vite/node_modules/fdir": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
diff --git a/ui/package.json b/ui/package.json
index d9bd809..ac94b6c 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -48,6 +48,7 @@
"eslint": "^8.57.1",
"jest": "^29.1.2",
"typescript": "^4.8.3",
- "vite": "^6.3.5"
+ "vite": "^6.3.5",
+ "vite-plugin-svgr": "^4.3.0"
}
}
diff --git a/ui/src/components/GitBranch/GitBranch.tsx b/ui/src/components/GitBranch/GitBranch.tsx
new file mode 100644
index 0000000..1b294b7
--- /dev/null
+++ b/ui/src/components/GitBranch/GitBranch.tsx
@@ -0,0 +1,6 @@
+import GitBranchSVG from './git-branch.svg?react'
+import { SvgIcon, SvgIconProps } from '@mui/material'
+
+export default function GitBranch(props: SvgIconProps) {
+ return
+}
diff --git a/ui/src/components/GitBranch/git-branch.svg b/ui/src/components/GitBranch/git-branch.svg
new file mode 100644
index 0000000..29b9a6d
--- /dev/null
+++ b/ui/src/components/GitBranch/git-branch.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/ui/src/pages/ApplicationDetailsPage/components/BranchesList/BranchCard.tsx b/ui/src/pages/ApplicationDetailsPage/components/BranchesList/BranchCard.tsx
new file mode 100644
index 0000000..138d9cc
--- /dev/null
+++ b/ui/src/pages/ApplicationDetailsPage/components/BranchesList/BranchCard.tsx
@@ -0,0 +1,77 @@
+import { Card, CardActionArea, CardContent, Skeleton, Stack, Typography } from '@mui/material'
+import { ArtifactListItemReducedDTO } from '../../../../../autogenerated/client/backend'
+import { useNavigate } from 'react-router-dom'
+import moment from 'moment'
+import { Commit, LayersOutlined, SvgIconComponent, SyncOutlined } from '@mui/icons-material'
+import GitBranch from '../../../../components/GitBranch/GitBranch'
+import InstallDialog from '../InstallDialog'
+import { useState } from 'react'
+import HintIcon, { LoadingHintIcon } from './HintIcon'
+
+export default function BranchCard({ branch, version, artifact }:
+{ branch: { name: string, pattern: string }, version: string, artifact: ArtifactListItemReducedDTO }) {
+ const [ installDialogOpen, setInstallDialogOpen ] = useState(false)
+
+ const navigate = useNavigate()
+
+ function onInstall() {
+ setInstallDialogOpen(false)
+ navigate('/workloads')
+ }
+
+ return (
+ <>
+
+ setInstallDialogOpen(true) }>
+
+
+
+ Version { version }
+
+
+
+
+
+
+
+
+
+
+
+ setInstallDialogOpen(false) }/>
+ >
+ )
+}
+
+export function LoadingBranchCard() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/ui/src/pages/ApplicationDetailsPage/components/BranchesList/HintIcon.tsx b/ui/src/pages/ApplicationDetailsPage/components/BranchesList/HintIcon.tsx
new file mode 100644
index 0000000..baefc39
--- /dev/null
+++ b/ui/src/pages/ApplicationDetailsPage/components/BranchesList/HintIcon.tsx
@@ -0,0 +1,28 @@
+import { SvgIconComponent } from '@mui/icons-material'
+import { PopperProps, Tooltip, Stack, SvgIcon, Typography, Skeleton } from '@mui/material'
+
+export default function HintIcon({ tooltip, icon, text }: { tooltip: string, icon: SvgIconComponent, text: string }) {
+ const popperProps: Partial = {
+ modifiers: [
+ {
+ name: 'offset',
+ options: {
+ offset: [0, -4]
+ }
+ }
+ ]
+ }
+
+ return (
+
+
+
+ { text }
+
+
+ )
+}
+
+export function LoadingHintIcon({ width = 70 }: { width?: number }) {
+ return
+}
diff --git a/ui/src/pages/ApplicationDetailsPage/components/BranchesList/index.tsx b/ui/src/pages/ApplicationDetailsPage/components/BranchesList/index.tsx
new file mode 100644
index 0000000..48710eb
--- /dev/null
+++ b/ui/src/pages/ApplicationDetailsPage/components/BranchesList/index.tsx
@@ -0,0 +1,61 @@
+import { Stack, Typography } from '@mui/material'
+import { ApplicationDTOPackagingFormatEnum, BranchDTO, VersionDTO } from '../../../../../autogenerated/client/backend'
+import { useEffect } from 'react'
+import { checkKubernetes } from '../../../../clients/kubectl'
+import { createDockerDesktopClient } from '@docker/extension-api-client'
+import { enqueueSnackbar } from 'notistack'
+import { checkDocker } from '../../../../clients/docker'
+import { sortArtifacts } from '../../../../clients/util'
+import BranchCard from './BranchCard'
+
+const ddClient = createDockerDesktopClient()
+
+export default function BranchesList({ branches, packagingFormat }: { branches: BranchDTO[], packagingFormat?: ApplicationDTOPackagingFormatEnum }) {
+ if (!packagingFormat) {
+ return This application cannot be deployed yet
+ }
+
+ useEffect(() => {
+ switch(packagingFormat) {
+ case 'HELM_CHART':
+ checkKubernetes(ddClient)
+ .catch(e => {
+ console.error(e)
+ enqueueSnackbar('Error with kubernetes, make sure the cluster is up and reachable')
+ })
+ break
+ case 'CONTAINER':
+ checkDocker(ddClient)
+ .catch(e => {
+ console.error(e)
+ enqueueSnackbar('Error with docker engine, make sure it is running')
+ })
+ break
+ }
+ }, [])
+
+ return (
+
+ {
+ branches
+ .filter(branch => branch.versions && branch.versions.length > 0 && branch.versions.find(v => v.artifacts.find(a => a.packaging_format === packagingFormat)))
+ .flatMap(branch => {
+ const version = (branch.versions as VersionDTO[]).find(v => v.artifacts.sort(sortArtifacts).find(a => a.packaging_format === packagingFormat)) as VersionDTO
+ return {
+ branch: { name: branch.branch_name, pattern: branch.branch_pattern },
+ version: version.version_number,
+ artifact: version.artifacts.reverse().find(a => a.packaging_format === packagingFormat)
+ }
+ })
+ .filter(({ artifact }) => artifact !== undefined)
+ .map(({ branch, version, artifact }) => artifact &&
+
+ )
+ }
+
+ )
+}
\ No newline at end of file
diff --git a/ui/src/pages/ApplicationDetailsPage/components/BranchesTable.tsx b/ui/src/pages/ApplicationDetailsPage/components/BranchesTable.tsx
deleted file mode 100644
index 3f92c9b..0000000
--- a/ui/src/pages/ApplicationDetailsPage/components/BranchesTable.tsx
+++ /dev/null
@@ -1,190 +0,0 @@
-import { createDockerDesktopClient } from '@docker/extension-api-client'
-import { ApplicationDTOPackagingFormatEnum, ArtifactListItemReducedDTO, BranchDTO, VersionDTO } from '../../../../autogenerated/client/backend'
-import { Box, CircularProgress, IconButton, Skeleton, Stack, Table, TableBody, TableCell, TableHead, TableRow, Tooltip, Typography } from '@mui/material'
-import { PlayCircleOutlined, SettingsOutlined, UpdateOutlined, WarningAmberOutlined } from '@mui/icons-material'
-import { useEffect, useState } from 'react'
-import { findHelmChart, HelmListItem, upgradeHelmChart, WorkloadStatus } from '../../../clients/helm'
-import { compareVersions, sortArtifacts } from '../../../clients/util'
-import moment from 'moment'
-import InstallDialog from './InstallDialog'
-import { checkKubernetes } from '../../../clients/kubectl'
-import { enqueueSnackbar } from 'notistack'
-import { checkDocker } from '../../../clients/docker'
-import { useNavigate } from 'react-router-dom'
-
-const ddClient = createDockerDesktopClient()
-
-export default function BranchesTable({ branches, packagingFormat }: { branches: BranchDTO[], packagingFormat?: ApplicationDTOPackagingFormatEnum }) {
- if (!packagingFormat) {
- return This application cannot be deployed yet
- }
-
- useEffect(() => {
- switch(packagingFormat) {
- case 'HELM_CHART':
- checkKubernetes(ddClient)
- .catch(e => {
- console.error(e)
- enqueueSnackbar('Error with kubernetes, make sure the cluster is up and reachable')
- })
- break
- case 'CONTAINER':
- checkDocker(ddClient)
- .catch(e => {
- console.error(e)
- enqueueSnackbar('Error with docker engine, make sure it is running')
- })
- break
- }
- }, [])
-
- return (
-
-
-
- Version
- Revision
- App Version
- Registered
- Digest
- Actions
-
-
-
- {
- branches
- .filter(branch => branch.versions && branch.versions.length > 0 && branch.versions.find(v => v.artifacts.find(a => a.packaging_format === packagingFormat)))
- .flatMap(branch => {
- const version = (branch.versions as VersionDTO[]).find(v => v.artifacts.sort(sortArtifacts).find(a => a.packaging_format === packagingFormat)) as VersionDTO
- return {
- branch: { name: branch.branch_name, pattern: branch.branch_pattern },
- version: version.version_number,
- artifact: version.artifacts.reverse().find(a => a.packaging_format === packagingFormat)
- }
- })
- .filter(({ artifact }) => artifact !== undefined)
- .map(({ branch, version, artifact }) => artifact &&
-
- )
- }
-
-
- )
-}
-
-function BranchRow({ branch, version, artifact }: { branch: { name: string, pattern: string }, version: string, artifact: ArtifactListItemReducedDTO }) {
- const [ runningInstance, setRunningInstance ] = useState()
- const [ status, setStatus ] = useState()
- const [ error, setError ] = useState()
- const [ installDialogOpen, setInstallDialogOpen ] = useState(false)
-
- const navigate = useNavigate()
-
- useEffect(() => {
- switch (artifact.packaging_format) {
- case 'HELM_CHART':
- findHelmChart(ddClient, artifact.name.split(':')[0], new RegExp(branch.pattern))
- .then(release => {
- if (release) {
- setRunningInstance(release)
- setStatus(release.status)
- } else {
- setStatus(WorkloadStatus.NotRunning)
- }
- })
- .catch(e => {
- setStatus(WorkloadStatus.Error)
- setError(error)
- })
- break
- case 'CONTAINER':
- case 'RPM':
- default:
- }
- }, [])
-
- function upgrade() {
- setStatus(WorkloadStatus.Loading)
- switch (artifact.packaging_format) {
- case 'HELM_CHART':
- if (runningInstance) {
- upgradeHelmChart(ddClient, artifact, runningInstance)
- .then(result => {
- setRunningInstance(result)
- setStatus(result.status)
- })
- .catch(e => {
- setStatus(WorkloadStatus.Error)
- setError(e)
- })
- }
- case 'RPM':
- case 'CONTAINER':
- }
- }
-
- return (
- <>
-
- { artifact?.version }
- { artifact?.revision }
- { version }
- { moment(artifact.registered_at).fromNow() }
- { artifact?.digest.value.substring(0, 7) }
-
- {
- status ?
-
- { status === WorkloadStatus.NotRunning &&
-
- setInstallDialogOpen(true) } sx={ { p: 0 } }>
-
- }
- { status === WorkloadStatus.Running &&
-
- navigate('/workloads') } sx={ { p: 0 } }>
-
- }
- { status === WorkloadStatus.Running && runningInstance && compareVersions(artifact.version as string, runningInstance.version as string) != 0 &&
-
- upgrade() } sx={ { p: 0 } }>
-
- }
- {
- status === WorkloadStatus.Error &&
-
-
-
- }
- {
- status === WorkloadStatus.Loading &&
-
-
-
- }
-
- :
-
- }
-
-
- { status === WorkloadStatus.NotRunning &&
- {
- setRunningInstance(result)
- setStatus(result.status)
- setInstallDialogOpen(false)
- navigate('/workloads')
- } }
- onDismiss={ () => setInstallDialogOpen(false) }/> }
- >
- )
-}
\ No newline at end of file
diff --git a/ui/src/pages/ApplicationDetailsPage/index.tsx b/ui/src/pages/ApplicationDetailsPage/index.tsx
index 7b0edde..9e051b2 100644
--- a/ui/src/pages/ApplicationDetailsPage/index.tsx
+++ b/ui/src/pages/ApplicationDetailsPage/index.tsx
@@ -4,7 +4,8 @@ import { ApplicationDTO, ApplicationDTOPackagingFormatEnum, ComponentDTO } from
import { useEffect, useState } from 'react'
import { applicationsClient, componentsClient } from '../../clients/backend'
import { useLoaderData } from 'react-router-dom'
-import BranchesTable from './components/BranchesTable'
+import BranchesList from './components/BranchesList'
+import { LoadingBranchCard } from './components/BranchesList/BranchCard'
export async function loader({ params }: { params: any }): Promise {
return params.slugName
@@ -47,8 +48,11 @@ export default function ApplicationDetailsPage() {
Manage branches
- Install, pause, stop and update workloads
-
+ Run new workloads in your cluster
+
+
+
+
>
)
}
@@ -70,8 +74,8 @@ export default function ApplicationDetailsPage() {
{ application.description }
Manage branches
- Install, pause, stop and update workloads
- !b.inactive_at || new Date(b.inactive_at) > new Date()) } packagingFormat={ application.packaging_format } />
+ Run new workloads in your cluster
+ !b.inactive_at || new Date(b.inactive_at) > new Date()) } packagingFormat={ application.packaging_format } />
>
)
}
\ No newline at end of file
diff --git a/ui/src/vite-env.d.ts b/ui/src/vite-env.d.ts
index 11f02fe..b1f45c7 100644
--- a/ui/src/vite-env.d.ts
+++ b/ui/src/vite-env.d.ts
@@ -1 +1,2 @@
///
+///
diff --git a/ui/vite.config.ts b/ui/vite.config.ts
index 5ff4b7c..9f10af8 100644
--- a/ui/vite.config.ts
+++ b/ui/vite.config.ts
@@ -1,9 +1,10 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
+import svgr from 'vite-plugin-svgr'
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [react()],
+ plugins: [react(), svgr()],
base: './',
build: {
outDir: 'build',