From f8908e5c11723a47ac1bbb3bc3c7398b851d5848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20Stenstr=C3=B6m?= Date: Wed, 25 Mar 2026 11:28:01 +0100 Subject: [PATCH 1/3] Add Biome as frontend formatter and linter --- .../npm_translate_lock_LTUzNjQ2NjY3 | 6 +- .github/workflows/frontend.yaml | 5 + frontend/biome.json | 52 ++++++ frontend/package-lock.json | 164 ++++++++++++++++++ frontend/package.json | 7 +- frontend/pnpm-lock.yaml | 95 ++++++++++ .../github_workflows/github_workflows.jsonnet | 5 + 7 files changed, 330 insertions(+), 4 deletions(-) create mode 100644 frontend/biome.json diff --git a/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTUzNjQ2NjY3 b/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTUzNjQ2NjY3 index 8a9544c9..21dde4c8 100755 --- a/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTUzNjQ2NjY3 +++ b/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTUzNjQ2NjY3 @@ -1,7 +1,7 @@ # @generated # Input hashes for repository rule npm_translate_lock(name = "npm", pnpm_lock = "@@//frontend:pnpm-lock.yaml"). # This file should be checked into version control along with the pnpm-lock.yaml file. -frontend/package-lock.json=1050907894 -frontend/package.json=-2118623145 +frontend/package-lock.json=1029306719 +frontend/package.json=1684304897 frontend/patches/vite.patch=-1261363333 -frontend/pnpm-lock.yaml=1245402688 +frontend/pnpm-lock.yaml=1691778299 diff --git a/.github/workflows/frontend.yaml b/.github/workflows/frontend.yaml index ca9ccdce..141c6074 100644 --- a/.github/workflows/frontend.yaml +++ b/.github/workflows/frontend.yaml @@ -29,6 +29,11 @@ "name": "Check if any generated files changed", "run": "git add . && git diff --exit-code HEAD --" }, + { + "name": "Lint and format", + "run": "npm run biome-ci", + "working-directory": "frontend" + }, { "name": "Test", "run": "npm run test", diff --git a/frontend/biome.json b/frontend/biome.json new file mode 100644 index 00000000..1013e432 --- /dev/null +++ b/frontend/biome.json @@ -0,0 +1,52 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.8/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "includes": [ + "**", + "!!**/dist", + "!!**/node_modules", + "!**/src/routeTree.gen.ts", + "!**/src/lib/grpc-client", + "!**/src/graphql/__generated__", + "!**/possibleTypes.json" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedImports": "error" + }, + "complexity": { + "noImportantStyles": "off" + } + } + }, + "javascript": { + "parser": { + "jsxEverywhere": false + }, + "formatter": { + "quoteStyle": "double" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e98a8e3d..b9692549 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -34,6 +34,7 @@ }, "devDependencies": { "@babel/core": "^7.29.0", + "@biomejs/biome": "2.4.8", "@graphql-codegen/cli": "^6.1.3", "@graphql-typed-document-node/core": "^3.2.0", "@rolldown/plugin-babel": "^0.2.1", @@ -560,6 +561,169 @@ "node": ">=6.9.0" } }, + "node_modules/@biomejs/biome": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.8.tgz", + "integrity": "sha512-ponn0oKOky1oRXBV+rlSaUlixUxf1aZvWC19Z41zBfUOUesthrQqL3OtiAlSB1EjFjyWpn98Q64DHelhA6jNlA==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.4.8", + "@biomejs/cli-darwin-x64": "2.4.8", + "@biomejs/cli-linux-arm64": "2.4.8", + "@biomejs/cli-linux-arm64-musl": "2.4.8", + "@biomejs/cli-linux-x64": "2.4.8", + "@biomejs/cli-linux-x64-musl": "2.4.8", + "@biomejs/cli-win32-arm64": "2.4.8", + "@biomejs/cli-win32-x64": "2.4.8" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.8.tgz", + "integrity": "sha512-ARx0tECE8I7S2C2yjnWYLNbBdDoPdq3oyNLhMglmuctThwUsuzFWRKrHmIGwIRWKz0Mat9DuzLEDp52hGnrxGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.8.tgz", + "integrity": "sha512-Jg9/PsB9vDCJlANE8uhG7qDhb5w0Ix69D7XIIc8IfZPUoiPrbLm33k2Ig3NOJ/7nb3UbesFz3D1aDKm9DvzjhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.8.tgz", + "integrity": "sha512-5CdrsJct76XG2hpKFwXnEtlT1p+4g4yV+XvvwBpzKsTNLO9c6iLlAxwcae2BJ7ekPGWjNGw9j09T5KGPKKxQig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.8.tgz", + "integrity": "sha512-Zo9OhBQDJ3IBGPlqHiTISloo5H0+FBIpemqIJdW/0edJ+gEcLR+MZeZozcUyz3o1nXkVA7++DdRKQT0599j9jA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.8.tgz", + "integrity": "sha512-PdKXspVEaMCQLjtZCn6vfSck/li4KX9KGwSDbZdgIqlrizJ2MnMcE3TvHa2tVfXNmbjMikzcfJpuPWH695yJrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.8.tgz", + "integrity": "sha512-Gi8quv8MEuDdKaPFtS2XjEnMqODPsRg6POT6KhoP+VrkNb+T2ywunVB+TvOU0LX1jAZzfBr+3V1mIbBhzAMKvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.8.tgz", + "integrity": "sha512-LoFatS0tnHv6KkCVpIy3qZCih+MxUMvdYiPWLHRri7mhi2vyOOs8OrbZBcLTUEWCS+ktO72nZMy4F96oMhkOHQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.8.tgz", + "integrity": "sha512-vAn7iXDoUbqFXqVocuq1sMYAd33p8+mmurqJkWl6CtIhobd/O6moe4rY5AJvzbunn/qZCdiDVcveqtkFh1e7Hg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@bramus/specificity": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 2b47bccc..fc95844c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,11 @@ "generate-graphql": "graphql-codegen --config ./src/graphql/codegen.ts", "generate-grpc": "./tools/grpc-client-gen.sh", "generate-possible-types": "node ./tools/create-possible-types.js", - "predev": "npm run generate" + "predev": "npm run generate", + "format": "biome format --write", + "lint": "biome lint --write", + "check": "biome check --write", + "biome-ci": "biome ci" }, "dependencies": { "@ant-design/colors": "^7.0.2", @@ -47,6 +51,7 @@ }, "devDependencies": { "@babel/core": "^7.29.0", + "@biomejs/biome": "2.4.8", "@graphql-codegen/cli": "^6.1.3", "@graphql-typed-document-node/core": "^3.2.0", "@rolldown/plugin-babel": "^0.2.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index bc057836..6fa32d20 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -86,6 +86,9 @@ importers: '@babel/core': specifier: ^7.29.0 version: 7.29.0 + '@biomejs/biome': + specifier: 2.4.8 + version: 2.4.8 '@graphql-codegen/cli': specifier: ^6.1.3 version: 6.3.1(@types/node@24.12.2)(graphql@16.13.2)(typescript@5.9.3) @@ -308,6 +311,63 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@biomejs/biome@2.4.8': + resolution: {integrity: sha512-ponn0oKOky1oRXBV+rlSaUlixUxf1aZvWC19Z41zBfUOUesthrQqL3OtiAlSB1EjFjyWpn98Q64DHelhA6jNlA==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.4.8': + resolution: {integrity: sha512-ARx0tECE8I7S2C2yjnWYLNbBdDoPdq3oyNLhMglmuctThwUsuzFWRKrHmIGwIRWKz0Mat9DuzLEDp52hGnrxGQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.4.8': + resolution: {integrity: sha512-Jg9/PsB9vDCJlANE8uhG7qDhb5w0Ix69D7XIIc8IfZPUoiPrbLm33k2Ig3NOJ/7nb3UbesFz3D1aDKm9DvzjhQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.4.8': + resolution: {integrity: sha512-Zo9OhBQDJ3IBGPlqHiTISloo5H0+FBIpemqIJdW/0edJ+gEcLR+MZeZozcUyz3o1nXkVA7++DdRKQT0599j9jA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-arm64@2.4.8': + resolution: {integrity: sha512-5CdrsJct76XG2hpKFwXnEtlT1p+4g4yV+XvvwBpzKsTNLO9c6iLlAxwcae2BJ7ekPGWjNGw9j09T5KGPKKxQig==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-linux-x64-musl@2.4.8': + resolution: {integrity: sha512-Gi8quv8MEuDdKaPFtS2XjEnMqODPsRg6POT6KhoP+VrkNb+T2ywunVB+TvOU0LX1jAZzfBr+3V1mIbBhzAMKvw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-x64@2.4.8': + resolution: {integrity: sha512-PdKXspVEaMCQLjtZCn6vfSck/li4KX9KGwSDbZdgIqlrizJ2MnMcE3TvHa2tVfXNmbjMikzcfJpuPWH695yJrw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-win32-arm64@2.4.8': + resolution: {integrity: sha512-LoFatS0tnHv6KkCVpIy3qZCih+MxUMvdYiPWLHRri7mhi2vyOOs8OrbZBcLTUEWCS+ktO72nZMy4F96oMhkOHQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.4.8': + resolution: {integrity: sha512-vAn7iXDoUbqFXqVocuq1sMYAd33p8+mmurqJkWl6CtIhobd/O6moe4rY5AJvzbunn/qZCdiDVcveqtkFh1e7Hg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + '@bramus/specificity@2.4.2': resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true @@ -3424,6 +3484,41 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@biomejs/biome@2.4.8': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.4.8 + '@biomejs/cli-darwin-x64': 2.4.8 + '@biomejs/cli-linux-arm64': 2.4.8 + '@biomejs/cli-linux-arm64-musl': 2.4.8 + '@biomejs/cli-linux-x64': 2.4.8 + '@biomejs/cli-linux-x64-musl': 2.4.8 + '@biomejs/cli-win32-arm64': 2.4.8 + '@biomejs/cli-win32-x64': 2.4.8 + + '@biomejs/cli-darwin-arm64@2.4.8': + optional: true + + '@biomejs/cli-darwin-x64@2.4.8': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.4.8': + optional: true + + '@biomejs/cli-linux-arm64@2.4.8': + optional: true + + '@biomejs/cli-linux-x64-musl@2.4.8': + optional: true + + '@biomejs/cli-linux-x64@2.4.8': + optional: true + + '@biomejs/cli-win32-arm64@2.4.8': + optional: true + + '@biomejs/cli-win32-x64@2.4.8': + optional: true + '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 diff --git a/tools/github_workflows/github_workflows.jsonnet b/tools/github_workflows/github_workflows.jsonnet index 70165ad2..0e85dec7 100644 --- a/tools/github_workflows/github_workflows.jsonnet +++ b/tools/github_workflows/github_workflows.jsonnet @@ -176,6 +176,11 @@ local lint_steps = [ name: "Check if any generated files changed", run: "git add . && git diff --exit-code HEAD --" }, + { + name: "Lint and format", + run: "npm run biome-ci", + "working-directory": "frontend", + }, { name: "Test", run: "npm run test", From 63884ab5965c0f995151cc786ec2378111ac50ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20Stenstr=C3=B6m?= Date: Thu, 26 Mar 2026 13:09:11 +0100 Subject: [PATCH 2/3] Fix frontend lint errors and warnings --- frontend/index.html | 8 ++--- .../src/components/ApolloWrapper/index.tsx | 2 +- .../src/components/AppBar/AppBarButton.tsx | 4 +-- .../src/components/AppBar/AppBarButtons.tsx | 2 +- frontend/src/components/Artifacts/index.tsx | 4 +-- .../src/components/BazelInvocation/index.tsx | 25 ++++++++-------- .../BazelInvocationColumns/Columns.tsx | 4 +-- .../BazelInvocationsTable/index.tsx | 8 ++--- frontend/src/components/Breadcrumbs/index.tsx | 3 +- .../src/components/BrowserActionGrid/fetch.ts | 22 +++++++------- .../components/BrowserActionGrid/index.tsx | 2 +- .../DownloadAsShellScriptButton.tsx | 6 ++-- .../BrowserCommandDescription/index.tsx | 1 - .../DownloadAsTarballButton.tsx | 4 +-- .../components/BrowserDirectoryPage/index.tsx | 2 +- .../index.tsx | 1 - .../BrowserResultDescription/index.tsx | 1 - .../src/components/BuildLogsDisplay/index.tsx | 1 - frontend/src/components/CodeLink/index.tsx | 2 +- frontend/src/components/CommandLine/index.tsx | 2 +- frontend/src/components/Content/index.tsx | 7 +++-- .../src/components/DownloadButton/index.tsx | 9 +++--- .../src/components/FilesTable/Columns.tsx | 1 - frontend/src/components/FooterBar/index.tsx | 10 +++---- .../InvocationOverviewDisplay/index.tsx | 6 ++-- frontend/src/components/LogViewer/index.tsx | 21 ++++++------- .../src/components/MemoryMetrics/index.tsx | 8 ++--- .../src/components/MenuItemLabel/index.tsx | 4 +-- .../components/NullableBooleanTag/index.tsx | 10 +++---- .../src/components/OperationDetails/index.tsx | 1 - .../OperationStateDisplay/index.tsx | 7 +++-- .../components/OperationStateDisplay/utils.ts | 2 +- .../src/components/OperationsGrid/index.tsx | 4 +-- .../OperationsInvocationFilter/index.tsx | 2 +- frontend/src/components/PortalAlert/index.tsx | 4 +-- frontend/src/components/PortalCard/index.tsx | 16 +++++----- .../PreviousExecuteStatsPlot/index.tsx | 2 +- .../src/components/SearchWidgets/index.tsx | 6 ++-- frontend/src/components/SiderBar/index.tsx | 9 +++--- .../src/components/SizeClassOutcome/index.tsx | 6 ++++ .../components/SourceControlDisplay/index.tsx | 8 ++--- .../src/components/SummaryPieChart/index.tsx | 8 ++--- .../components/SystemMetricsDisplay/index.tsx | 7 ++--- .../Targets/TargetGridBtn/index.tsx | 8 ++--- frontend/src/components/TestGridBtn/index.tsx | 2 +- frontend/src/components/TestGridRow/index.tsx | 3 +- .../src/components/TestStatusTag/index.tsx | 4 +-- frontend/src/components/Uploader/index.tsx | 30 +++++++++---------- .../src/components/Utilities/navigation.tsx | 11 ++++--- .../src/components/Utilities/nullPercent.tsx | 2 +- frontend/src/components/WorkersGrid/index.tsx | 2 +- .../src/components/WorkersTable/index.tsx | 2 +- .../pages/BazelInvocationDetails/index.tsx | 5 ++-- .../pages/BazelInvocations/index.tsx | 2 +- .../pages/Browser/BrowserWelcome.tsx | 2 ++ .../src/components/pages/Browser/index.tsx | 4 +-- .../components/pages/BuildDetails/Columns.tsx | 1 - .../components/pages/BuildDetails/index.tsx | 2 +- .../components/pages/DisabledPage/index.tsx | 2 +- .../src/components/pages/NotFound/index.tsx | 2 +- .../src/components/pages/Operations/index.tsx | 2 +- .../src/components/pages/RootLayout/index.tsx | 4 +-- .../pages/SchedulerWorkers/index.tsx | 2 +- .../src/components/pages/Targets/index.tsx | 2 +- frontend/src/components/pages/Tests/index.tsx | 2 +- .../src/components/pages/Trends/index.tsx | 23 +++++++------- frontend/src/context/GrpcClientsProvider.tsx | 1 - frontend/src/graphql/codegen.ts | 2 +- frontend/src/main.tsx | 1 + frontend/src/splash/splash.js | 10 +++---- frontend/src/theme/dark.tsx | 2 +- frontend/src/theme/light.tsx | 2 +- frontend/src/utils/bloomFilter.test.ts | 2 +- frontend/src/utils/bloomFilter.ts | 3 +- frontend/src/utils/env.ts | 4 +-- frontend/src/utils/featureGuard.ts | 4 ++- frontend/src/utils/filesize.ts | 2 +- frontend/src/utils/parseGraphqlEdgeList.ts | 6 ++-- frontend/src/utils/screen.ts | 15 +++++----- frontend/src/utils/time.ts | 2 +- frontend/vite.config.ts | 4 +-- 81 files changed, 226 insertions(+), 215 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index eae46ce7..cb750546 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -6,12 +6,12 @@ diff --git a/frontend/src/components/ApolloWrapper/index.tsx b/frontend/src/components/ApolloWrapper/index.tsx index 01536b32..61e44e91 100644 --- a/frontend/src/components/ApolloWrapper/index.tsx +++ b/frontend/src/components/ApolloWrapper/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type React from 'react'; import { ApolloClient, InMemoryCache, diff --git a/frontend/src/components/AppBar/AppBarButton.tsx b/frontend/src/components/AppBar/AppBarButton.tsx index d7de0497..fe8c48f2 100644 --- a/frontend/src/components/AppBar/AppBarButton.tsx +++ b/frontend/src/components/AppBar/AppBarButton.tsx @@ -1,12 +1,12 @@ -import React from 'react'; +import type React from 'react'; import { Button, Tooltip } from 'antd'; type Props = { icon: React.ReactNode; title: string; href?: string; - onMouseDown?: Function; + onMouseDown?: () => void; }; const AppBarButton: React.FC = ({ icon, title, href, onMouseDown }) => { diff --git a/frontend/src/components/AppBar/AppBarButtons.tsx b/frontend/src/components/AppBar/AppBarButtons.tsx index 5ec8d048..f4b47f2c 100644 --- a/frontend/src/components/AppBar/AppBarButtons.tsx +++ b/frontend/src/components/AppBar/AppBarButtons.tsx @@ -1,5 +1,5 @@ -import React from 'react'; +import type React from 'react'; import { BulbOutlined, GithubOutlined } from '@ant-design/icons'; import AppBarButton from '@/components/AppBar/AppBarButton'; import styles from '@/components/AppBar/index.module.css'; diff --git a/frontend/src/components/Artifacts/index.tsx b/frontend/src/components/Artifacts/index.tsx index e0ae844f..98aa3483 100644 --- a/frontend/src/components/Artifacts/index.tsx +++ b/frontend/src/components/Artifacts/index.tsx @@ -1,7 +1,7 @@ -import React from "react"; +import type React from "react"; import { Table, Row, Col, Space } from 'antd'; import type { TableColumnsType } from "antd/lib"; -import { ArtifactMetrics } from "@/graphql/__generated__/graphql"; +import type { ArtifactMetrics } from "@/graphql/__generated__/graphql"; import PortalCard from "../PortalCard"; import { RadiusUprightOutlined } from "@ant-design/icons"; import styles from "../../theme/theme.module.css" diff --git a/frontend/src/components/BazelInvocation/index.tsx b/frontend/src/components/BazelInvocation/index.tsx index db3d1fd9..ecf0d267 100644 --- a/frontend/src/components/BazelInvocation/index.tsx +++ b/frontend/src/components/BazelInvocation/index.tsx @@ -6,7 +6,7 @@ import SourceControlDisplay from "../SourceControlDisplay"; import InvocationOverviewDisplay from "../InvocationOverviewDisplay"; import PortalCard from "@/components/PortalCard"; import PortalDuration from "@/components/PortalDuration"; -import { +import type { BazelInvocationInfoFragment, RunnerCount } from "@/graphql/__generated__/graphql"; @@ -26,7 +26,8 @@ import { } from "@ant-design/icons"; import { Space, Tabs, Typography } from "antd"; import type { TabsProps } from "antd/lib"; -import React, { useMemo, useState } from "react"; +import type React from "react"; +import { useMemo, useState } from "react"; import ActionStatisticsDisplay from "../ActionStatisticsDisplay"; import styles from "../AppBar/index.module.css"; import BuildLogsDisplay from "../BuildLogsDisplay"; @@ -65,17 +66,17 @@ const getTabItems = (invocationOverview: BazelInvocationInfoFragment): TabsProps runnerMetrics.push(item) ); - const hideActionStatisticsTab: boolean = metrics?.actionSummary == undefined || metrics?.actionSummary == null; + const hideActionStatisticsTab: boolean = metrics?.actionSummary === undefined || metrics?.actionSummary == null; const hideLogsTab: boolean = false; - const hideArtifactsTab: boolean = metrics?.artifactMetrics == undefined || metrics?.artifactMetrics == null; - const hideMemoryTab: boolean = metrics?.memoryMetrics == undefined || metrics?.memoryMetrics == null; + const hideArtifactsTab: boolean = metrics?.artifactMetrics === undefined || metrics?.artifactMetrics == null; + const hideMemoryTab: boolean = metrics?.memoryMetrics === undefined || metrics?.memoryMetrics == null; const hideSystemMetricsTab: boolean = - (metrics?.timingMetrics == undefined || metrics?.timingMetrics == null) - && (metrics?.networkMetrics == undefined || metrics?.networkMetrics == null); - const hideFailedActionsTab: boolean = actions == undefined || actions == null || actions.length == 0; + (metrics?.timingMetrics === undefined || metrics?.timingMetrics == null) + && (metrics?.networkMetrics === undefined || metrics?.networkMetrics == null); + const hideFailedActionsTab: boolean = actions === undefined || actions == null || actions.length === 0; const hideTargetsTab: boolean = !env.featureFlags?.bes?.pageTargets; const hideTestsTab: boolean = !env.featureFlags?.bes?.pageTests; - const hideSourceControlTab: boolean = sourceControl == undefined || sourceControl == null; + const hideSourceControlTab: boolean = sourceControl === undefined || sourceControl == null; const command = commandLineDataToString(originalCommandLine) @@ -242,7 +243,7 @@ const getTitleBits = (invocationOverview: BazelInvocationInfoFragment): React.Re } = invocationOverview; const titleBits: React.ReactNode[] = [] - if (user?.LDAP && user?.LDAP != "") titleBits.push( + if (user?.LDAP && user?.LDAP !== "") titleBits.push( User:{" "} @@ -250,7 +251,7 @@ const getTitleBits = (invocationOverview: BazelInvocationInfoFragment): React.Re ); - if (invocationID && invocationID != "") titleBits.push( + if (invocationID && invocationID !== "") titleBits.push( Invocation ID:{" "} @@ -338,7 +339,7 @@ const BazelInvocation: React.FC = ({ invocationOverview }) => { ); function checkIfNotHidden(key: string) { - const hidden = items?.findIndex((x) => x.key == key) == -1 + const hidden = items?.findIndex((x) => x.key === key) === -1 return hidden ? DEFAULT_TAB_KEY : key; } diff --git a/frontend/src/components/BazelInvocationColumns/Columns.tsx b/frontend/src/components/BazelInvocationColumns/Columns.tsx index 2a9229f9..7a97f795 100644 --- a/frontend/src/components/BazelInvocationColumns/Columns.tsx +++ b/frontend/src/components/BazelInvocationColumns/Columns.tsx @@ -7,12 +7,12 @@ import { import { Link } from '@tanstack/react-router'; import dayjs from 'dayjs'; import styles from './Columns.module.css'; -import { BazelInvocationNodeFragment, BazelInvocationWhereInput } from '@/graphql/__generated__/graphql'; +import type { BazelInvocationNodeFragment, BazelInvocationWhereInput } from '@/graphql/__generated__/graphql'; import { SearchFilterIcon, SearchWidget, TimeRangeSelector } from '@/components/SearchWidgets'; import PortalDuration from "@/components/PortalDuration"; import UserStatusIndicator from '../UserStatusIndicator'; import { InvocationResultTag } from '../InvocationResultTag'; -import { FilterValue } from 'antd/es/table/interface'; +import type { FilterValue } from 'antd/es/table/interface'; import { applyInvocationResultTagFilter, invocationResultTagFilters } from '../InvocationResultTag/filters'; type ColumnTypeWithFilter = ColumnType & { diff --git a/frontend/src/components/BazelInvocationsTable/index.tsx b/frontend/src/components/BazelInvocationsTable/index.tsx index 03c05582..946b2f78 100644 --- a/frontend/src/components/BazelInvocationsTable/index.tsx +++ b/frontend/src/components/BazelInvocationsTable/index.tsx @@ -11,16 +11,16 @@ import FIND_BAZEL_INVOCATIONS_QUERY, { } from './query.graphql'; import { BazelInvocationOrderField, - BazelInvocationWhereInput, OrderDirection + type BazelInvocationWhereInput, OrderDirection } from '@/graphql/__generated__/graphql'; import themeStyles from '@/theme/theme.module.css'; import { BuildOutlined } from '@ant-design/icons'; import { useQuery } from '@apollo/client/react'; import { Space, Typography } from 'antd'; -import { FilterValue } from 'antd/lib/table/interface'; +import type { FilterValue } from 'antd/lib/table/interface'; import React from 'react'; import { CursorTable, getNewPaginationVariables } from '../CursorTable'; -import { PaginationVariables } from '../CursorTable/types'; +import type { PaginationVariables } from '../CursorTable/types'; import PortalAlert from "../PortalAlert"; import styles from "@/theme/theme.module.css"; import { parseGraphqlEdgeListWithFragment } from "@/utils/parseGraphqlEdgeList"; @@ -111,7 +111,7 @@ const BazelInvocationsTable: React.FC = () => { ); } - let emptyText = 'No Bazel invocations match the specified search criteria'; + const emptyText = 'No Bazel invocations match the specified search criteria'; return ( MAX_CONSOLE_OUTPUT_SIZE) { + if (Number.parseInt(digest.sizeBytes, 10) > MAX_CONSOLE_OUTPUT_SIZE) { return { name, digest, @@ -193,7 +193,7 @@ export const getActionConsoleOutput = async ( notFound: false, content: new TextDecoder().decode(content), }; - } catch (e) { + } catch (_e) { return { name, digest, @@ -222,7 +222,7 @@ async function fetchPreviousExecutionStats( action.platform, ), }); - } catch (error) { + } catch (_error) { console.log("No previous execution stats found"); } } @@ -269,12 +269,12 @@ function extractMetadataFromExecuteResponse( inputRootResourceUsage: InputRootResourceUsage | undefined; monetaryResourceUsage: MonetaryResourceUsage | undefined; } { - let authenticationMetadata: AuthenticationMetadata | undefined = undefined; - let requestMetadata: RequestMetadata | undefined = undefined; - let posixResourceUsage: POSIXResourceUsage | undefined = undefined; - let filePoolResourceUsage: FilePoolResourceUsage | undefined = undefined; - let inputRootResourceUsage: InputRootResourceUsage | undefined = undefined; - let monetaryResourceUsage: MonetaryResourceUsage | undefined = undefined; + let authenticationMetadata: AuthenticationMetadata | undefined ; + let requestMetadata: RequestMetadata | undefined ; + let posixResourceUsage: POSIXResourceUsage | undefined ; + let filePoolResourceUsage: FilePoolResourceUsage | undefined ; + let inputRootResourceUsage: InputRootResourceUsage | undefined ; + let monetaryResourceUsage: MonetaryResourceUsage | undefined ; if (!executeResponse?.result?.executionMetadata?.auxiliaryMetadata) { return { @@ -399,7 +399,7 @@ async function fetchExecuteResponse( result: actionResult, }), }; - } catch (error) { + } catch (_error) { console.log("No execute response was found"); } @@ -423,7 +423,7 @@ async function fetchFileSystemAccessProfile( action.platform, ), }); - } catch (error) { + } catch (_error) { console.log("No file system access cache profile was found"); } } diff --git a/frontend/src/components/BrowserActionGrid/index.tsx b/frontend/src/components/BrowserActionGrid/index.tsx index ef19ac27..55c725b1 100644 --- a/frontend/src/components/BrowserActionGrid/index.tsx +++ b/frontend/src/components/BrowserActionGrid/index.tsx @@ -32,7 +32,7 @@ import PropertyTagList from "../PropertyTagList"; import type { PropertyTagListEntry } from "../PropertyTagList/types"; import CopyBbClientdActionButton from "./CopyBbClientdActionButton"; import { fetchBrowserActionGrid } from "./fetch"; -import { Digest } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; +import type { Digest } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; interface Params { browserPageParams: BrowserPageParams; diff --git a/frontend/src/components/BrowserCommandDescription/DownloadAsShellScriptButton.tsx b/frontend/src/components/BrowserCommandDescription/DownloadAsShellScriptButton.tsx index 881a1a75..9bdf7aa9 100644 --- a/frontend/src/components/BrowserCommandDescription/DownloadAsShellScriptButton.tsx +++ b/frontend/src/components/BrowserCommandDescription/DownloadAsShellScriptButton.tsx @@ -1,8 +1,8 @@ -import { Digest } from '@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution'; -import { BrowserPageParams } from '@/types/BrowserPageType'; +import type { Digest } from '@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution'; +import type { BrowserPageParams } from '@/types/BrowserPageType'; import { generateCommandShellScriptUrl } from '@/utils/urlGenerator'; import { Button } from 'antd'; -import React from 'react'; +import type React from 'react'; interface Params { browserPageParams: BrowserPageParams; diff --git a/frontend/src/components/BrowserCommandDescription/index.tsx b/frontend/src/components/BrowserCommandDescription/index.tsx index cd5b48e1..4919656d 100644 --- a/frontend/src/components/BrowserCommandDescription/index.tsx +++ b/frontend/src/components/BrowserCommandDescription/index.tsx @@ -3,7 +3,6 @@ import type { Digest, } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; import { BrowserPageType, type BrowserPageParams } from "@/types/BrowserPageType"; -import { digestFunctionValueToString } from "@/utils/digestFunctionUtils"; import { Descriptions, Flex, Space, Typography } from "antd"; import { Link } from '@tanstack/react-router'; import type React from "react"; diff --git a/frontend/src/components/BrowserDirectory/DownloadAsTarballButton.tsx b/frontend/src/components/BrowserDirectory/DownloadAsTarballButton.tsx index 2ca02f00..9417882a 100644 --- a/frontend/src/components/BrowserDirectory/DownloadAsTarballButton.tsx +++ b/frontend/src/components/BrowserDirectory/DownloadAsTarballButton.tsx @@ -1,10 +1,10 @@ -import { +import type { Digest, DigestFunction_Value, } from '@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution'; import { generateDirectoryTarballUrl } from '@/utils/urlGenerator'; import { Button } from 'antd'; -import React from 'react'; +import type React from 'react'; interface Params { instanceName: string; diff --git a/frontend/src/components/BrowserDirectoryPage/index.tsx b/frontend/src/components/BrowserDirectoryPage/index.tsx index 7cdad22a..e05fa493 100644 --- a/frontend/src/components/BrowserDirectoryPage/index.tsx +++ b/frontend/src/components/BrowserDirectoryPage/index.tsx @@ -1,7 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import { Spin } from "antd"; import { useGrpcClients } from "@/context/GrpcClientsContext"; -import { FileSystemAccessProfileReference } from "@/lib/grpc-client/buildbarn/query/query"; +import type { FileSystemAccessProfileReference } from "@/lib/grpc-client/buildbarn/query/query"; import type { BrowserPageParams } from "@/types/BrowserPageType"; import BrowserDirectory from "../BrowserDirectory"; import PortalAlert from "../PortalAlert"; diff --git a/frontend/src/components/BrowserPreviousExecutionsDisplay/index.tsx b/frontend/src/components/BrowserPreviousExecutionsDisplay/index.tsx index f33b58a0..6a2ecf60 100644 --- a/frontend/src/components/BrowserPreviousExecutionsDisplay/index.tsx +++ b/frontend/src/components/BrowserPreviousExecutionsDisplay/index.tsx @@ -1,7 +1,6 @@ import type { Digest } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; import type { PreviousExecutionStats } from "@/lib/grpc-client/buildbarn/iscc/iscc"; import { BrowserPageType, type BrowserPageParams } from "@/types/BrowserPageType"; -import { digestFunctionValueToString } from "@/utils/digestFunctionUtils"; import { Descriptions, Space, Typography } from "antd"; import { Link } from '@tanstack/react-router'; import PreviousExecutionsPlot from "../PreviousExecuteStatsPlot"; diff --git a/frontend/src/components/BrowserResultDescription/index.tsx b/frontend/src/components/BrowserResultDescription/index.tsx index 948f4e93..f7a73b1f 100644 --- a/frontend/src/components/BrowserResultDescription/index.tsx +++ b/frontend/src/components/BrowserResultDescription/index.tsx @@ -5,7 +5,6 @@ import { readableFileSizeFromString } from "@/utils/filesize"; import { generateFileUrl } from "@/utils/urlGenerator"; import { Descriptions, Space, Tag, Typography } from "antd"; import Paragraph from "antd/es/typography/Paragraph"; -import { Link } from '@tanstack/react-router'; import type React from "react"; import type { ActionConsoleOutput } from "../BrowserActionGrid/types"; diff --git a/frontend/src/components/BuildLogsDisplay/index.tsx b/frontend/src/components/BuildLogsDisplay/index.tsx index 6490b328..6b6bc831 100644 --- a/frontend/src/components/BuildLogsDisplay/index.tsx +++ b/frontend/src/components/BuildLogsDisplay/index.tsx @@ -4,7 +4,6 @@ import { } from "@ant-design/icons"; import { useQuery } from "@tanstack/react-query"; import { Tooltip } from "antd"; -import { env } from "@/utils/env"; import DownloadButton from "@/components/DownloadButton"; import PortalCard from "@/components/PortalCard"; import LogViewer from "../LogViewer"; diff --git a/frontend/src/components/CodeLink/index.tsx b/frontend/src/components/CodeLink/index.tsx index c6f853e8..6510786b 100644 --- a/frontend/src/components/CodeLink/index.tsx +++ b/frontend/src/components/CodeLink/index.tsx @@ -1,4 +1,4 @@ -import { Link, LinkOptions } from "@tanstack/react-router"; +import { Link, type LinkOptions } from "@tanstack/react-router"; interface Props { text: string; diff --git a/frontend/src/components/CommandLine/index.tsx b/frontend/src/components/CommandLine/index.tsx index 87679cbc..ec3e9ce0 100644 --- a/frontend/src/components/CommandLine/index.tsx +++ b/frontend/src/components/CommandLine/index.tsx @@ -1,6 +1,6 @@ import { CodeOutlined, InfoCircleOutlined } from '@ant-design/icons'; import { Empty, List, Tooltip } from 'antd'; -import React from 'react'; +import type React from 'react'; import PortalCard from '../PortalCard'; // TODO: find a way to apply these interfaces automatically to the diff --git a/frontend/src/components/Content/index.tsx b/frontend/src/components/Content/index.tsx index 243ce9e5..d6e626b2 100644 --- a/frontend/src/components/Content/index.tsx +++ b/frontend/src/components/Content/index.tsx @@ -1,7 +1,8 @@ -import React, { Key } from 'react'; -import { FloatButton, Layout, MenuProps } from 'antd'; -import { ItemType } from 'antd/es/menu/interface'; +import type React from 'react'; +import type { Key } from 'react'; +import { FloatButton, Layout, type MenuProps } from 'antd'; +import type { ItemType } from 'antd/es/menu/interface'; import styles from '@/components/Content/index.module.css'; import SiderBar from '@/components/SiderBar'; import { Breadcrumbs } from '@/components/Breadcrumbs'; diff --git a/frontend/src/components/DownloadButton/index.tsx b/frontend/src/components/DownloadButton/index.tsx index 8f6cd762..f2cb1cba 100644 --- a/frontend/src/components/DownloadButton/index.tsx +++ b/frontend/src/components/DownloadButton/index.tsx @@ -1,7 +1,8 @@ -import React, { useState } from 'react'; -import { Button, Dropdown, DropdownProps, MenuProps, Popover, Space } from 'antd'; +import type React from 'react'; +import { useState } from 'react'; +import { Button, Dropdown, type DropdownProps, type MenuProps, Popover, Space } from 'antd'; import { DownloadOutlined } from '@ant-design/icons'; -import { MenuItemType } from 'antd/es/menu/interface'; +import type { MenuItemType } from 'antd/es/menu/interface'; export interface DownloadOpts { url?: string; @@ -31,7 +32,7 @@ const DownloadButton: React.FC = ({ const handleMenuClick: MenuProps['onClick'] = e => { // Closing is delegated to item. - if (preventDefaultForKeys && preventDefaultForKeys.includes(e.key)) { + if (preventDefaultForKeys?.includes(e.key)) { e.domEvent.preventDefault(); } }; diff --git a/frontend/src/components/FilesTable/Columns.tsx b/frontend/src/components/FilesTable/Columns.tsx index 1d329de3..502ca566 100644 --- a/frontend/src/components/FilesTable/Columns.tsx +++ b/frontend/src/components/FilesTable/Columns.tsx @@ -1,6 +1,5 @@ import { type TableColumnsType, Typography } from "antd"; import type { ColumnType } from "antd/lib/table"; -import { Link } from '@tanstack/react-router'; export interface FilesTableEntry { mode: string | undefined; diff --git a/frontend/src/components/FooterBar/index.tsx b/frontend/src/components/FooterBar/index.tsx index eb5dcb0d..5ae7abe5 100644 --- a/frontend/src/components/FooterBar/index.tsx +++ b/frontend/src/components/FooterBar/index.tsx @@ -7,12 +7,11 @@ import { } from "@ant-design/icons"; import { Layout, Space } from "antd"; import { env } from "@/utils/env"; -import { Link } from '@tanstack/react-router'; -import React from "react"; -import { PortalFrontendConfiguration_FooterElement } from "@/lib/grpc-client/portal/frontend/frontend"; +import type React from "react"; +import type { PortalFrontendConfiguration_FooterElement } from "@/lib/grpc-client/portal/frontend/frontend"; const FooterLink: React.FC = ({ text, href, icon }) => { - let iconElement: React.ReactElement | undefined = undefined; + let iconElement: React.ReactElement | undefined ; if (icon?.url) { iconElement = Footer icon } else if (icon?.slack) { @@ -33,7 +32,7 @@ const FooterLink: React.FC = ({ text, } return ( - + {iconElement} {text} @@ -51,6 +50,7 @@ const FooterBar: React.FC = ({ className }) => { {env.footerContent.map((element: PortalFrontendConfiguration_FooterElement, index: number) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: This won't change ))} diff --git a/frontend/src/components/InvocationOverviewDisplay/index.tsx b/frontend/src/components/InvocationOverviewDisplay/index.tsx index f6036afb..98591714 100644 --- a/frontend/src/components/InvocationOverviewDisplay/index.tsx +++ b/frontend/src/components/InvocationOverviewDisplay/index.tsx @@ -1,8 +1,8 @@ -import React from 'react'; +import type React from 'react'; import { Descriptions, Space } from 'antd'; import PortalDuration from '../PortalDuration'; import { InvocationResultTag } from '../InvocationResultTag'; -import { Configuration as BazelConfiguration } from '@/graphql/__generated__/graphql'; +import type { Configuration as BazelConfiguration } from '@/graphql/__generated__/graphql'; type Configuration = Pick; @@ -48,7 +48,7 @@ export const InvocationOverviewDisplay: React.FC = ({ ) .sort() .join(", "); - let mnemonics = Array.from( + const mnemonics = Array.from( new Set(configurations ?.map((config) => config.mnemonic) ?.filter((mnemonic) => mnemonic && mnemonic !== "") diff --git a/frontend/src/components/LogViewer/index.tsx b/frontend/src/components/LogViewer/index.tsx index 47d404f7..e06fb35a 100644 --- a/frontend/src/components/LogViewer/index.tsx +++ b/frontend/src/components/LogViewer/index.tsx @@ -1,27 +1,26 @@ -import type { ApolloError } from "@apollo/client"; import { AnsiUp } from "ansi_up"; -import { Card, type CardProps, Spin } from "antd"; -import type { RefAttributes } from "react"; +import { Spin } from "antd"; import React from "react"; -import { JSX } from "react/jsx-runtime"; import { WindowVirtualizer } from "virtua"; import PortalAlert from "@/components/PortalAlert"; import styles from "./index.module.css"; - -import IntrinsicAttributes = JSX.IntrinsicAttributes; +import { v4 as uuidv4 } from 'uuid'; const ansi = new AnsiUp(); interface Props { log?: string | null; loading?: boolean; - error?: ApolloError | Error | null; + error?: Error | null; } const LogViewer: React.FC = ({ log, loading, error }) => { const lines = React.useMemo(() => { if (!log) return []; - return ansi.ansi_to_html(log).split("\n"); + return ansi.ansi_to_html(log).split("\n").map(line => ({ + line, + key: uuidv4() + })); }, [log]); if (loading === true) @@ -56,8 +55,10 @@ const LogViewer: React.FC = ({ log, loading, error }) => { return (
       
-        {lines.map((line, index) => (
-          
+        {lines.map((line) => (
+          // TODO: Remove the danger
+          // biome-ignore lint/security/noDangerouslySetInnerHtml: Should be reworked
+          
         ))}
       
     
diff --git a/frontend/src/components/MemoryMetrics/index.tsx b/frontend/src/components/MemoryMetrics/index.tsx index 9a575cf1..68e95cd8 100644 --- a/frontend/src/components/MemoryMetrics/index.tsx +++ b/frontend/src/components/MemoryMetrics/index.tsx @@ -1,12 +1,12 @@ -import React from "react"; +import type React from "react"; import { Table, Row, Col, Statistic, Space } from "antd"; import type { TableColumnsType } from "antd/lib"; -import { MemoryMetrics, GarbageMetrics } from "@/graphql/__generated__/graphql"; +import type { MemoryMetrics, GarbageMetrics } from "@/graphql/__generated__/graphql"; import PortalCard from "../PortalCard"; import { PieChartOutlined, HddOutlined } from "@ant-design/icons"; import styles from "../../theme/theme.module.css"; import { readableFileSize } from "@/utils/filesize"; -import SummaryPieChart, { SummaryChartItem } from "../SummaryPieChart"; +import SummaryPieChart, { type SummaryChartItem } from "../SummaryPieChart"; import { nullPercent } from "../Utilities/nullPercent"; interface GarbageMetricDetailDisplayType { @@ -35,7 +35,7 @@ const MemoryMetricsDisplay: React.FC<{ memoryMetrics: MemoryMetrics | undefined; }> = ({ memoryMetrics }) => { const garbage_data: GarbageMetricDetailDisplayType[] = []; - memoryMetrics?.garbageMetrics?.map((item: GarbageMetrics, index) => { + memoryMetrics?.garbageMetrics?.forEach((item: GarbageMetrics, index) => { var gm: GarbageMetricDetailDisplayType = { key: index, name: item.type ?? "", diff --git a/frontend/src/components/MenuItemLabel/index.tsx b/frontend/src/components/MenuItemLabel/index.tsx index e4e2b3de..422a4041 100644 --- a/frontend/src/components/MenuItemLabel/index.tsx +++ b/frontend/src/components/MenuItemLabel/index.tsx @@ -1,8 +1,8 @@ -import React, { ForwardedRef, forwardRef, useLayoutEffect, useRef } from 'react'; +import { type ForwardedRef, forwardRef, useLayoutEffect, useRef } from 'react'; import { Link, useLocation } from '@tanstack/react-router'; import { Tag } from 'antd'; import styles from '@/components/MenuItemLabel/index.module.css'; -import { UpdateSidebarMenuExpandedWidthFunction } from '@/components/Utilities/navigation'; +import type { UpdateSidebarMenuExpandedWidthFunction } from '@/components/Utilities/navigation'; import { SIDEBAR_MENU_INLINE_INDENT } from '@/components/SiderBar'; export interface MenuItemTag { diff --git a/frontend/src/components/NullableBooleanTag/index.tsx b/frontend/src/components/NullableBooleanTag/index.tsx index 91219810..d591c9ff 100644 --- a/frontend/src/components/NullableBooleanTag/index.tsx +++ b/frontend/src/components/NullableBooleanTag/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type React from 'react'; import { CheckCircleFilled, CloseCircleFilled, QuestionCircleFilled, } from '@ant-design/icons'; import { Tag } from 'antd'; import themeStyles from '@/theme/theme.module.css'; @@ -37,16 +37,16 @@ const NullBooleanTag: React.FC = ({ status, hideText }) => { hideText = false } var status_string: NilBoolEnum = "null_tag"; - if (status == true && hideText == false) { + if (status === true && hideText === false) { status_string = "true_tag" } - if (status == true && hideText == true) { + if (status === true && hideText === true) { status_string = "true_hide_tag" } - if (status == false && hideText == false) { + if (status === false && hideText === false) { status_string = "false_tag" } - if (status == false && hideText == true) { + if (status === false && hideText === true) { status_string = "false_hide_tag" } const resultTag = BOOL_TAGS[status_string] || BOOL_TAGS.null_tag; diff --git a/frontend/src/components/OperationDetails/index.tsx b/frontend/src/components/OperationDetails/index.tsx index 3728cf72..e86f768a 100644 --- a/frontend/src/components/OperationDetails/index.tsx +++ b/frontend/src/components/OperationDetails/index.tsx @@ -1,7 +1,6 @@ import { useGrpcClients } from "@/context/GrpcClientsContext"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Button, Space, Spin } from "antd"; -import { env } from "@/utils/env"; import { Code } from "@/lib/grpc-client/google/rpc/code"; import ExecuteResponseDisplay from "../ExecutionResponseDisplay"; import OperationStateDisplay from "../OperationStateDisplay"; diff --git a/frontend/src/components/OperationStateDisplay/index.tsx b/frontend/src/components/OperationStateDisplay/index.tsx index 32136cfd..4f7505b8 100644 --- a/frontend/src/components/OperationStateDisplay/index.tsx +++ b/frontend/src/components/OperationStateDisplay/index.tsx @@ -17,6 +17,7 @@ import { } from "./utils"; import { generateBrowserSplat } from "@/utils/urlGenerator"; import { BrowserPageType } from "@/types/BrowserPageType"; +import type { OperationsFilterParams } from "@/routes/operations.index"; interface Props { operation: OperationState; @@ -56,7 +57,7 @@ const OperationStateDisplay: React.FC = ({ operation }) => {
    {invocationMetadata?.map((value) => { - let metadataObject = undefined + let metadataObject: OperationsFilterParams try { metadataObject = JSON.parse(value) } catch { @@ -123,7 +124,7 @@ const OperationStateDisplay: React.FC = ({ operation }) => { {operation.expectedDuration && readableDurationFromSeconds( - Number.parseInt(operation.expectedDuration.seconds), + Number.parseInt(operation.expectedDuration.seconds, 10), { precision: 1, smallestUnit: "s" }, )} @@ -142,7 +143,7 @@ const OperationStateDisplay: React.FC = ({ operation }) => { color="default" className={themeStyles.tag} > - <>Status message: {operation.completed?.status?.message} + Status message: {operation.completed?.status?.message} )} diff --git a/frontend/src/components/OperationStateDisplay/utils.ts b/frontend/src/components/OperationStateDisplay/utils.ts index caa5f5ac..5e19a21a 100644 --- a/frontend/src/components/OperationStateDisplay/utils.ts +++ b/frontend/src/components/OperationStateDisplay/utils.ts @@ -1,5 +1,5 @@ import type { OperationState } from "@/lib/grpc-client/buildbarn/buildqueuestate/buildqueuestate"; -import { BrowserPageParams } from "@/types/BrowserPageType"; +import type { BrowserPageParams } from "@/types/BrowserPageType"; import { parseBrowserPageSlug } from "@/utils/parseBrowserPageSlug"; export const historicalExecuteResponseDigestFromOperation = ( diff --git a/frontend/src/components/OperationsGrid/index.tsx b/frontend/src/components/OperationsGrid/index.tsx index c9c01559..effd11cb 100644 --- a/frontend/src/components/OperationsGrid/index.tsx +++ b/frontend/src/components/OperationsGrid/index.tsx @@ -7,7 +7,7 @@ import themeStyles from "@/theme/theme.module.css"; import OperationsInvocationFilter from "../OperationsInvocationFilter"; import PortalAlert from "../PortalAlert"; import getColumns from "./Columns"; -import { OperationsFilterParams } from "@/routes/operations.index"; +import type { OperationsFilterParams } from "@/routes/operations.index"; const PAGE_SIZE = 1000; @@ -24,7 +24,7 @@ const OperationsTable: React.FC = ({ filter }) => { pageSize: PAGE_SIZE, filterInvocationId: filter ?{ typeUrl: filter?.["@type"], - value: RequestMetadata.encode(filter as any).finish(), + value: RequestMetadata.encode(RequestMetadata.fromPartial(filter)).finish(), }: undefined }), staleTime: Number.POSITIVE_INFINITY, diff --git a/frontend/src/components/OperationsInvocationFilter/index.tsx b/frontend/src/components/OperationsInvocationFilter/index.tsx index 6212b3f1..a79aa8b8 100644 --- a/frontend/src/components/OperationsInvocationFilter/index.tsx +++ b/frontend/src/components/OperationsInvocationFilter/index.tsx @@ -1,6 +1,6 @@ import { Button, Col, Divider, Row } from "antd"; import styles from "./index.module.css"; -import { OperationsFilterParams } from "@/routes/operations.index"; +import type { OperationsFilterParams } from "@/routes/operations.index"; import { useNavigate } from "@tanstack/react-router"; interface Props { diff --git a/frontend/src/components/PortalAlert/index.tsx b/frontend/src/components/PortalAlert/index.tsx index 389d4555..e3304d54 100644 --- a/frontend/src/components/PortalAlert/index.tsx +++ b/frontend/src/components/PortalAlert/index.tsx @@ -1,6 +1,6 @@ -import React from 'react'; +import type React from 'react'; import { Alert, Typography } from 'antd'; -import { AlertProps } from 'antd/lib/alert'; +import type { AlertProps } from 'antd/lib/alert'; import styles from './index.module.css'; type Props = AlertProps; diff --git a/frontend/src/components/PortalCard/index.tsx b/frontend/src/components/PortalCard/index.tsx index c4a9b8cf..f0d5ef27 100644 --- a/frontend/src/components/PortalCard/index.tsx +++ b/frontend/src/components/PortalCard/index.tsx @@ -1,5 +1,6 @@ -import React, { forwardRef, useLayoutEffect, useRef, useState } from 'react'; -import { Button, Card, CardProps, List, Popover, Space, theme } from 'antd'; +import React from 'react'; +import { forwardRef, useLayoutEffect, useRef, useState } from 'react'; +import { Button, Card, type CardProps, List, Popover, Space, theme } from 'antd'; import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'; import styles from './index.module.css'; import themeStyles from '@/theme/theme.module.css'; @@ -13,14 +14,13 @@ interface HeaderProps { const Header = forwardRef(({ headerBits, className }, ref) => { const actualClassName = [styles.header, className].join(' '); - const actualTitleBits = headerBits.filter(titleBit => titleBit); return ( - {actualTitleBits.map( - (titleBit, index) => - titleBit && ( -
    - {titleBit} + {headerBits.map( + (headerBit, index) => + headerBit && ( +
    + {headerBit}
    ), )} diff --git a/frontend/src/components/PreviousExecuteStatsPlot/index.tsx b/frontend/src/components/PreviousExecuteStatsPlot/index.tsx index c0d92d74..c25a559c 100644 --- a/frontend/src/components/PreviousExecuteStatsPlot/index.tsx +++ b/frontend/src/components/PreviousExecuteStatsPlot/index.tsx @@ -31,7 +31,7 @@ const PreviousExecutionsPlot: React.FC = ({ prevStats }) => { const entries = Object.entries(prevStats.sizeClasses); for (let i = 0; i < entries.length; ++i) { - const sizeClass = Number.parseInt(entries[i][0]); + const sizeClass = Number.parseInt(entries[i][0], 10); sizeClasses.push(sizeClass); for (const prevExec of entries[i][1].previousExecutions) { // TODO: Make random scatter deterministic for each data point diff --git a/frontend/src/components/SearchWidgets/index.tsx b/frontend/src/components/SearchWidgets/index.tsx index 97d871a7..58125690 100644 --- a/frontend/src/components/SearchWidgets/index.tsx +++ b/frontend/src/components/SearchWidgets/index.tsx @@ -1,14 +1,14 @@ -import React from "react"; +import type React from "react"; import { Button, Input, Space, Divider, DatePicker, - TimeRangePickerProps, + type TimeRangePickerProps, Tooltip, } from "antd"; -import { FilterDropdownProps } from "antd/es/table/interface"; +import type { FilterDropdownProps } from "antd/es/table/interface"; import dayjs from "dayjs"; import { blue } from "@ant-design/colors"; import styles from "@/components/SearchWidgets/index.module.css"; diff --git a/frontend/src/components/SiderBar/index.tsx b/frontend/src/components/SiderBar/index.tsx index 884309d0..295cde45 100644 --- a/frontend/src/components/SiderBar/index.tsx +++ b/frontend/src/components/SiderBar/index.tsx @@ -1,7 +1,8 @@ -import React, { Key, useState } from 'react'; -import { Layout, Menu, MenuProps } from 'antd'; -import { ItemType } from 'antd/es/menu/interface'; +import type React from 'react'; +import { type Key, useState } from 'react' +import { Layout, Menu, type MenuProps } from 'antd'; +import type { ItemType } from 'antd/es/menu/interface'; import styles from '@/components/SiderBar/index.module.css'; import { useLocation } from '@tanstack/react-router'; import { getClosestKey } from '../Utilities/navigation'; @@ -40,7 +41,7 @@ const SiderBar: React.FC = ({ const [siderWidth, setSiderWidth] = useState(() => { const cachedExpandedState = window.localStorage.getItem(SIDER_EXPANDED_WIDTH_LOCAL_STORAGE_KEY); if (cachedExpandedState) { - return parseInt(cachedExpandedState); + return parseInt(cachedExpandedState, 10); } return DEFAULT_SIDER_WIDTH; }); diff --git a/frontend/src/components/SizeClassOutcome/index.tsx b/frontend/src/components/SizeClassOutcome/index.tsx index 018f6753..1c86ccde 100644 --- a/frontend/src/components/SizeClassOutcome/index.tsx +++ b/frontend/src/components/SizeClassOutcome/index.tsx @@ -36,6 +36,12 @@ const SizeClassOutcome: React.FC = ({ sizeClassStats }) => { ); } + return ( + // biome-ignore lint/suspicious/noArrayIndexKey: We have nothing better to use + + Unknown + + ); })} diff --git a/frontend/src/components/SourceControlDisplay/index.tsx b/frontend/src/components/SourceControlDisplay/index.tsx index 3f933432..e937f838 100644 --- a/frontend/src/components/SourceControlDisplay/index.tsx +++ b/frontend/src/components/SourceControlDisplay/index.tsx @@ -4,7 +4,6 @@ import { } from "@/graphql/__generated__/graphql"; import { BranchesOutlined } from "@ant-design/icons"; import { Descriptions, Row, Space } from "antd"; -import { Link } from '@tanstack/react-router'; import type React from "react"; import PortalCard from "../PortalCard"; @@ -50,6 +49,7 @@ const getRefLabelAndUrl = ( .split("/")[0]; return ["Pull request",`${repoUrl}/pull/${prNumber}`]; } + return ["Ref",`${repoUrl}/tree/${sc.refs}`]; case SourceControlProvider.Gitlab: return ["Branch",`${repoUrl}/-/tree/${sc.refs}`]; default: @@ -125,7 +125,7 @@ type RepoLinkProps = { const RepoLink: React.FC = ({ text, url }) => { if (url) { return ( -
    + {text} ); @@ -136,7 +136,7 @@ const RepoLink: React.FC = ({ text, url }) => { const SourceControlDisplay: React.FC<{ stepLabel: string | undefined | null; sourceControlData: SourceControl | undefined | null; -}> = ({ sourceControlData, stepLabel }) => { +}> = ({ sourceControlData }) => { const repoUrl = getRepoUrl(sourceControlData); const [refLabel, refUrl] = getRefLabelAndUrl(sourceControlData, repoUrl); const commitUrl = getCommitUrl(sourceControlData, repoUrl); @@ -145,7 +145,7 @@ const SourceControlDisplay: React.FC<{ let workflowLabel = sourceControlData?.workflow || ""; const runNumber = sourceControlData?.runNumber || ""; - if (workflowLabel != "" && runNumber != "") { + if (workflowLabel !== "" && runNumber !== "") { workflowLabel = `${workflowLabel} #${runNumber}`; } diff --git a/frontend/src/components/SummaryPieChart/index.tsx b/frontend/src/components/SummaryPieChart/index.tsx index 662cff70..614c9456 100644 --- a/frontend/src/components/SummaryPieChart/index.tsx +++ b/frontend/src/components/SummaryPieChart/index.tsx @@ -1,7 +1,7 @@ import { Space, theme } from "antd"; -import { Link, LinkOptions } from '@tanstack/react-router'; +import { Link, type LinkOptions } from '@tanstack/react-router'; import { useState } from "react"; -import { Legend, LegendPayload, Pie, PieChart, PieSectorDataItem, PieSectorShapeProps, Sector } from "recharts"; +import { Legend, type LegendPayload, Pie, PieChart, type PieSectorDataItem, type PieSectorShapeProps, Sector } from "recharts"; import { themeColor } from "./utils"; import React from "react"; @@ -48,7 +48,7 @@ const SummaryPieChart: React.FC = ({ setHoverIndex(undefined); }; - const renderLegendText = (_value: any, entry: LegendPayload, index: number) => { + const renderLegendText = (_value: string, entry: LegendPayload, index: number) => { let item: SummaryChartItem | undefined if (entry.payload) { item = entry.payload as SummaryChartItem @@ -92,7 +92,7 @@ const SummaryPieChart: React.FC = ({ fill={fill} stroke="white" /> - {index == hoverIndex && + {index === hoverIndex && } - if (status == true) { + if (status === true) { return } return @@ -26,7 +26,7 @@ function getClassForStatus(status: boolean | null) { if (status == null) { return themeStyles.colorDisabled } - if (status == true) { + if (status === true) { return themeStyles.colorSuccess } return themeStyles.colorFailure @@ -34,7 +34,7 @@ function getClassForStatus(status: boolean | null) { const TargetGridBtn: React.FC = ({ status, invocationId }) => { const resultTag = diff --git a/frontend/src/components/AppBar/AppBarButtons.tsx b/frontend/src/components/AppBar/AppBarButtons.tsx index f4b47f2c..a81b4640 100644 --- a/frontend/src/components/AppBar/AppBarButtons.tsx +++ b/frontend/src/components/AppBar/AppBarButtons.tsx @@ -1,8 +1,7 @@ - -import type React from 'react'; -import { BulbOutlined, GithubOutlined } from '@ant-design/icons'; -import AppBarButton from '@/components/AppBar/AppBarButton'; -import styles from '@/components/AppBar/index.module.css'; +import { BulbOutlined, GithubOutlined } from "@ant-design/icons"; +import type React from "react"; +import AppBarButton from "@/components/AppBar/AppBarButton"; +import styles from "@/components/AppBar/index.module.css"; type Props = { toggleTheme: () => void; @@ -12,10 +11,14 @@ type Props = { const AppBarButtons: React.FC = ({ toggleTheme, prefersDark }) => { return (
    - } title="Github" href="https://github.com/buildbarn/bb-portal" /> + } + title="Github" + href="https://github.com/buildbarn/bb-portal" + /> } - title={`${prefersDark ? 'Light' : 'Dark'} Mode`} + title={`${prefersDark ? "Light" : "Dark"} Mode`} onMouseDown={toggleTheme} />
    diff --git a/frontend/src/components/AppBar/AppBarMenu.tsx b/frontend/src/components/AppBar/AppBarMenu.tsx index 1e42157b..4c1bac98 100644 --- a/frontend/src/components/AppBar/AppBarMenu.tsx +++ b/frontend/src/components/AppBar/AppBarMenu.tsx @@ -1,11 +1,10 @@ - -import { getClosestKey } from "@/components/Utilities/navigation"; +import { useLocation } from "@tanstack/react-router"; import { Menu } from "antd"; import type { ItemType } from "antd/lib/menu/interface"; import type { MenuMode } from "rc-menu/es/interface"; import type React from "react"; +import { getClosestKey } from "@/components/Utilities/navigation"; import styles from "./index.module.css"; -import { useLocation } from "@tanstack/react-router"; type Props = { mode: MenuMode; diff --git a/frontend/src/components/AppBar/AppBarTitle.tsx b/frontend/src/components/AppBar/AppBarTitle.tsx index feeb15fc..71eb8caa 100644 --- a/frontend/src/components/AppBar/AppBarTitle.tsx +++ b/frontend/src/components/AppBar/AppBarTitle.tsx @@ -1,12 +1,12 @@ +import { Link } from "@tanstack/react-router"; import { Typography } from "antd"; import { env } from "@/utils/env"; -import { Link } from '@tanstack/react-router'; const AppBarTitle = () => { return ( - {`${env.companyName ? `${env.companyName} ` : ''}Buildbarn Portal`} + {`${env.companyName ? `${env.companyName} ` : ""}Buildbarn Portal`} ); diff --git a/frontend/src/components/AppBar/index.tsx b/frontend/src/components/AppBar/index.tsx index 39d1f5f6..1d102dba 100644 --- a/frontend/src/components/AppBar/index.tsx +++ b/frontend/src/components/AppBar/index.tsx @@ -1,45 +1,80 @@ - -import AppBarButtons from '@/components/AppBar/AppBarButtons'; -import AppBarMenu from '@/components/AppBar/AppBarMenu'; -import AppBarTitle from '@/components/AppBar/AppBarTitle'; -import styles from '@/components/AppBar/index.module.css'; -import { SIDER_BAR_MINIMUM_SCREEN_WIDTH } from '@/components/Content'; -import FooterBar from '@/components/FooterBar'; -import { getItem } from '@/components/Utilities/navigation'; -import { env } from '@/utils/env'; -import useScreenSize from '@/utils/screen'; -import { MenuOutlined } from '@ant-design/icons'; -import { Button, Divider, Drawer, Layout } from 'antd'; -import type { ItemType } from 'antd/lib/menu/interface'; -import type React from 'react'; -import { useEffect, useState } from 'react'; +import { MenuOutlined } from "@ant-design/icons"; +import { Button, Divider, Drawer, Layout } from "antd"; +import type { ItemType } from "antd/lib/menu/interface"; +import type React from "react"; +import { useEffect, useState } from "react"; +import AppBarButtons from "@/components/AppBar/AppBarButtons"; +import AppBarMenu from "@/components/AppBar/AppBarMenu"; +import AppBarTitle from "@/components/AppBar/AppBarTitle"; +import styles from "@/components/AppBar/index.module.css"; +import { SIDER_BAR_MINIMUM_SCREEN_WIDTH } from "@/components/Content"; +import FooterBar from "@/components/FooterBar"; +import { getItem } from "@/components/Utilities/navigation"; +import { env } from "@/utils/env"; +import useScreenSize from "@/utils/screen"; const getAppBarMenuItems = (): ItemType[] => { const items: (ItemType | undefined)[] = [ - getItem({ depth: 0, href: '/builds', title: 'Builds', requiredFeatures: [env.featureFlags?.bes?.pageBuilds] }), - getItem({ depth: 0, href: '/bazel-invocations', title: 'Invocations', requiredFeatures: [env.featureFlags?.bes?.pageInvocations] }), - getItem({ depth: 0, href: '/trends', title: 'Trends', requiredFeatures: [env.featureFlags?.bes?.pageTrends] }), - getItem({ depth: 0, href: '/tests', title: 'Tests', requiredFeatures: [env.featureFlags?.bes?.pageTests] }), - getItem({ depth: 0, href: '/targets', title: 'Targets', requiredFeatures: [env.featureFlags?.bes?.pageTargets] }), - getItem({ depth: 0, href: '/browser', title: 'Browser', requiredFeatures: [env.featureFlags?.browser] }), - getItem({ depth: 0, href: '/scheduler', title: 'Scheduler', requiredFeatures: [env.featureFlags?.scheduler] }), - getItem({ depth: 0, href: '/operations', title: 'Operations', requiredFeatures: [env.featureFlags?.scheduler] }), + getItem({ + depth: 0, + href: "/builds", + title: "Builds", + requiredFeatures: [env.featureFlags?.bes?.pageBuilds], + }), + getItem({ + depth: 0, + href: "/bazel-invocations", + title: "Invocations", + requiredFeatures: [env.featureFlags?.bes?.pageInvocations], + }), + getItem({ + depth: 0, + href: "/trends", + title: "Trends", + requiredFeatures: [env.featureFlags?.bes?.pageTrends], + }), + getItem({ + depth: 0, + href: "/tests", + title: "Tests", + requiredFeatures: [env.featureFlags?.bes?.pageTests], + }), + getItem({ + depth: 0, + href: "/targets", + title: "Targets", + requiredFeatures: [env.featureFlags?.bes?.pageTargets], + }), + getItem({ + depth: 0, + href: "/browser", + title: "Browser", + requiredFeatures: [env.featureFlags?.browser], + }), + getItem({ + depth: 0, + href: "/scheduler", + title: "Scheduler", + requiredFeatures: [env.featureFlags?.scheduler], + }), + getItem({ + depth: 0, + href: "/operations", + title: "Operations", + requiredFeatures: [env.featureFlags?.scheduler], + }), ]; return items.filter((item): item is ItemType => item !== undefined); -} - -const APP_BAR_MENU_ITEMS: ItemType[] = getAppBarMenuItems() +}; +const APP_BAR_MENU_ITEMS: ItemType[] = getAppBarMenuItems(); type Props = { toggleTheme: () => void; prefersDark: boolean; }; -const AppBar: React.FC = ({ - toggleTheme, - prefersDark, -}) => { +const AppBar: React.FC = ({ toggleTheme, prefersDark }) => { const screenSize = useScreenSize(); const showHeaderMenu = screenSize.width > SIDER_BAR_MINIMUM_SCREEN_WIDTH; const [isDrawerOpen, setIsDrawerOpen] = useState(false); @@ -82,9 +117,7 @@ const AppBar: React.FC = ({ setIsDrawerOpen(false); }} open={isDrawerOpen} - footer={ - - } + footer={} > diff --git a/frontend/src/components/Artifacts/index.tsx b/frontend/src/components/Artifacts/index.tsx index 98aa3483..10c8475d 100644 --- a/frontend/src/components/Artifacts/index.tsx +++ b/frontend/src/components/Artifacts/index.tsx @@ -1,87 +1,96 @@ -import type React from "react"; -import { Table, Row, Col, Space } from 'antd'; +import { RadiusUprightOutlined } from "@ant-design/icons"; +import { Col, Row, Space, Table } from "antd"; import type { TableColumnsType } from "antd/lib"; +import type React from "react"; import type { ArtifactMetrics } from "@/graphql/__generated__/graphql"; -import PortalCard from "../PortalCard"; -import { RadiusUprightOutlined } from "@ant-design/icons"; -import styles from "../../theme/theme.module.css" import { readableFileSize } from "@/utils/filesize"; - +import styles from "../../theme/theme.module.css"; +import PortalCard from "../PortalCard"; const artifacts_columns: TableColumnsType = [ + { + title: "Type", + dataIndex: "name", + }, + { + title: "Size", + dataIndex: "sizeInBytes", + align: "right", + render: (_, record) => ( + + {readableFileSize(record.sizeInBytes)} + + ), + sorter: (a, b) => (a.sizeInBytes ?? 0) - (b.sizeInBytes ?? 0), + }, + { + title: "Count", + dataIndex: "count", + align: "right", + render: (_, record) => ( + {record.count} + ), + sorter: (a, b) => (a.count ?? 0) - (b.count ?? 0), + }, +]; + +interface ArtifactMetricsTableData { + name: string; + sizeInBytes: number; + count: number; +} + +const ArtifactsDataMetrics: React.FC<{ + artifactMetrics: ArtifactMetrics | undefined; +}> = ({ artifactMetrics }) => { + const artifacts_data: ArtifactMetricsTableData[] = []; + artifacts_data.push( { - title: "Type", - dataIndex: "name" + name: "Source Artifacts Read", + sizeInBytes: artifactMetrics?.sourceArtifactsReadSizeInBytes ?? 0, + count: artifactMetrics?.sourceArtifactsReadCount ?? 0, }, { - title: "Size", - dataIndex: "sizeInBytes", - align: "right", - render: (_, record) => {readableFileSize(record.sizeInBytes)}, - sorter: (a, b) => (a.sizeInBytes ?? 0) - (b.sizeInBytes ?? 0), + name: "Output Artifacts From Action Cache", + sizeInBytes: + artifactMetrics?.outputArtifactsFromActionCacheSizeInBytes ?? 0, + count: artifactMetrics?.outputArtifactsFromActionCacheCount ?? 0, }, { - title: "Count", - dataIndex: "count", - align: "right", - render: (_, record) => {record.count}, - sorter: (a, b) => (a.count ?? 0) - (b.count ?? 0), + name: "Output Artifacts Seen", + sizeInBytes: artifactMetrics?.outputArtifactsSeenSizeInBytes ?? 0, + count: artifactMetrics?.outputArtifactsSeenCount ?? 0, }, -] - - -interface ArtifactMetricsTableData { - name: string; - sizeInBytes: number; - count: number; -} - -const ArtifactsDataMetrics: React.FC<{ artifactMetrics: ArtifactMetrics | undefined; }> = ({ artifactMetrics }) => { - - const artifacts_data: ArtifactMetricsTableData[] = []; - artifacts_data.push( - { - name: "Source Artifacts Read", - sizeInBytes: artifactMetrics?.sourceArtifactsReadSizeInBytes ?? 0, - count: artifactMetrics?.sourceArtifactsReadCount ?? 0 - }, - { - name: "Output Artifacts From Action Cache", - sizeInBytes: artifactMetrics?.outputArtifactsFromActionCacheSizeInBytes ?? 0, - count: artifactMetrics?.outputArtifactsFromActionCacheCount ?? 0 - }, - { - name: "Output Artifacts Seen", - sizeInBytes: artifactMetrics?.outputArtifactsSeenSizeInBytes ?? 0, - count: artifactMetrics?.outputArtifactsSeenCount ?? 0 - }, - { - name: "Top Level Artifacts", - sizeInBytes: artifactMetrics?.topLevelArtifactsSizeInBytes ?? 0, - count: artifactMetrics?.topLevelArtifactsCount ?? 0 - }, - ) - + { + name: "Top Level Artifacts", + sizeInBytes: artifactMetrics?.topLevelArtifactsSizeInBytes ?? 0, + count: artifactMetrics?.topLevelArtifactsCount ?? 0, + }, + ); - const actionsTitle: React.ReactNode[] = [Artifacts]; + const actionsTitle: React.ReactNode[] = [Artifacts]; - return ( - - } titleBits={actionsTitle}> - - - - - - - - - ) -} + return ( + + } + titleBits={actionsTitle} + > + + +
    + + + + + + ); +}; -export default ArtifactsDataMetrics; \ No newline at end of file +export default ArtifactsDataMetrics; diff --git a/frontend/src/components/BazelInvocation/index.tsx b/frontend/src/components/BazelInvocation/index.tsx index ecf0d267..4a099fa6 100644 --- a/frontend/src/components/BazelInvocation/index.tsx +++ b/frontend/src/components/BazelInvocation/index.tsx @@ -1,16 +1,3 @@ -import ArtifactsDataMetrics from "../Artifacts"; -import MemoryMetricsDisplay from "../MemoryMetrics"; -import SystemMetricsDisplay from "../SystemMetricsDisplay"; -import CommandLineDisplay from "../CommandLine"; -import SourceControlDisplay from "../SourceControlDisplay"; -import InvocationOverviewDisplay from "../InvocationOverviewDisplay"; -import PortalCard from "@/components/PortalCard"; -import PortalDuration from "@/components/PortalDuration"; -import type { - BazelInvocationInfoFragment, - RunnerCount -} from "@/graphql/__generated__/graphql"; -import themeStyles from "@/theme/theme.module.css"; import { AreaChartOutlined, BranchesOutlined, @@ -21,29 +8,45 @@ import { ExperimentOutlined, FieldTimeOutlined, FileSearchOutlined, - InfoCircleOutlined, LineChartOutlined, - RadiusUprightOutlined + InfoCircleOutlined, + LineChartOutlined, + RadiusUprightOutlined, } from "@ant-design/icons"; +import { Link } from "@tanstack/react-router"; import { Space, Tabs, Typography } from "antd"; import type { TabsProps } from "antd/lib"; import type React from "react"; import { useMemo, useState } from "react"; +import PortalCard from "@/components/PortalCard"; +import PortalDuration from "@/components/PortalDuration"; +import type { + BazelInvocationInfoFragment, + RunnerCount, +} from "@/graphql/__generated__/graphql"; +import themeStyles from "@/theme/theme.module.css"; +import { commandLineDataToString } from "@/utils/commandLineDataToString"; +import { env } from "@/utils/env"; import ActionStatisticsDisplay from "../ActionStatisticsDisplay"; +import { ActionsTab } from "../ActionsTab"; import styles from "../AppBar/index.module.css"; +import ArtifactsDataMetrics from "../Artifacts"; import BuildLogsDisplay from "../BuildLogsDisplay"; +import CommandLineDisplay from "../CommandLine"; +import InvocationOverviewDisplay from "../InvocationOverviewDisplay"; +import { InvocationResultTag } from "../InvocationResultTag"; +import { InvocationTargetsTab } from "../InvocationTargets/InvocationTargetsTab"; +import MemoryMetricsDisplay from "../MemoryMetrics"; import ProfileDropdown from "../ProfileDropdown"; +import SourceControlDisplay from "../SourceControlDisplay"; +import SystemMetricsDisplay from "../SystemMetricsDisplay"; import { TestTab } from "../TestTab"; -import { InvocationTargetsTab } from "../InvocationTargets/InvocationTargetsTab"; import UserStatusIndicator from "../UserStatusIndicator"; -import { InvocationResultTag } from "../InvocationResultTag"; -import { ActionsTab } from "../ActionsTab"; -import { commandLineDataToString } from "@/utils/commandLineDataToString"; -import { Link } from "@tanstack/react-router"; -import { env } from "@/utils/env"; const DEFAULT_TAB_KEY = "BazelInvocationTabs-Overview"; -const getTabItems = (invocationOverview: BazelInvocationInfoFragment): TabsProps["items"] => { +const getTabItems = ( + invocationOverview: BazelInvocationInfoFragment, +): TabsProps["items"] => { const { actions, invocationID, @@ -63,22 +66,27 @@ const getTabItems = (invocationOverview: BazelInvocationInfoFragment): TabsProps var runnerMetrics: RunnerCount[] = []; metrics?.actionSummary?.runnerCount?.map((item: RunnerCount) => - runnerMetrics.push(item) + runnerMetrics.push(item), ); - const hideActionStatisticsTab: boolean = metrics?.actionSummary === undefined || metrics?.actionSummary == null; + const hideActionStatisticsTab: boolean = + metrics?.actionSummary === undefined || metrics?.actionSummary == null; const hideLogsTab: boolean = false; - const hideArtifactsTab: boolean = metrics?.artifactMetrics === undefined || metrics?.artifactMetrics == null; - const hideMemoryTab: boolean = metrics?.memoryMetrics === undefined || metrics?.memoryMetrics == null; + const hideArtifactsTab: boolean = + metrics?.artifactMetrics === undefined || metrics?.artifactMetrics == null; + const hideMemoryTab: boolean = + metrics?.memoryMetrics === undefined || metrics?.memoryMetrics == null; const hideSystemMetricsTab: boolean = - (metrics?.timingMetrics === undefined || metrics?.timingMetrics == null) - && (metrics?.networkMetrics === undefined || metrics?.networkMetrics == null); - const hideFailedActionsTab: boolean = actions === undefined || actions == null || actions.length === 0; + (metrics?.timingMetrics === undefined || metrics?.timingMetrics == null) && + (metrics?.networkMetrics === undefined || metrics?.networkMetrics == null); + const hideFailedActionsTab: boolean = + actions === undefined || actions == null || actions.length === 0; const hideTargetsTab: boolean = !env.featureFlags?.bes?.pageTargets; const hideTestsTab: boolean = !env.featureFlags?.bes?.pageTests; - const hideSourceControlTab: boolean = sourceControl === undefined || sourceControl == null; + const hideSourceControlTab: boolean = + sourceControl === undefined || sourceControl == null; - const command = commandLineDataToString(originalCommandLine) + const command = commandLineDataToString(originalCommandLine); const items: TabsProps["items"] = []; items.push({ @@ -118,62 +126,73 @@ const getTabItems = (invocationOverview: BazelInvocationInfoFragment): TabsProps ), }); - if (!hideActionStatisticsTab) items.push({ - key: "BazelInvocationTabs-ActionStatistics", - label: "Action Statistics", - icon: , - children: ( - - - - ), - }); - if (!hideLogsTab) items.push({ - key: "BazelInvocationTabs-Logs", - label: "Logs", - icon: , - children: ( - - - - ), - }); - if (!hideArtifactsTab) items.push({ - key: "BazelInvocationTabs-Artifacts", - label: "Artifacts", - icon: , - children: ( - - - - ), - }); - if (!hideMemoryTab) items.push({ - key: "BazelInvocationTabs-Memory", - label: "Memory", - icon: , - children: ( - - - - ), - }); - if (!hideSystemMetricsTab) items.push({ - key: "BazelInvocationTabs-SystemMetrics", - label: "System Metrics", - icon: , - children: ( - - - - ), - }); + if (!hideActionStatisticsTab) + items.push({ + key: "BazelInvocationTabs-ActionStatistics", + label: "Action Statistics", + icon: , + children: ( + + + + ), + }); + if (!hideLogsTab) + items.push({ + key: "BazelInvocationTabs-Logs", + label: "Logs", + icon: , + children: ( + + + + ), + }); + if (!hideArtifactsTab) + items.push({ + key: "BazelInvocationTabs-Artifacts", + label: "Artifacts", + icon: , + children: ( + + + + ), + }); + if (!hideMemoryTab) + items.push({ + key: "BazelInvocationTabs-Memory", + label: "Memory", + icon: , + children: ( + + + + ), + }); + if (!hideSystemMetricsTab) + items.push({ + key: "BazelInvocationTabs-SystemMetrics", + label: "System Metrics", + icon: , + children: ( + + + + ), + }); if (!hideTargetsTab) items.push({ key: "BazelInvocationTabs-Targets", @@ -188,125 +207,144 @@ const getTabItems = (invocationOverview: BazelInvocationInfoFragment): TabsProps ), }); - if (!hideTestsTab) items.push({ - key: "BazelInvocationTabs-Tests", - label: "Tests", - icon: , - children: ( - - - - ), - }); + if (!hideTestsTab) + items.push({ + key: "BazelInvocationTabs-Tests", + label: "Tests", + icon: , + children: ( + + + + ), + }); items.push({ key: "BazelInvocationTabs-CommandLine", label: "Command Line", icon: , children: ( - - - ), - }); - if (!hideSourceControlTab) items.push({ - key: "BazelInvocationTabs-SourceControl", - label: "Source Control", - icon: , - children: ( - - ), }); - if (!hideFailedActionsTab) items.push({ - key: "BazelInvocationTabs-Actions", - label: "Failed Actions", - icon: , - children: ( - - - - ), - }); + if (!hideSourceControlTab) + items.push({ + key: "BazelInvocationTabs-SourceControl", + label: "Source Control", + icon: , + children: ( + + + + ), + }); + if (!hideFailedActionsTab) + items.push({ + key: "BazelInvocationTabs-Actions", + label: "Failed Actions", + icon: , + children: ( + + + + ), + }); return items; -} +}; -const getTitleBits = (invocationOverview: BazelInvocationInfoFragment): React.ReactNode[] => { - const { - invocationID, - authenticatedUser, - user, - } = invocationOverview; +const getTitleBits = ( + invocationOverview: BazelInvocationInfoFragment, +): React.ReactNode[] => { + const { invocationID, authenticatedUser, user } = invocationOverview; - const titleBits: React.ReactNode[] = [] - if (user?.LDAP && user?.LDAP !== "") titleBits.push( - - User:{" "} - - - - - ); - if (invocationID && invocationID !== "") titleBits.push( - - Invocation ID:{" "} - - {invocationID} - {" "} - - ); + const titleBits: React.ReactNode[] = []; + if (user?.LDAP && user?.LDAP !== "") + titleBits.push( + + User:{" "} + + + + , + ); + if (invocationID && invocationID !== "") + titleBits.push( + + Invocation ID:{" "} + + {invocationID} + {" "} + , + ); titleBits.push( + timeSinceLastConnectionMillis={ + invocationOverview.connectionMetadata?.timeSinceLastConnectionMillis || + undefined + } + />, ); return titleBits; -} +}; -const getExtraBits = (invocationOverview: BazelInvocationInfoFragment): React.ReactNode[] => { - const { - invocationID, - instanceName, - build, - profile, - } = invocationOverview; +const getExtraBits = ( + invocationOverview: BazelInvocationInfoFragment, +): React.ReactNode[] => { + const { invocationID, instanceName, build, profile } = invocationOverview; const extraBits: React.ReactNode[] = []; extraBits.push( - ) - if (profile) extraBits.push( - , ); + if (profile) + extraBits.push( + , + ); if (build?.buildUUID) { extraBits.unshift( - Build {build.buildUUID} - + Build{" "} + + {build.buildUUID} + + , ); } return extraBits; -} +}; interface Props { invocationOverview: BazelInvocationInfoFragment; @@ -315,7 +353,7 @@ interface Props { const BazelInvocation: React.FC = ({ invocationOverview }) => { const [activeKey, setActiveKey] = useState( localStorage.getItem("bazelInvocationViewActiveTabKey") ?? - "BazelInvocationTabs-Overview" + "BazelInvocationTabs-Overview", ); const onTabChange = (key: string) => { @@ -325,21 +363,21 @@ const BazelInvocation: React.FC = ({ invocationOverview }) => { const titleBits = useMemo( () => getTitleBits(invocationOverview), - [invocationOverview] + [invocationOverview], ); const extraBits = useMemo( () => getExtraBits(invocationOverview), - [invocationOverview] + [invocationOverview], ); const items = useMemo( () => getTabItems(invocationOverview), - [invocationOverview] + [invocationOverview], ); function checkIfNotHidden(key: string) { - const hidden = items?.findIndex((x) => x.key === key) === -1 + const hidden = items?.findIndex((x) => x.key === key) === -1; return hidden ? DEFAULT_TAB_KEY : key; } diff --git a/frontend/src/components/BazelInvocationColumns/Columns.tsx b/frontend/src/components/BazelInvocationColumns/Columns.tsx index 7a97f795..a33fdb0a 100644 --- a/frontend/src/components/BazelInvocationColumns/Columns.tsx +++ b/frontend/src/components/BazelInvocationColumns/Columns.tsx @@ -1,112 +1,147 @@ -import type { ColumnType } from 'antd/lib/table'; -import { Typography } from 'antd'; -import { - ClockCircleFilled, - SearchOutlined, -} from '@ant-design/icons'; -import { Link } from '@tanstack/react-router'; -import dayjs from 'dayjs'; -import styles from './Columns.module.css'; -import type { BazelInvocationNodeFragment, BazelInvocationWhereInput } from '@/graphql/__generated__/graphql'; -import { SearchFilterIcon, SearchWidget, TimeRangeSelector } from '@/components/SearchWidgets'; +import { ClockCircleFilled, SearchOutlined } from "@ant-design/icons"; +import { Link } from "@tanstack/react-router"; +import { Typography } from "antd"; +import type { FilterValue } from "antd/es/table/interface"; +import type { ColumnType } from "antd/lib/table"; +import dayjs from "dayjs"; import PortalDuration from "@/components/PortalDuration"; -import UserStatusIndicator from '../UserStatusIndicator'; -import { InvocationResultTag } from '../InvocationResultTag'; -import type { FilterValue } from 'antd/es/table/interface'; -import { applyInvocationResultTagFilter, invocationResultTagFilters } from '../InvocationResultTag/filters'; +import { + SearchFilterIcon, + SearchWidget, + TimeRangeSelector, +} from "@/components/SearchWidgets"; +import type { + BazelInvocationNodeFragment, + BazelInvocationWhereInput, +} from "@/graphql/__generated__/graphql"; +import { InvocationResultTag } from "../InvocationResultTag"; +import { + applyInvocationResultTagFilter, + invocationResultTagFilters, +} from "../InvocationResultTag/filters"; +import UserStatusIndicator from "../UserStatusIndicator"; +import styles from "./Columns.module.css"; type ColumnTypeWithFilter = ColumnType & { applyFilter?: (value: FilterValue) => BazelInvocationWhereInput[] | undefined; }; -export const invocationIdColumn: ColumnTypeWithFilter = { - key: 'invocationID', - width: 220, - title: 'Invocation', - render: (_, record) => ( - - {record.invocationID} - - ), - filterDropdown: filterProps => ( - - ), - filterIcon: filtered => } filtered={filtered} />, - applyFilter: (value: FilterValue) => { - if (value.length === 0) { - return undefined - } - return [{ invocationID: value[0] as string }]; - }, -}; +export const invocationIdColumn: ColumnTypeWithFilter = + { + key: "invocationID", + width: 220, + title: "Invocation", + render: (_, record) => ( + + {record.invocationID} + + ), + filterDropdown: (filterProps) => ( + + ), + filterIcon: (filtered) => ( + } filtered={filtered} /> + ), + applyFilter: (value: FilterValue) => { + if (value.length === 0) { + return undefined; + } + return [{ invocationID: value[0] as string }]; + }, + }; -export const startedAtColumn: ColumnTypeWithFilter = { - key: 'startedAt', - width: 165, - title: 'Start Time', - render: (_, record) => ( - - {dayjs(record.startedAt).format('YYYY-MM-DD hh:mm:ss A')} - - ), - filterDropdown: filterProps => , - filterIcon: filtered => } filtered={filtered} />, - applyFilter: (value: FilterValue) => { - if (value.length !== 2) { - return undefined; - } - const filter: BazelInvocationWhereInput[] = []; - if (value[0]) { - filter.push({ startedAtGTE: value[0] }); - } - if (value[1]) { - filter.push({ startedAtLTE: value[1] }); - } - return filter; - }, -}; +export const startedAtColumn: ColumnTypeWithFilter = + { + key: "startedAt", + width: 165, + title: "Start Time", + render: (_, record) => ( + + {dayjs(record.startedAt).format("YYYY-MM-DD hh:mm:ss A")} + + ), + filterDropdown: (filterProps) => , + filterIcon: (filtered) => ( + } filtered={filtered} /> + ), + applyFilter: (value: FilterValue) => { + if (value.length !== 2) { + return undefined; + } + const filter: BazelInvocationWhereInput[] = []; + if (value[0]) { + filter.push({ startedAtGTE: value[0] }); + } + if (value[1]) { + filter.push({ startedAtLTE: value[1] }); + } + return filter; + }, + }; -export const durationColumn: ColumnTypeWithFilter = { - key: 'duration', - width: 100, - title: 'Duration', - render: (_, record) => ( - - ), -}; +export const durationColumn: ColumnTypeWithFilter = + { + key: "duration", + width: 100, + title: "Duration", + render: (_, record) => ( + + ), + }; export const statusColumn: ColumnTypeWithFilter = { - key: 'result', + key: "result", width: 120, - title: 'Result', - render: (_, record) => , - filterIcon: filtered => } filtered={filtered} />, + title: "Result", + render: (_, record) => ( + + ), + filterIcon: (filtered) => ( + } filtered={filtered} /> + ), filters: invocationResultTagFilters, applyFilter: applyInvocationResultTagFilter, }; export const buildColumn: ColumnTypeWithFilter = { - key: 'build', + key: "build", width: 220, - title: 'Build', - render: (_, record) => record.build && ( - - {record.build.buildUUID} - ), - filterDropdown: filterProps => ( + title: "Build", + render: (_, record) => + record.build && ( + + {record.build.buildUUID} + + ), + filterDropdown: (filterProps) => ( ), - filterIcon: filtered => } filtered={filtered} />, + filterIcon: (filtered) => ( + } filtered={filtered} /> + ), applyFilter: (value: FilterValue) => { if (value.length === 0) { return undefined; @@ -116,19 +151,23 @@ export const buildColumn: ColumnTypeWithFilter = { }; export const userColumn: ColumnTypeWithFilter = { - key: 'user', + key: "user", width: 120, title: "User", render: (_, record) => { - return + return ( + + ); }, - filterDropdown: filterProps => ( + filterDropdown: (filterProps) => ( ), - filterIcon: filtered => } filtered={filtered} />, + filterIcon: (filtered) => ( + } filtered={filtered} /> + ), applyFilter: (value: FilterValue) => { if (value.length === 0) { return undefined; diff --git a/frontend/src/components/BazelInvocationsTable/index.tsx b/frontend/src/components/BazelInvocationsTable/index.tsx index 946b2f78..093e12ce 100644 --- a/frontend/src/components/BazelInvocationsTable/index.tsx +++ b/frontend/src/components/BazelInvocationsTable/index.tsx @@ -1,3 +1,8 @@ +import { BuildOutlined } from "@ant-design/icons"; +import { useQuery } from "@apollo/client/react"; +import { Space, Typography } from "antd"; +import type { FilterValue } from "antd/lib/table/interface"; +import React from "react"; import { buildColumn, durationColumn, @@ -6,25 +11,21 @@ import { statusColumn, userColumn, } from "@/components/BazelInvocationColumns/Columns"; -import FIND_BAZEL_INVOCATIONS_QUERY, { - BAZEL_INVOCATION_NODE_FRAGMENT, -} from './query.graphql'; import { BazelInvocationOrderField, - type BazelInvocationWhereInput, OrderDirection -} from '@/graphql/__generated__/graphql'; -import themeStyles from '@/theme/theme.module.css'; -import { BuildOutlined } from '@ant-design/icons'; -import { useQuery } from '@apollo/client/react'; -import { Space, Typography } from 'antd'; -import type { FilterValue } from 'antd/lib/table/interface'; -import React from 'react'; -import { CursorTable, getNewPaginationVariables } from '../CursorTable'; -import type { PaginationVariables } from '../CursorTable/types'; -import PortalAlert from "../PortalAlert"; + type BazelInvocationWhereInput, + OrderDirection, +} from "@/graphql/__generated__/graphql"; +import themeStyles from "@/theme/theme.module.css"; import styles from "@/theme/theme.module.css"; import { parseGraphqlEdgeListWithFragment } from "@/utils/parseGraphqlEdgeList"; import { shouldPollInvocation } from "@/utils/shouldPollInvocation"; +import { CursorTable, getNewPaginationVariables } from "../CursorTable"; +import type { PaginationVariables } from "../CursorTable/types"; +import PortalAlert from "../PortalAlert"; +import FIND_BAZEL_INVOCATIONS_QUERY, { + BAZEL_INVOCATION_NODE_FRAGMENT, +} from "./query.graphql"; const BazelInvocationsTable: React.FC = () => { const [paginationVariables, setPaginationVariables] = @@ -111,13 +112,13 @@ const BazelInvocationsTable: React.FC = () => { ); } - const emptyText = 'No Bazel invocations match the specified search criteria'; + const emptyText = "No Bazel invocations match the specified search criteria"; return ( item.id} + rowKey={(item) => item.id} loading={loading} size="small" locale={{ diff --git a/frontend/src/components/Breadcrumbs/index.tsx b/frontend/src/components/Breadcrumbs/index.tsx index 34427dc9..b17a687b 100644 --- a/frontend/src/components/Breadcrumbs/index.tsx +++ b/frontend/src/components/Breadcrumbs/index.tsx @@ -1,30 +1,31 @@ - -import type React from 'react'; -import { useMemo } from 'react'; -import { useLocation } from '@tanstack/react-router'; -import { Breadcrumb } from 'antd'; -import type { ItemType } from 'antd/es/breadcrumb/Breadcrumb'; -import { Link } from '@tanstack/react-router'; -import styles from '@/components/Breadcrumbs/index.module.css'; -import BuildbarnIcon from '../BuildbarnIcon'; +import { Link, useLocation } from "@tanstack/react-router"; +import { Breadcrumb } from "antd"; +import type { ItemType } from "antd/es/breadcrumb/Breadcrumb"; +import type React from "react"; +import { useMemo } from "react"; +import styles from "@/components/Breadcrumbs/index.module.css"; +import BuildbarnIcon from "../BuildbarnIcon"; const itemRender = (currentRoute: ItemType) => { - return {currentRoute.title} -} + return {currentRoute.title}; +}; export const Breadcrumbs: React.FC = () => { const { pathname } = useLocation(); const breadcrumbItems = useMemo(() => { - const items: ItemType[] = [{ - path: '/', - title: , - }]; + const items: ItemType[] = [ + { + path: "/", + title: , + }, + ]; - let cumulativePath = ''; - pathname.split('/') - .filter(segment => segment !== '') - .forEach(segment => { + let cumulativePath = ""; + pathname + .split("/") + .filter((segment) => segment !== "") + .forEach((segment) => { cumulativePath += `/${segment}`; items.push({ path: cumulativePath, diff --git a/frontend/src/components/BrowserActionGrid/CopyBbClientdActionButton.tsx b/frontend/src/components/BrowserActionGrid/CopyBbClientdActionButton.tsx index 532646bd..adf79728 100644 --- a/frontend/src/components/BrowserActionGrid/CopyBbClientdActionButton.tsx +++ b/frontend/src/components/BrowserActionGrid/CopyBbClientdActionButton.tsx @@ -1,9 +1,8 @@ - +import { Button, message } from "antd"; +import type React from "react"; import type { Digest } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; import type { BrowserPageParams } from "@/types/BrowserPageType"; import { getBBClientdPath } from "@/utils/getBbClientdPath"; -import { Button, message } from "antd"; -import type React from "react"; interface Params { browserPageParams: BrowserPageParams; diff --git a/frontend/src/components/BrowserActionGrid/fetch.ts b/frontend/src/components/BrowserActionGrid/fetch.ts index 75694028..cf06d750 100644 --- a/frontend/src/components/BrowserActionGrid/fetch.ts +++ b/frontend/src/components/BrowserActionGrid/fetch.ts @@ -269,12 +269,12 @@ function extractMetadataFromExecuteResponse( inputRootResourceUsage: InputRootResourceUsage | undefined; monetaryResourceUsage: MonetaryResourceUsage | undefined; } { - let authenticationMetadata: AuthenticationMetadata | undefined ; - let requestMetadata: RequestMetadata | undefined ; - let posixResourceUsage: POSIXResourceUsage | undefined ; - let filePoolResourceUsage: FilePoolResourceUsage | undefined ; - let inputRootResourceUsage: InputRootResourceUsage | undefined ; - let monetaryResourceUsage: MonetaryResourceUsage | undefined ; + let authenticationMetadata: AuthenticationMetadata | undefined; + let requestMetadata: RequestMetadata | undefined; + let posixResourceUsage: POSIXResourceUsage | undefined; + let filePoolResourceUsage: FilePoolResourceUsage | undefined; + let inputRootResourceUsage: InputRootResourceUsage | undefined; + let monetaryResourceUsage: MonetaryResourceUsage | undefined; if (!executeResponse?.result?.executionMetadata?.auxiliaryMetadata) { return { diff --git a/frontend/src/components/BrowserActionGrid/index.tsx b/frontend/src/components/BrowserActionGrid/index.tsx index 55c725b1..23f4ae18 100644 --- a/frontend/src/components/BrowserActionGrid/index.tsx +++ b/frontend/src/components/BrowserActionGrid/index.tsx @@ -1,18 +1,17 @@ - import { useQuery } from "@tanstack/react-query"; +import { Link } from "@tanstack/react-router"; import { Descriptions, Space, Spin, Typography } from "antd"; -import { Link } from '@tanstack/react-router'; import type React from "react"; import { useEffect, useState } from "react"; import { useGrpcClients } from "@/context/GrpcClientsContext"; +import type { Digest } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; import { FileSystemAccessProfileReference } from "@/lib/grpc-client/buildbarn/query/query"; -import { BrowserPageType, type BrowserPageParams } from "@/types/BrowserPageType"; -import { - PATH_HASH_BASE_HASH -} from "@/utils/bloomFilter"; import { - getReducedActionDigest_SHA256 -} from "@/utils/digestFunctionUtils"; + type BrowserPageParams, + BrowserPageType, +} from "@/types/BrowserPageType"; +import { PATH_HASH_BASE_HASH } from "@/utils/bloomFilter"; +import { getReducedActionDigest_SHA256 } from "@/utils/digestFunctionUtils"; import { readableFileSizeFromString } from "@/utils/filesize"; import { readableDurationFromProtobufDuration } from "@/utils/time"; import { generateBrowserSplat } from "@/utils/urlGenerator"; @@ -32,7 +31,6 @@ import PropertyTagList from "../PropertyTagList"; import type { PropertyTagListEntry } from "../PropertyTagList/types"; import CopyBbClientdActionButton from "./CopyBbClientdActionButton"; import { fetchBrowserActionGrid } from "./fetch"; -import type { Digest } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; interface Params { browserPageParams: BrowserPageParams; @@ -136,7 +134,7 @@ const BrowserActionGrid: React.FC = ({ browserPageParams.digestFunction, data.actionDigest, BrowserPageType.Action, - ) + ), }} style={{ textDecoration: "underline" }} > @@ -177,23 +175,20 @@ const BrowserActionGrid: React.FC = ({ ) : ( This action could not be found. - ) - } - - { - data.casCommand ? ( - - ) : ( - - The command of this action could not be found. - - ) - } + )} + + {data.casCommand ? ( + + ) : ( + + The command of this action could not be found. + + )} Result @@ -211,38 +206,36 @@ const BrowserActionGrid: React.FC = ({ )} - { - data.action.inputRootDigest && ( - - - - Input files - - - - - ) - } + {data.action.inputRootDigest && ( + + + + Input files + + + + + )} Output files @@ -257,8 +250,7 @@ const BrowserActionGrid: React.FC = ({ /> - { - data.executeResponse?.serverLogs && + {data.executeResponse?.serverLogs && Object.keys(data.executeResponse.serverLogs).length !== 0 && ( Server logs @@ -271,246 +263,231 @@ const BrowserActionGrid: React.FC = ({ isPending={isPending} /> - ) - } + )} - { - data.executeResponse?.result?.executionMetadata && ( - - Execution metadata + {data.executeResponse?.result?.executionMetadata && ( + + Execution metadata - - - - - - - - {data.executeResponse.result.executionMetadata - .virtualExecutionDuration && ( - - {readableDurationFromProtobufDuration( - data.executeResponse.result.executionMetadata - .virtualExecutionDuration, - )} - + + + + + + + + {data.executeResponse.result.executionMetadata + .virtualExecutionDuration && ( + + {readableDurationFromProtobufDuration( + data.executeResponse.result.executionMetadata + .virtualExecutionDuration, )} - - - ) - } + + )} + + + )} - { - data.authenticationMetadata && ( - - Authentication metadata + {data.authenticationMetadata && ( + + Authentication metadata - - -
    -                  {JSON.stringify(data.authenticationMetadata.public, null, 2)}
    -                
    -
    -
    -
    - ) - } + + +
    +                {JSON.stringify(data.authenticationMetadata.public, null, 2)}
    +              
    +
    +
    +
    + )} - { - data.requestMetadata && ( - - Request metadata + {data.requestMetadata && ( + + Request metadata - - {data.requestMetadata.toolDetails && ( - - {`${data.requestMetadata.toolDetails.toolName} ${data.requestMetadata.toolDetails.toolVersion}`} - - )} - - - - - {data.requestMetadata.correlatedInvocationsId} - - - {data.requestMetadata.targetId} - - - {data.requestMetadata.actionMnemonic} - - - {data.requestMetadata.actionId} - - - {data.requestMetadata.configurationId} + + {data.requestMetadata.toolDetails && ( + + {`${data.requestMetadata.toolDetails.toolName} ${data.requestMetadata.toolDetails.toolVersion}`} - - - ) - } + )} + + + + + {data.requestMetadata.correlatedInvocationsId} + + + {data.requestMetadata.targetId} + + + {data.requestMetadata.actionMnemonic} + + + {data.requestMetadata.actionId} + + + {data.requestMetadata.configurationId} + + + + )} - { - data.posixResourceUsage && ( - - POSIX resource usage + {data.posixResourceUsage && ( + + POSIX resource usage - - - {data.posixResourceUsage.userTime && - `${readableDurationFromProtobufDuration(data.posixResourceUsage.userTime)} user`} - {data.posixResourceUsage.userTime && - data.posixResourceUsage.systemTime && - ","}{" "} - {data.posixResourceUsage.systemTime && - `${readableDurationFromProtobufDuration(data.posixResourceUsage.systemTime)} system`} - - - {readableFileSizeFromString( - data.posixResourceUsage.maximumResidentSetSize, - )} - - - {`${data.posixResourceUsage.pageReclaims} reclaims, ${data.posixResourceUsage.pageFaults} faults, ${data.posixResourceUsage.swaps} swaps`} - - - {`${data.posixResourceUsage.blockInputOperations} inputs, ${data.posixResourceUsage.blockOutputOperations} outputs`} - - - {`${data.posixResourceUsage.messagesSent} sent, ${data.posixResourceUsage.messagesReceived} received`} - - - {`${data.posixResourceUsage.signalsReceived} received`} - - - {`${data.posixResourceUsage.voluntaryContextSwitches} voluntary, ${data.posixResourceUsage.involuntaryContextSwitches} involuntary`} - - - - ) - } + + + {data.posixResourceUsage.userTime && + `${readableDurationFromProtobufDuration(data.posixResourceUsage.userTime)} user`} + {data.posixResourceUsage.userTime && + data.posixResourceUsage.systemTime && + ","}{" "} + {data.posixResourceUsage.systemTime && + `${readableDurationFromProtobufDuration(data.posixResourceUsage.systemTime)} system`} + + + {readableFileSizeFromString( + data.posixResourceUsage.maximumResidentSetSize, + )} + + + {`${data.posixResourceUsage.pageReclaims} reclaims, ${data.posixResourceUsage.pageFaults} faults, ${data.posixResourceUsage.swaps} swaps`} + + + {`${data.posixResourceUsage.blockInputOperations} inputs, ${data.posixResourceUsage.blockOutputOperations} outputs`} + + + {`${data.posixResourceUsage.messagesSent} sent, ${data.posixResourceUsage.messagesReceived} received`} + + + {`${data.posixResourceUsage.signalsReceived} received`} + + + {`${data.posixResourceUsage.voluntaryContextSwitches} voluntary, ${data.posixResourceUsage.involuntaryContextSwitches} involuntary`} + + + + )} - { - data.filePoolResourceUsage && ( - - - File pool resource usage - + {data.filePoolResourceUsage && ( + + + File pool resource usage + - - - {data.filePoolResourceUsage.filesCreated} - - - {`${data.filePoolResourceUsage.filesCountPeak - } files, having a total size of ${readableFileSizeFromString( - data.filePoolResourceUsage.filesSizeBytesPeak, - )}`} - - - {`${data.filePoolResourceUsage.readsCount - } operations, having a total size of ${readableFileSizeFromString( - data.filePoolResourceUsage.readsSizeBytes, - )}`} - - - {`${data.filePoolResourceUsage.writesCount - } operations, having a total size of ${readableFileSizeFromString( - data.filePoolResourceUsage.writesSizeBytes, - )}`} - - - {`${data.filePoolResourceUsage.truncatesCount} operations`} - - - - ) - } + + + {data.filePoolResourceUsage.filesCreated} + + + {`${ + data.filePoolResourceUsage.filesCountPeak + } files, having a total size of ${readableFileSizeFromString( + data.filePoolResourceUsage.filesSizeBytesPeak, + )}`} + + + {`${ + data.filePoolResourceUsage.readsCount + } operations, having a total size of ${readableFileSizeFromString( + data.filePoolResourceUsage.readsSizeBytes, + )}`} + + + {`${ + data.filePoolResourceUsage.writesCount + } operations, having a total size of ${readableFileSizeFromString( + data.filePoolResourceUsage.writesSizeBytes, + )}`} + + + {`${data.filePoolResourceUsage.truncatesCount} operations`} + + + + )} - { - data.inputRootResourceUsage && ( - - - Input root resource usage - + {data.inputRootResourceUsage && ( + + + Input root resource usage + - - - {`${data.inputRootResourceUsage.directoriesResolved} resolved, ${data.inputRootResourceUsage.directoriesRead} read`} - - - {`${data.inputRootResourceUsage.filesRead} read`} - - - - ) - } + + + {`${data.inputRootResourceUsage.directoriesResolved} resolved, ${data.inputRootResourceUsage.directoriesRead} read`} + + + {`${data.inputRootResourceUsage.filesRead} read`} + + + + )} - { - data.monetaryResourceUsage && ( - - Monetary resource usage + {data.monetaryResourceUsage && ( + + Monetary resource usage - - {Object.entries(data.monetaryResourceUsage.expenses).map( - ([key, value]) => ( - - {`${value.currency} ${value.cost}`} - - ), - )} - - - ) - } - { - data.previousExecutionStats && - reducedActionDigest && ( - - ) - } - + + {Object.entries(data.monetaryResourceUsage.expenses).map( + ([key, value]) => ( + + {`${value.currency} ${value.cost}`} + + ), + )} + + + )} + {data.previousExecutionStats && reducedActionDigest && ( + + )} + ); }; diff --git a/frontend/src/components/BrowserCommandDescription/CopyBbClientdCommandButton.tsx b/frontend/src/components/BrowserCommandDescription/CopyBbClientdCommandButton.tsx index 82ec93d8..1ba4b227 100644 --- a/frontend/src/components/BrowserCommandDescription/CopyBbClientdCommandButton.tsx +++ b/frontend/src/components/BrowserCommandDescription/CopyBbClientdCommandButton.tsx @@ -1,8 +1,8 @@ +import { Button, message } from "antd"; +import type React from "react"; import type { Digest } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; import type { BrowserPageParams } from "@/types/BrowserPageType"; import { getBBClientdPath } from "@/utils/getBbClientdPath"; -import { Button, message } from "antd"; -import type React from "react"; interface Params { browserPageParams: BrowserPageParams; diff --git a/frontend/src/components/BrowserCommandDescription/DownloadAsShellScriptButton.tsx b/frontend/src/components/BrowserCommandDescription/DownloadAsShellScriptButton.tsx index 9bdf7aa9..0d010dc4 100644 --- a/frontend/src/components/BrowserCommandDescription/DownloadAsShellScriptButton.tsx +++ b/frontend/src/components/BrowserCommandDescription/DownloadAsShellScriptButton.tsx @@ -1,8 +1,8 @@ -import type { Digest } from '@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution'; -import type { BrowserPageParams } from '@/types/BrowserPageType'; -import { generateCommandShellScriptUrl } from '@/utils/urlGenerator'; -import { Button } from 'antd'; -import type React from 'react'; +import { Button } from "antd"; +import type React from "react"; +import type { Digest } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; +import type { BrowserPageParams } from "@/types/BrowserPageType"; +import { generateCommandShellScriptUrl } from "@/utils/urlGenerator"; interface Params { browserPageParams: BrowserPageParams; diff --git a/frontend/src/components/BrowserCommandDescription/index.tsx b/frontend/src/components/BrowserCommandDescription/index.tsx index 4919656d..468c1907 100644 --- a/frontend/src/components/BrowserCommandDescription/index.tsx +++ b/frontend/src/components/BrowserCommandDescription/index.tsx @@ -1,14 +1,17 @@ +import { Link } from "@tanstack/react-router"; +import { Descriptions, Flex, Space, Typography } from "antd"; +import type React from "react"; import type { Command, Digest, } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; -import { BrowserPageType, type BrowserPageParams } from "@/types/BrowserPageType"; -import { Descriptions, Flex, Space, Typography } from "antd"; -import { Link } from '@tanstack/react-router'; -import type React from "react"; +import { + type BrowserPageParams, + BrowserPageType, +} from "@/types/BrowserPageType"; +import { generateBrowserSplat } from "@/utils/urlGenerator"; import CopyBbClientdCommandButton from "./CopyBbClientdCommandButton"; import DownloadAsShellScriptButton from "./DownloadAsShellScriptButton"; -import { generateBrowserSplat } from "@/utils/urlGenerator"; interface Params { browserPageParams: BrowserPageParams; @@ -30,12 +33,14 @@ const BrowserCommandDescription: React.FC = ({ {commandDigest ? ( Command diff --git a/frontend/src/components/BrowserCommandGrid/index.tsx b/frontend/src/components/BrowserCommandGrid/index.tsx index 6561f62b..741fa9ae 100644 --- a/frontend/src/components/BrowserCommandGrid/index.tsx +++ b/frontend/src/components/BrowserCommandGrid/index.tsx @@ -1,4 +1,3 @@ - import { useQuery } from "@tanstack/react-query"; import { Space, Spin, Typography } from "antd"; import type React from "react"; diff --git a/frontend/src/components/BrowserDirectory/CopyBbClientdDirectoryButton.tsx b/frontend/src/components/BrowserDirectory/CopyBbClientdDirectoryButton.tsx index dcece520..286eccc8 100644 --- a/frontend/src/components/BrowserDirectory/CopyBbClientdDirectoryButton.tsx +++ b/frontend/src/components/BrowserDirectory/CopyBbClientdDirectoryButton.tsx @@ -1,10 +1,10 @@ +import { Button, message } from "antd"; +import type React from "react"; import type { Digest, DigestFunction_Value, } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; import { getBBClientdPath } from "@/utils/getBbClientdPath"; -import { Button, message } from "antd"; -import type React from "react"; interface Params { instanceName: string; diff --git a/frontend/src/components/BrowserDirectory/DownloadAsTarballButton.tsx b/frontend/src/components/BrowserDirectory/DownloadAsTarballButton.tsx index 9417882a..ccc0cb04 100644 --- a/frontend/src/components/BrowserDirectory/DownloadAsTarballButton.tsx +++ b/frontend/src/components/BrowserDirectory/DownloadAsTarballButton.tsx @@ -1,10 +1,10 @@ +import { Button } from "antd"; +import type React from "react"; import type { Digest, DigestFunction_Value, -} from '@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution'; -import { generateDirectoryTarballUrl } from '@/utils/urlGenerator'; -import { Button } from 'antd'; -import type React from 'react'; +} from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; +import { generateDirectoryTarballUrl } from "@/utils/urlGenerator"; interface Params { instanceName: string; diff --git a/frontend/src/components/BrowserDirectory/index.tsx b/frontend/src/components/BrowserDirectory/index.tsx index 0f353fef..c0011596 100644 --- a/frontend/src/components/BrowserDirectory/index.tsx +++ b/frontend/src/components/BrowserDirectory/index.tsx @@ -1,4 +1,8 @@ - +import { DownOutlined, RightOutlined } from "@ant-design/icons"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { Link } from "@tanstack/react-router"; +import { Button, Flex, Space, Spin, Typography } from "antd"; +import React, { useEffect } from "react"; import { useGrpcClients } from "@/context/GrpcClientsContext"; import { type Digest, @@ -9,24 +13,19 @@ import type { FileSystemAccessProfile } from "@/lib/grpc-client/buildbarn/fsac/f import type { FileSystemAccessProfileReference } from "@/lib/grpc-client/buildbarn/query/query"; import type { ByteStreamClient } from "@/lib/grpc-client/google/bytestream/bytestream"; import themeStyles from "@/theme/theme.module.css"; +import { BrowserPageType } from "@/types/BrowserPageType"; import { type BloomFilterReader, - PathHashes, containsPathHashes, + PathHashes, readBloomFilter, } from "@/utils/bloomFilter"; import { fetchCasObjectAndParse } from "@/utils/fetchCasObject"; import { readableFileSizeFromString } from "@/utils/filesize"; import { generateBrowserSplat, generateFileUrl } from "@/utils/urlGenerator"; -import { DownOutlined, RightOutlined } from "@ant-design/icons"; -import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { Button, Flex, Space, Spin, Typography } from "antd"; -import { Link } from '@tanstack/react-router'; -import React, { useEffect } from "react"; import PortalAlert from "../PortalAlert"; import CopyBbClientdDirectoryButton from "./CopyBbClientdDirectoryButton"; import DownloadAsTarballButton from "./DownloadAsTarballButton"; -import { BrowserPageType } from "@/types/BrowserPageType"; const FETCH_STALE_TIME = 30000; @@ -36,8 +35,8 @@ interface Params { inputRootDigest: Digest; fileSystemAccessProfile: FileSystemAccessProfile | undefined; fileSystemAccessProfileReference: - | FileSystemAccessProfileReference - | undefined; + | FileSystemAccessProfileReference + | undefined; } const BrowserDirectory: React.FC = ({ @@ -63,9 +62,10 @@ const BrowserDirectory: React.FC = ({ pathHashes={ bloomFilterReader ? new PathHashes( - fileSystemAccessProfileReference?.pathHashesBaseHash ? - BigInt(fileSystemAccessProfileReference?.pathHashesBaseHash) : undefined, - ) + fileSystemAccessProfileReference?.pathHashesBaseHash + ? BigInt(fileSystemAccessProfileReference?.pathHashesBaseHash) + : undefined, + ) : undefined } fileSystemAccessProfileRef={fileSystemAccessProfileReference} @@ -126,7 +126,9 @@ interface RecursiveDirectoryNodeProps { bloomFilterReader?: BloomFilterReader; pathHashes?: PathHashes; willBePrefetched?: boolean; - fileSystemAccessProfileRef: Partial | undefined; + fileSystemAccessProfileRef: + | Partial + | undefined; } const RecursiveDirectoryNode: React.FC = ({ @@ -230,13 +232,24 @@ const RecursiveDirectoryNode: React.FC = ({ linkWrapper={(children) => ( {children} @@ -275,22 +288,31 @@ const RecursiveDirectoryNode: React.FC = ({ ( - {children} - ) : undefined} + linkWrapper={ + digest + ? (children) => ( + + {children} + + ) + : undefined + } sizeBytes={file.digest?.sizeBytes} - permissions={`-r-${file.isExecutable ? "x" : "-"}r-${file.isExecutable ? "x" : "-" - }r-${file.isExecutable ? "x" : "-"}`} + permissions={`-r-${file.isExecutable ? "x" : "-"}r-${ + file.isExecutable ? "x" : "-" + }r-${file.isExecutable ? "x" : "-"}`} willBePrefetched={calcWillBePrefetched( pathHashes?.appendComponent(file.name), )} /> - ) + ); })} {data.symlinks.map((symlink) => ( = ({ browserPageParams, fileSystemAccessProfileReference }) => { +const BrowserDirectoryPage: React.FC = ({ + browserPageParams, + fileSystemAccessProfileReference, +}) => { const { fileSystemAccessCacheClient } = useGrpcClients(); const { data, isError, error, isLoading } = useQuery({ diff --git a/frontend/src/components/BrowserPreviousExecutionsDisplay/index.tsx b/frontend/src/components/BrowserPreviousExecutionsDisplay/index.tsx index 6a2ecf60..b663bf80 100644 --- a/frontend/src/components/BrowserPreviousExecutionsDisplay/index.tsx +++ b/frontend/src/components/BrowserPreviousExecutionsDisplay/index.tsx @@ -1,11 +1,14 @@ +import { Link } from "@tanstack/react-router"; +import { Descriptions, Space, Typography } from "antd"; import type { Digest } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; import type { PreviousExecutionStats } from "@/lib/grpc-client/buildbarn/iscc/iscc"; -import { BrowserPageType, type BrowserPageParams } from "@/types/BrowserPageType"; -import { Descriptions, Space, Typography } from "antd"; -import { Link } from '@tanstack/react-router'; +import { + type BrowserPageParams, + BrowserPageType, +} from "@/types/BrowserPageType"; +import { generateBrowserSplat } from "@/utils/urlGenerator"; import PreviousExecutionsPlot from "../PreviousExecuteStatsPlot"; import SizeClassOutcome from "../SizeClassOutcome"; -import { generateBrowserSplat } from "@/utils/urlGenerator"; interface Props { browserParams: BrowserPageParams; @@ -25,12 +28,14 @@ const BrowserPreviousExecutionsDisplay: React.FC = ({ Previous execution stats diff --git a/frontend/src/components/BrowserResultDescription/index.tsx b/frontend/src/components/BrowserResultDescription/index.tsx index f7a73b1f..31c53a7f 100644 --- a/frontend/src/components/BrowserResultDescription/index.tsx +++ b/frontend/src/components/BrowserResultDescription/index.tsx @@ -1,11 +1,11 @@ +import { Descriptions, Space, Tag, Typography } from "antd"; +import Paragraph from "antd/es/typography/Paragraph"; +import type React from "react"; import type { ExecuteResponse } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; import type { POSIXResourceUsage } from "@/lib/grpc-client/buildbarn/resourceusage/resourceusage"; import type { BrowserPageParams } from "@/types/BrowserPageType"; import { readableFileSizeFromString } from "@/utils/filesize"; import { generateFileUrl } from "@/utils/urlGenerator"; -import { Descriptions, Space, Tag, Typography } from "antd"; -import Paragraph from "antd/es/typography/Paragraph"; -import type React from "react"; import type { ActionConsoleOutput } from "../BrowserActionGrid/types"; interface Params { @@ -78,8 +78,8 @@ const BrowserResultDescription: React.FC = ({ if (consoleOutput.digest && logLinkHref) { return ( - The log file for this action is to - large to display ( + The log file for this action is to large + to display ( {readableFileSizeFromString(consoleOutput.digest.sizeBytes)}). ); diff --git a/frontend/src/components/BuildLogsDisplay/index.tsx b/frontend/src/components/BuildLogsDisplay/index.tsx index 6b6bc831..1ba81d5e 100644 --- a/frontend/src/components/BuildLogsDisplay/index.tsx +++ b/frontend/src/components/BuildLogsDisplay/index.tsx @@ -10,7 +10,7 @@ import LogViewer from "../LogViewer"; interface Props { invocationId: string; - rawCommand: string|null; + rawCommand: string | null; } const fetchLog = async (id: string, start = 0, end = -1): Promise => { @@ -56,7 +56,6 @@ const BuildLogsDisplay: React.FC = ({ invocationId, rawCommand }) => { ), ]} > - ); diff --git a/frontend/src/components/BuildsTable/Columns.tsx b/frontend/src/components/BuildsTable/Columns.tsx index c86b9d88..42844ecc 100644 --- a/frontend/src/components/BuildsTable/Columns.tsx +++ b/frontend/src/components/BuildsTable/Columns.tsx @@ -1,7 +1,7 @@ import { SearchOutlined } from "@ant-design/icons"; +import { Link } from "@tanstack/react-router"; import { type TableColumnsType, Typography } from "antd"; import dayjs from "dayjs"; -import { Link } from '@tanstack/react-router'; import { validate as uuidValidate } from "uuid"; import styles from "@/components/BazelInvocationColumns/Columns.module.css"; import { @@ -17,7 +17,9 @@ export const columns: TableColumnsType = [ width: 220, title: "Build ID", render: (_, record) => ( - {record.buildUUID} + + {record.buildUUID} + ), filterDropdown: (filterProps) => ( = [ key: "buildURL", width: 220, title: "Build URL", - render: (_, record) => ( - {record.buildURL} - ), + render: (_, record) => {record.buildURL}, filterDropdown: (filterProps) => ( = ({ text, link }) => { return ( - - {text.length > 11 - ? `${text.slice(0, 8)}...` - : text} - + {text.length > 11 ? `${text.slice(0, 8)}...` : text} ); }; diff --git a/frontend/src/components/CommandLine/index.tsx b/frontend/src/components/CommandLine/index.tsx index ec3e9ce0..28887b28 100644 --- a/frontend/src/components/CommandLine/index.tsx +++ b/frontend/src/components/CommandLine/index.tsx @@ -1,62 +1,76 @@ -import { CodeOutlined, InfoCircleOutlined } from '@ant-design/icons'; -import { Empty, List, Tooltip } from 'antd'; -import type React from 'react'; -import PortalCard from '../PortalCard'; +import { CodeOutlined, InfoCircleOutlined } from "@ant-design/icons"; +import { Empty, List, Tooltip } from "antd"; +import type React from "react"; +import PortalCard from "../PortalCard"; // TODO: find a way to apply these interfaces automatically to the // output of graphql while remaining a scalar with regard to the graphql // api. export interface CommandLineData { - executable: string - command: string - options: CommandLineOptions[] - startupOptions: CommandLineOptions[] - residual: string[] + executable: string; + command: string; + options: CommandLineOptions[]; + startupOptions: CommandLineOptions[]; + residual: string[]; } export interface CommandLineOptions { - option: string - value: string + option: string; + value: string; } interface ParsedOptions { - explicitOptions: string[] - options: string[] + explicitOptions: string[]; + options: string[]; } interface Props { - rawCommand: string | null - canonicalCommandLine: CommandLineData | null - parsedOptions: ParsedOptions + rawCommand: string | null; + canonicalCommandLine: CommandLineData | null; + parsedOptions: ParsedOptions; } -const CommandLineDisplay: React.FC = ({rawCommand, canonicalCommandLine, parsedOptions}) => { +const CommandLineDisplay: React.FC = ({ + rawCommand, + canonicalCommandLine, + parsedOptions, +}) => { if (!canonicalCommandLine) { - return } titleBits={["Command Line", rawCommand]}> - - ; + return ( + } + titleBits={["Command Line", rawCommand]} + > + + + ); } - - const opts = canonicalCommandLine.options.filter(x => x.option !== 'config') + + const opts = canonicalCommandLine.options.filter( + (x) => x.option !== "config", + ); return ( - } titleBits={["Command Line", rawCommand]}> - { !parsedOptions ? null : - - - Parsed Options - - - } - dataSource={parsedOptions.options} - renderItem={x => {x}} + } + titleBits={["Command Line", rawCommand]} + > + {!parsedOptions ? null : ( + + + Parsed Options + + + } + dataSource={parsedOptions.options} + renderItem={(x) => {x}} /> - } + )} = ({rawCommand, canonicalCommandLine, } dataSource={opts} - renderItem={(item) => --{item.option}={item.value}} + renderItem={(item) => ( + + --{item.option}={item.value} + + )} /> = ({rawCommand, canonicalCommandLine, } dataSource={canonicalCommandLine.startupOptions} - renderItem={(item) => --{item.option}={item.value}} + renderItem={(item) => ( + + --{item.option}={item.value} + + )} /> ); -} +}; export default CommandLineDisplay; diff --git a/frontend/src/components/ConditionalToolInvocationLink/index.tsx b/frontend/src/components/ConditionalToolInvocationLink/index.tsx index 9d7b36e0..c2bf3c97 100644 --- a/frontend/src/components/ConditionalToolInvocationLink/index.tsx +++ b/frontend/src/components/ConditionalToolInvocationLink/index.tsx @@ -1,5 +1,5 @@ import { useQuery } from "@apollo/client/react"; -import { Link } from '@tanstack/react-router'; +import { Link } from "@tanstack/react-router"; import { CHECK_IF_INVOCATION_EXISTS } from "./graphql"; interface Props { diff --git a/frontend/src/components/Content/index.tsx b/frontend/src/components/Content/index.tsx index d6e626b2..2e8cb486 100644 --- a/frontend/src/components/Content/index.tsx +++ b/frontend/src/components/Content/index.tsx @@ -1,13 +1,12 @@ - -import type React from 'react'; -import type { Key } from 'react'; -import { FloatButton, Layout, type MenuProps } from 'antd'; -import type { ItemType } from 'antd/es/menu/interface'; -import styles from '@/components/Content/index.module.css'; -import SiderBar from '@/components/SiderBar'; -import { Breadcrumbs } from '@/components/Breadcrumbs'; -import FooterBar from '@/components/FooterBar'; -import useScreenSize from '@/utils/screen'; +import { FloatButton, Layout, type MenuProps } from "antd"; +import type { ItemType } from "antd/es/menu/interface"; +import type React from "react"; +import type { Key } from "react"; +import { Breadcrumbs } from "@/components/Breadcrumbs"; +import styles from "@/components/Content/index.module.css"; +import FooterBar from "@/components/FooterBar"; +import SiderBar from "@/components/SiderBar"; +import useScreenSize from "@/utils/screen"; export const SIDER_BAR_MINIMUM_SCREEN_WIDTH = 932; @@ -17,7 +16,7 @@ interface Props { sidebarMenuDefaultSelectedKeys?: Key[]; sidebarMenuDefaultOpenKeys?: Key[]; sidebarMenuOpenKeys?: Key[]; - sidebarMenuOnOpenChange?: MenuProps['onOpenChange']; + sidebarMenuOnOpenChange?: MenuProps["onOpenChange"]; sidebarMenuExpandedWidth?: number; content: React.ReactNode; } @@ -33,7 +32,9 @@ const Content: React.FC = ({ content, }) => { const screenSize = useScreenSize(); - const showSiderBar = sidebarMenuItems?.length && screenSize.width > SIDER_BAR_MINIMUM_SCREEN_WIDTH; + const showSiderBar = + sidebarMenuItems?.length && + screenSize.width > SIDER_BAR_MINIMUM_SCREEN_WIDTH; return ( {showSiderBar ? ( diff --git a/frontend/src/components/DownloadButton/index.tsx b/frontend/src/components/DownloadButton/index.tsx index f2cb1cba..c8957613 100644 --- a/frontend/src/components/DownloadButton/index.tsx +++ b/frontend/src/components/DownloadButton/index.tsx @@ -1,8 +1,15 @@ -import type React from 'react'; -import { useState } from 'react'; -import { Button, Dropdown, type DropdownProps, type MenuProps, Popover, Space } from 'antd'; -import { DownloadOutlined } from '@ant-design/icons'; -import type { MenuItemType } from 'antd/es/menu/interface'; +import { DownloadOutlined } from "@ant-design/icons"; +import { + Button, + Dropdown, + type DropdownProps, + type MenuProps, + Popover, + Space, +} from "antd"; +import type { MenuItemType } from "antd/es/menu/interface"; +import type React from "react"; +import { useState } from "react"; export interface DownloadOpts { url?: string; @@ -30,7 +37,7 @@ const DownloadButton: React.FC = ({ }) => { const [open, setOpen] = useState(false); - const handleMenuClick: MenuProps['onClick'] = e => { + const handleMenuClick: MenuProps["onClick"] = (e) => { // Closing is delegated to item. if (preventDefaultForKeys?.includes(e.key)) { e.domEvent.preventDefault(); @@ -41,7 +48,9 @@ const DownloadButton: React.FC = ({ setOpen(false); }, 750); }; - const handleOpenChange: DropdownProps['onOpenChange'] = (nextOpen: boolean) => { + const handleOpenChange: DropdownProps["onOpenChange"] = ( + nextOpen: boolean, + ) => { setOpen(nextOpen); }; if (items) { @@ -57,7 +66,7 @@ const DownloadButton: React.FC = ({ if (!items || items.length === 0) { return ( @@ -74,7 +83,7 @@ const DownloadButton: React.FC = ({ > - + {buttonLabel} diff --git a/frontend/src/components/ExecutionMetadataTimeline/index.tsx b/frontend/src/components/ExecutionMetadataTimeline/index.tsx index a03fb501..bf4c0957 100644 --- a/frontend/src/components/ExecutionMetadataTimeline/index.tsx +++ b/frontend/src/components/ExecutionMetadataTimeline/index.tsx @@ -1,9 +1,8 @@ - -import type { ExecutedActionMetadata } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; -import { readableDurationFromDates } from "@/utils/time"; import { ClockCircleOutlined } from "@ant-design/icons"; import { Flex, Space, Typography } from "antd"; import type React from "react"; +import type { ExecutedActionMetadata } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; +import { readableDurationFromDates } from "@/utils/time"; interface Params { executionMetadata: ExecutedActionMetadata; diff --git a/frontend/src/components/ExecutionResponseDisplay/index.tsx b/frontend/src/components/ExecutionResponseDisplay/index.tsx index 6b48c615..e40f8253 100644 --- a/frontend/src/components/ExecutionResponseDisplay/index.tsx +++ b/frontend/src/components/ExecutionResponseDisplay/index.tsx @@ -1,7 +1,7 @@ -import { ExecuteResponse } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; -import { protobufToObjectWithTypeField } from "@/utils/protobufToObject"; import { CodeFilled } from "@ant-design/icons"; import { Space } from "antd"; +import { ExecuteResponse } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; +import { protobufToObjectWithTypeField } from "@/utils/protobufToObject"; import PortalCard from "../PortalCard"; interface Props { diff --git a/frontend/src/components/FilesTable/index.tsx b/frontend/src/components/FilesTable/index.tsx index 1ecd2d9d..7f947bc5 100644 --- a/frontend/src/components/FilesTable/index.tsx +++ b/frontend/src/components/FilesTable/index.tsx @@ -1,7 +1,7 @@ -import themeStyles from "@/theme/theme.module.css"; import { BuildOutlined } from "@ant-design/icons"; import { Space, Table, Typography } from "antd"; import type React from "react"; +import themeStyles from "@/theme/theme.module.css"; import getColumns, { type FilesTableEntry } from "./Columns"; type Props = { diff --git a/frontend/src/components/FilesTable/utils.ts b/frontend/src/components/FilesTable/utils.ts index efc4d3cd..cc002f97 100644 --- a/frontend/src/components/FilesTable/utils.ts +++ b/frontend/src/components/FilesTable/utils.ts @@ -7,7 +7,11 @@ import type { OutputFile, OutputSymlink, } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; -import { generateDirectoryUrl, generateFileUrl, generateTreeUrl } from "@/utils/urlGenerator"; +import { + generateDirectoryUrl, + generateFileUrl, + generateTreeUrl, +} from "@/utils/urlGenerator"; import type { FilesTableEntry } from "./Columns"; export function filesTableEntryFromOutputDirectory( @@ -19,18 +23,23 @@ export function filesTableEntryFromOutputDirectory( if (outputDirectory.rootDirectoryDigest) { return [ outputDirectory.rootDirectoryDigest, - generateDirectoryUrl(instanceName, digestFunction, outputDirectory.rootDirectoryDigest), - ] + generateDirectoryUrl( + instanceName, + digestFunction, + outputDirectory.rootDirectoryDigest, + ), + ]; } else if (outputDirectory.treeDigest) { return [ outputDirectory.treeDigest, - generateTreeUrl(instanceName, digestFunction, outputDirectory.treeDigest), - ] + generateTreeUrl( + instanceName, + digestFunction, + outputDirectory.treeDigest, + ), + ]; } else { - return [ - undefined, - undefined, - ] + return [undefined, undefined]; } })(); return { diff --git a/frontend/src/components/FooterBar/index.tsx b/frontend/src/components/FooterBar/index.tsx index 5ae7abe5..25b4fcc9 100644 --- a/frontend/src/components/FooterBar/index.tsx +++ b/frontend/src/components/FooterBar/index.tsx @@ -1,19 +1,24 @@ - -import styles from "@/components/FooterBar/index.module.css"; import { DiscordOutlined, GithubOutlined, - SlackOutlined + SlackOutlined, } from "@ant-design/icons"; import { Layout, Space } from "antd"; -import { env } from "@/utils/env"; import type React from "react"; +import styles from "@/components/FooterBar/index.module.css"; import type { PortalFrontendConfiguration_FooterElement } from "@/lib/grpc-client/portal/frontend/frontend"; +import { env } from "@/utils/env"; -const FooterLink: React.FC = ({ text, href, icon }) => { - let iconElement: React.ReactElement | undefined ; +const FooterLink: React.FC = ({ + text, + href, + icon, +}) => { + let iconElement: React.ReactElement | undefined; if (icon?.url) { - iconElement = Footer icon + iconElement = ( + Footer icon + ); } else if (icon?.slack) { iconElement = ; } else if (icon?.github) { @@ -49,10 +54,15 @@ const FooterBar: React.FC = ({ className }) => { return ( - {env.footerContent.map((element: PortalFrontendConfiguration_FooterElement, index: number) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: This won't change - - ))} + {env.footerContent.map( + ( + element: PortalFrontendConfiguration_FooterElement, + index: number, + ) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: This won't change + + ), + )} ); diff --git a/frontend/src/components/InvocationOverviewDisplay/index.tsx b/frontend/src/components/InvocationOverviewDisplay/index.tsx index 98591714..3d105d22 100644 --- a/frontend/src/components/InvocationOverviewDisplay/index.tsx +++ b/frontend/src/components/InvocationOverviewDisplay/index.tsx @@ -1,125 +1,127 @@ -import type React from 'react'; -import { Descriptions, Space } from 'antd'; -import PortalDuration from '../PortalDuration'; -import { InvocationResultTag } from '../InvocationResultTag'; -import type { Configuration as BazelConfiguration } from '@/graphql/__generated__/graphql'; +import { Descriptions, Space } from "antd"; +import type React from "react"; +import type { Configuration as BazelConfiguration } from "@/graphql/__generated__/graphql"; +import { InvocationResultTag } from "../InvocationResultTag"; +import PortalDuration from "../PortalDuration"; -type Configuration = Pick; +type Configuration = Pick; interface Props { - command: string, - exitCodeName: string | undefined, - connectionLastOpenAt: string | undefined - timeSinceLastConnectionMillis: number | undefined, - invocationId: string, - instanceName: string | undefined, - configurations: Configuration[] | undefined, - startedAt: string - endedAt: string - hostname: string - isCiWorker: boolean, - stepLabel: string, - numFetches: number, - bazelVersion: string, + command: string; + exitCodeName: string | undefined; + connectionLastOpenAt: string | undefined; + timeSinceLastConnectionMillis: number | undefined; + invocationId: string; + instanceName: string | undefined; + configurations: Configuration[] | undefined; + startedAt: string; + endedAt: string; + hostname: string; + isCiWorker: boolean; + stepLabel: string; + numFetches: number; + bazelVersion: string; } export const InvocationOverviewDisplay: React.FC = ({ - command, - exitCodeName, - connectionLastOpenAt, - timeSinceLastConnectionMillis, - invocationId, - instanceName, - configurations, - startedAt, - endedAt, - hostname, - isCiWorker, - stepLabel, - numFetches, - bazelVersion, + command, + exitCodeName, + connectionLastOpenAt, + timeSinceLastConnectionMillis, + invocationId, + instanceName, + configurations, + startedAt, + endedAt, + hostname, + isCiWorker, + stepLabel, + numFetches, + bazelVersion, }) => { - // TODO: Determine how to best display multiple configurations - const cpu = Array.from( - new Set(configurations - ?.map((config) => config.cpu) - ?.filter((cpu) => cpu && cpu !== "") - ) - ) - .sort() - .join(", "); - const mnemonics = Array.from( - new Set(configurations - ?.map((config) => config.mnemonic) - ?.filter((mnemonic) => mnemonic && mnemonic !== "") - ) - ) - .sort() - .join(", "); + // TODO: Determine how to best display multiple configurations + const cpu = Array.from( + new Set( + configurations + ?.map((config) => config.cpu) + ?.filter((cpu) => cpu && cpu !== ""), + ), + ) + .sort() + .join(", "); + const mnemonics = Array.from( + new Set( + configurations + ?.map((config) => config.mnemonic) + ?.filter((mnemonic) => mnemonic && mnemonic !== ""), + ), + ) + .sort() + .join(", "); - return ( - - - - - - - {invocationId} - - {instanceName !== undefined && instanceName !== "" && - - {instanceName} - - } - - - - {command !== "" && - - - {command} - - - } - {cpu !== "" && - - {cpu} - - } - {mnemonics !== "" && - - {mnemonics} - - } - {hostname !== "" && - - {hostname} - - } - {numFetches !== 0 && - - {numFetches} - - } - {isCiWorker && - True - } - {stepLabel !== "" && - {stepLabel} - } - {bazelVersion !== "" && - {bazelVersion} - } - - - ); + return ( + + + + + + + {invocationId} + + {instanceName !== undefined && instanceName !== "" && ( + + {instanceName} + + )} + + + + {command !== "" && ( + + {command} + + )} + {cpu !== "" && {cpu}} + {mnemonics !== "" && ( + + {mnemonics} + + )} + {hostname !== "" && ( + {hostname} + )} + {numFetches !== 0 && ( + + {numFetches} + + )} + {isCiWorker && ( + True + )} + {stepLabel !== "" && ( + + {stepLabel} + + )} + {bazelVersion !== "" && ( + + {bazelVersion} + + )} + + + ); }; export default InvocationOverviewDisplay; diff --git a/frontend/src/components/InvocationTargets/InvocationTargetsTable/Columns.tsx b/frontend/src/components/InvocationTargets/InvocationTargetsTable/Columns.tsx index 9260a7be..24ef14c9 100644 --- a/frontend/src/components/InvocationTargets/InvocationTargetsTable/Columns.tsx +++ b/frontend/src/components/InvocationTargets/InvocationTargetsTable/Columns.tsx @@ -1,6 +1,6 @@ import { SearchOutlined } from "@ant-design/icons"; +import { Link } from "@tanstack/react-router"; import type { TableColumnsType } from "antd"; -import { Link } from '@tanstack/react-router'; import { getInvocationTargetAbortReasonFilterOptions } from "@/components/InvocationTargetAbortReasonTag/filter"; import NullBooleanTag from "@/components/NullableBooleanTag"; import SearchWidget, { SearchFilterIcon } from "@/components/SearchWidgets"; @@ -45,10 +45,7 @@ export const columns: TableColumnsType = [ dataIndex: "label", filterSearch: true, render: (_, record) => ( - + {record.target.label} ), diff --git a/frontend/src/components/InvocationTimeline/index.tsx b/frontend/src/components/InvocationTimeline/index.tsx index 27e8d2f8..323be49f 100644 --- a/frontend/src/components/InvocationTimeline/index.tsx +++ b/frontend/src/components/InvocationTimeline/index.tsx @@ -1,3 +1,4 @@ +import { useNavigate } from "@tanstack/react-router"; import { theme } from "antd"; import { useMemo } from "react"; import { @@ -20,7 +21,6 @@ import CommandLinePreview from "../CommandLinePreview"; import PortalAlert from "../PortalAlert"; import type { InvocationInfo, TickProps } from "./types"; import { getInvocationResultTagColor } from "./utils"; -import { useNavigate } from "@tanstack/react-router"; interface Props { invocations: GetBuildInvocationFragment[]; @@ -30,7 +30,7 @@ const BAR_HEIGHT = 20; const CHART_PADDING = 40; const InvocationTimeline: React.FC = ({ invocations }) => { - const navigate = useNavigate() + const navigate = useNavigate(); const { token } = theme.useToken(); const invocationsInfo: InvocationInfo[] = useMemo( @@ -111,8 +111,8 @@ const InvocationTimeline: React.FC = ({ invocations }) => { if (state.activeLabel !== undefined) { navigate({ to: "/bazel-invocations/$invocationID", - params: { invocationID: `${state.activeLabel}` } - }) + params: { invocationID: `${state.activeLabel}` }, + }); } }} > diff --git a/frontend/src/components/LogViewer/index.tsx b/frontend/src/components/LogViewer/index.tsx index e06fb35a..a5a2cf7e 100644 --- a/frontend/src/components/LogViewer/index.tsx +++ b/frontend/src/components/LogViewer/index.tsx @@ -1,10 +1,10 @@ import { AnsiUp } from "ansi_up"; import { Spin } from "antd"; import React from "react"; +import { v4 as uuidv4 } from "uuid"; import { WindowVirtualizer } from "virtua"; import PortalAlert from "@/components/PortalAlert"; import styles from "./index.module.css"; -import { v4 as uuidv4 } from 'uuid'; const ansi = new AnsiUp(); @@ -17,10 +17,13 @@ interface Props { const LogViewer: React.FC = ({ log, loading, error }) => { const lines = React.useMemo(() => { if (!log) return []; - return ansi.ansi_to_html(log).split("\n").map(line => ({ - line, - key: uuidv4() - })); + return ansi + .ansi_to_html(log) + .split("\n") + .map((line) => ({ + line, + key: uuidv4(), + })); }, [log]); if (loading === true) @@ -56,9 +59,12 @@ const LogViewer: React.FC = ({ log, loading, error }) => {
           
             {lines.map((line) => (
    -          // TODO: Remove the danger
    -          // biome-ignore lint/security/noDangerouslySetInnerHtml: Should be reworked
    -          
    +          
             ))}
           
         
    diff --git a/frontend/src/components/MemoryMetrics/index.tsx b/frontend/src/components/MemoryMetrics/index.tsx index 68e95cd8..dfe929b5 100644 --- a/frontend/src/components/MemoryMetrics/index.tsx +++ b/frontend/src/components/MemoryMetrics/index.tsx @@ -1,11 +1,14 @@ -import type React from "react"; -import { Table, Row, Col, Statistic, Space } from "antd"; +import { HddOutlined, PieChartOutlined } from "@ant-design/icons"; +import { Col, Row, Space, Statistic, Table } from "antd"; import type { TableColumnsType } from "antd/lib"; -import type { MemoryMetrics, GarbageMetrics } from "@/graphql/__generated__/graphql"; -import PortalCard from "../PortalCard"; -import { PieChartOutlined, HddOutlined } from "@ant-design/icons"; -import styles from "../../theme/theme.module.css"; +import type React from "react"; +import type { + GarbageMetrics, + MemoryMetrics, +} from "@/graphql/__generated__/graphql"; import { readableFileSize } from "@/utils/filesize"; +import styles from "../../theme/theme.module.css"; +import PortalCard from "../PortalCard"; import SummaryPieChart, { type SummaryChartItem } from "../SummaryPieChart"; import { nullPercent } from "../Utilities/nullPercent"; @@ -75,11 +78,15 @@ const MemoryMetricsDisplay: React.FC<{ /> diff --git a/frontend/src/components/MenuItemLabel/index.tsx b/frontend/src/components/MenuItemLabel/index.tsx index 422a4041..1db64195 100644 --- a/frontend/src/components/MenuItemLabel/index.tsx +++ b/frontend/src/components/MenuItemLabel/index.tsx @@ -1,9 +1,9 @@ -import { type ForwardedRef, forwardRef, useLayoutEffect, useRef } from 'react'; -import { Link, useLocation } from '@tanstack/react-router'; -import { Tag } from 'antd'; -import styles from '@/components/MenuItemLabel/index.module.css'; -import type { UpdateSidebarMenuExpandedWidthFunction } from '@/components/Utilities/navigation'; -import { SIDEBAR_MENU_INLINE_INDENT } from '@/components/SiderBar'; +import { Link, useLocation } from "@tanstack/react-router"; +import { Tag } from "antd"; +import { type ForwardedRef, forwardRef, useLayoutEffect, useRef } from "react"; +import styles from "@/components/MenuItemLabel/index.module.css"; +import { SIDEBAR_MENU_INLINE_INDENT } from "@/components/SiderBar"; +import type { UpdateSidebarMenuExpandedWidthFunction } from "@/components/Utilities/navigation"; export interface MenuItemTag { label: string; @@ -20,57 +20,66 @@ export interface Props { updateMenuItemWidth?: UpdateSidebarMenuExpandedWidthFunction; } -export const MenuItemLabel = forwardRef((props: Props, ref: ForwardedRef) => { - const {pathname} = useLocation(); - const menuItemLabelRef = useRef(null); - useLayoutEffect(() => { - const handleResize = () => { - if (props?.updateMenuItemWidth && menuItemLabelRef.current?.clientWidth) { - const itemMarginInline = 8; - const iconWidth = props.hasIcon ? 16 : 0; - const iconMarginInlineEnd = props.hasIcon ? 10 : 0; - const marginBetweenLabelAndExpandWidth = 16; - const submenuExpanderWidth = props.hasExpander ? 34 : 0; - const fullMenuItemWidth = - itemMarginInline + - iconWidth + - iconMarginInlineEnd + - (props.depth + 1) * SIDEBAR_MENU_INLINE_INDENT + - menuItemLabelRef.current.clientWidth + - marginBetweenLabelAndExpandWidth + - submenuExpanderWidth + - itemMarginInline; - props.updateMenuItemWidth(fullMenuItemWidth); - } - }; - handleResize(); - window.addEventListener('resize', handleResize); - return () => { - window.removeEventListener('resize', handleResize); - }; - }, [props]); - let label = ( - - {props.title} - - ); - if (props.tag) { - label = ( - - {label} - - - {props.tag.label} - - - +export const MenuItemLabel = forwardRef( + (props: Props, ref: ForwardedRef) => { + const { pathname } = useLocation(); + const menuItemLabelRef = useRef(null); + useLayoutEffect(() => { + const handleResize = () => { + if ( + props?.updateMenuItemWidth && + menuItemLabelRef.current?.clientWidth + ) { + const itemMarginInline = 8; + const iconWidth = props.hasIcon ? 16 : 0; + const iconMarginInlineEnd = props.hasIcon ? 10 : 0; + const marginBetweenLabelAndExpandWidth = 16; + const submenuExpanderWidth = props.hasExpander ? 34 : 0; + const fullMenuItemWidth = + itemMarginInline + + iconWidth + + iconMarginInlineEnd + + (props.depth + 1) * SIDEBAR_MENU_INLINE_INDENT + + menuItemLabelRef.current.clientWidth + + marginBetweenLabelAndExpandWidth + + submenuExpanderWidth + + itemMarginInline; + props.updateMenuItemWidth(fullMenuItemWidth); + } + }; + handleResize(); + window.addEventListener("resize", handleResize); + return () => { + window.removeEventListener("resize", handleResize); + }; + }, [props]); + let label = ( + + {props.title} + ); - } - if (pathname === props.href) { - label =
    {label}
    ; - } - return label; -}); -MenuItemLabel.displayName = 'MenuItemLabel'; + if (props.tag) { + label = ( + + {label} + + + {props.tag.label} + + + + ); + } + if (pathname === props.href) { + label =
    {label}
    ; + } + return label; + }, +); +MenuItemLabel.displayName = "MenuItemLabel"; export default MenuItemLabel; diff --git a/frontend/src/components/NullableBooleanTag/index.tsx b/frontend/src/components/NullableBooleanTag/index.tsx index d591c9ff..d8f3fe1f 100644 --- a/frontend/src/components/NullableBooleanTag/index.tsx +++ b/frontend/src/components/NullableBooleanTag/index.tsx @@ -1,56 +1,78 @@ -import type React from 'react'; -import { CheckCircleFilled, CloseCircleFilled, QuestionCircleFilled, } from '@ant-design/icons'; -import { Tag } from 'antd'; -import themeStyles from '@/theme/theme.module.css'; +import { + CheckCircleFilled, + CloseCircleFilled, + QuestionCircleFilled, +} from "@ant-design/icons"; +import { Tag } from "antd"; +import type React from "react"; +import themeStyles from "@/theme/theme.module.css"; interface Props { - status: boolean | null; - hideText?: boolean + status: boolean | null; + hideText?: boolean; } export const BOOL_MAP = [ - "true_tag", - "false_tag", - "false_hide_tag", - "null_tag", - "true_hide_tag" + "true_tag", + "false_tag", + "false_hide_tag", + "null_tag", + "true_hide_tag", ] as const; export type BoolTuple = typeof BOOL_MAP; -export type NilBoolEnum = BoolTuple[number] +export type NilBoolEnum = BoolTuple[number]; const BOOL_TAGS: { [key in NilBoolEnum]: React.ReactNode } = { - false_tag: ( - } color="red" className={themeStyles.tag}>No - ), - false_hide_tag: (} color="red" className={themeStyles.tag} />), - true_tag: ( - } color="green" className={themeStyles.tag}>Yes - ), - true_hide_tag: (} color="green" className={themeStyles.tag} />), - null_tag: ( - } color="orange" className={themeStyles.tag}>? - ), + false_tag: ( + } color="red" className={themeStyles.tag}> + No + + ), + false_hide_tag: ( + } color="red" className={themeStyles.tag} /> + ), + true_tag: ( + } color="green" className={themeStyles.tag}> + Yes + + ), + true_hide_tag: ( + } + color="green" + className={themeStyles.tag} + /> + ), + null_tag: ( + } + color="orange" + className={themeStyles.tag} + > + ? + + ), }; const NullBooleanTag: React.FC = ({ status, hideText }) => { - if (hideText == null) { - hideText = false - } - var status_string: NilBoolEnum = "null_tag"; - if (status === true && hideText === false) { - status_string = "true_tag" - } - if (status === true && hideText === true) { - status_string = "true_hide_tag" - } - if (status === false && hideText === false) { - status_string = "false_tag" - } - if (status === false && hideText === true) { - status_string = "false_hide_tag" - } - const resultTag = BOOL_TAGS[status_string] || BOOL_TAGS.null_tag; - return <>{resultTag}; + if (hideText == null) { + hideText = false; + } + var status_string: NilBoolEnum = "null_tag"; + if (status === true && hideText === false) { + status_string = "true_tag"; + } + if (status === true && hideText === true) { + status_string = "true_hide_tag"; + } + if (status === false && hideText === false) { + status_string = "false_tag"; + } + if (status === false && hideText === true) { + status_string = "false_hide_tag"; + } + const resultTag = BOOL_TAGS[status_string] || BOOL_TAGS.null_tag; + return <>{resultTag}; }; export default NullBooleanTag; diff --git a/frontend/src/components/OperationDetails/index.tsx b/frontend/src/components/OperationDetails/index.tsx index e86f768a..4b88841f 100644 --- a/frontend/src/components/OperationDetails/index.tsx +++ b/frontend/src/components/OperationDetails/index.tsx @@ -1,6 +1,6 @@ -import { useGrpcClients } from "@/context/GrpcClientsContext"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Button, Space, Spin } from "antd"; +import { useGrpcClients } from "@/context/GrpcClientsContext"; import { Code } from "@/lib/grpc-client/google/rpc/code"; import ExecuteResponseDisplay from "../ExecutionResponseDisplay"; import OperationStateDisplay from "../OperationStateDisplay"; diff --git a/frontend/src/components/OperationStateDisplay/index.tsx b/frontend/src/components/OperationStateDisplay/index.tsx index 4f7505b8..41e01670 100644 --- a/frontend/src/components/OperationStateDisplay/index.tsx +++ b/frontend/src/components/OperationStateDisplay/index.tsx @@ -1,23 +1,21 @@ +import { ExclamationCircleFilled } from "@ant-design/icons"; +import { Link } from "@tanstack/react-router"; +import { Descriptions, Space, Tag } from "antd"; import { CodeLink } from "@/components/CodeLink"; import type { OperationState } from "@/lib/grpc-client/buildbarn/buildqueuestate/buildqueuestate"; +import type { OperationsFilterParams } from "@/routes/operations.index"; import themeStyles from "@/theme/theme.module.css"; +import { BrowserPageType } from "@/types/BrowserPageType"; import { protobufToObjectWithTypeField } from "@/utils/protobufToObject"; import { readableDurationFromDates, readableDurationFromSeconds, } from "@/utils/time"; -import { ExclamationCircleFilled } from "@ant-design/icons"; -import { Descriptions, Space, Tag } from "antd"; -import { Link } from '@tanstack/react-router'; +import { generateBrowserSplat } from "@/utils/urlGenerator"; import OperationStatusTag from "../OperationStatusTag"; import { operationsStateToBrowserSplat } from "../OperationsGrid/utils"; import PropertyTagList from "../PropertyTagList"; -import { - historicalExecuteResponseDigestFromOperation, -} from "./utils"; -import { generateBrowserSplat } from "@/utils/urlGenerator"; -import { BrowserPageType } from "@/types/BrowserPageType"; -import type { OperationsFilterParams } from "@/routes/operations.index"; +import { historicalExecuteResponseDigestFromOperation } from "./utils"; interface Props { operation: OperationState; @@ -57,11 +55,13 @@ const OperationStateDisplay: React.FC = ({ operation }) => {
      {invocationMetadata?.map((value) => { - let metadataObject: OperationsFilterParams + let metadataObject: OperationsFilterParams; try { - metadataObject = JSON.parse(value) + metadataObject = JSON.parse(value); } catch { - console.error("Failed to deserialize invocation metadata object") + console.error( + "Failed to deserialize invocation metadata object", + ); } return (
    • @@ -74,7 +74,7 @@ const OperationStateDisplay: React.FC = ({ operation }) => { {value}
    • - ) + ); })}
    @@ -98,12 +98,13 @@ const OperationStateDisplay: React.FC = ({ operation }) => { link={{ to: "/browser/$", params: { - _splat: - generateBrowserSplat(historicalExecuteResponseBrowserPageParams.instanceName, - historicalExecuteResponseBrowserPageParams.digestFunction, - historicalExecuteResponseBrowserPageParams.digest, - BrowserPageType.HistoricalExecuteResponse), - } + _splat: generateBrowserSplat( + historicalExecuteResponseBrowserPageParams.instanceName, + historicalExecuteResponseBrowserPageParams.digestFunction, + historicalExecuteResponseBrowserPageParams.digest, + BrowserPageType.HistoricalExecuteResponse, + ), + }, }} /> diff --git a/frontend/src/components/OperationStateDisplay/utils.ts b/frontend/src/components/OperationStateDisplay/utils.ts index 5e19a21a..b1be4087 100644 --- a/frontend/src/components/OperationStateDisplay/utils.ts +++ b/frontend/src/components/OperationStateDisplay/utils.ts @@ -5,15 +5,22 @@ import { parseBrowserPageSlug } from "@/utils/parseBrowserPageSlug"; export const historicalExecuteResponseDigestFromOperation = ( operation: OperationState, ): BrowserPageParams | undefined => { - if (!operation.completed?.message.startsWith("Action details (uncached result):")) { - return undefined + if ( + !operation.completed?.message.startsWith( + "Action details (uncached result):", + ) + ) { + return undefined; } const url = operation.completed.message.substring(34); - const index = url.indexOf("/browser/") + const index = url.indexOf("/browser/"); if (index === -1) { - return undefined + return undefined; } - const urlSegments = url.substring(index + 9).split("/").filter(segment => segment) - return parseBrowserPageSlug(urlSegments) + const urlSegments = url + .substring(index + 9) + .split("/") + .filter((segment) => segment); + return parseBrowserPageSlug(urlSegments); }; diff --git a/frontend/src/components/OperationsGrid/Columns.tsx b/frontend/src/components/OperationsGrid/Columns.tsx index 45fdd45c..cfbe232a 100644 --- a/frontend/src/components/OperationsGrid/Columns.tsx +++ b/frontend/src/components/OperationsGrid/Columns.tsx @@ -1,15 +1,13 @@ +import { type TableColumnsType, Typography } from "antd"; +import type { ColumnType } from "antd/lib/table"; import { CodeLink } from "@/components/CodeLink"; import type { OperationState } from "@/lib/grpc-client/buildbarn/buildqueuestate/buildqueuestate"; +import { BrowserPageType } from "@/types/BrowserPageType"; import { readableDurationFromDates } from "@/utils/time"; -import { type TableColumnsType, Typography } from "antd"; -import type { ColumnType } from "antd/lib/table"; -import { - historicalExecuteResponseDigestFromOperation, -} from "../OperationStateDisplay/utils"; +import { generateBrowserSplat } from "@/utils/urlGenerator"; +import { historicalExecuteResponseDigestFromOperation } from "../OperationStateDisplay/utils"; import OperationStatusTag from "../OperationStatusTag"; import { operationsStateToBrowserSplat } from "./utils"; -import { generateBrowserSplat } from "@/utils/urlGenerator"; -import { BrowserPageType } from "@/types/BrowserPageType"; const operationNameColumn: ColumnType = { key: "name", @@ -32,9 +30,9 @@ const timeoutColumn: ColumnType = { {record.timeout ? readableDurationFromDates(new Date(), record.timeout, { - precision: 1, - smallestUnit: "s", - }) + precision: 1, + smallestUnit: "s", + }) : "∞"} ), @@ -54,12 +52,13 @@ const actionDigestColumn: ColumnType = { link={{ to: "/browser/$", params: { - _splat: - generateBrowserSplat(historicalExecuteResponseBrowserPageParams.instanceName, - historicalExecuteResponseBrowserPageParams.digestFunction, - historicalExecuteResponseBrowserPageParams.digest, - BrowserPageType.HistoricalExecuteResponse), - } + _splat: generateBrowserSplat( + historicalExecuteResponseBrowserPageParams.instanceName, + historicalExecuteResponseBrowserPageParams.digestFunction, + historicalExecuteResponseBrowserPageParams.digest, + BrowserPageType.HistoricalExecuteResponse, + ), + }, }} /> ); @@ -70,7 +69,7 @@ const actionDigestColumn: ColumnType = { text={`${record.actionDigest?.hash}-${record.actionDigest?.sizeBytes}`} link={{ to: "/browser/$", - params: { _splat: operationsStateToBrowserSplat(record) } + params: { _splat: operationsStateToBrowserSplat(record) }, }} /> ); diff --git a/frontend/src/components/OperationsGrid/index.tsx b/frontend/src/components/OperationsGrid/index.tsx index effd11cb..5f4c1b7b 100644 --- a/frontend/src/components/OperationsGrid/index.tsx +++ b/frontend/src/components/OperationsGrid/index.tsx @@ -3,11 +3,11 @@ import { Table } from "antd"; import type React from "react"; import { useGrpcClients } from "@/context/GrpcClientsContext"; import { RequestMetadata } from "@/lib/grpc-client/build/bazel/remote/execution/v2/remote_execution"; +import type { OperationsFilterParams } from "@/routes/operations.index"; import themeStyles from "@/theme/theme.module.css"; import OperationsInvocationFilter from "../OperationsInvocationFilter"; import PortalAlert from "../PortalAlert"; import getColumns from "./Columns"; -import type { OperationsFilterParams } from "@/routes/operations.index"; const PAGE_SIZE = 1000; @@ -22,10 +22,14 @@ const OperationsTable: React.FC = ({ filter }) => { queryKey: ["operationsTable", filter], queryFn: buildQueueStateClient.listOperations.bind(window, { pageSize: PAGE_SIZE, - filterInvocationId: filter ?{ - typeUrl: filter?.["@type"], - value: RequestMetadata.encode(RequestMetadata.fromPartial(filter)).finish(), - }: undefined + filterInvocationId: filter + ? { + typeUrl: filter?.["@type"], + value: RequestMetadata.encode( + RequestMetadata.fromPartial(filter), + ).finish(), + } + : undefined, }), staleTime: Number.POSITIVE_INFINITY, refetchOnMount: "always", diff --git a/frontend/src/components/OperationsGrid/utils.ts b/frontend/src/components/OperationsGrid/utils.ts index f12e4566..5e0e4568 100644 --- a/frontend/src/components/OperationsGrid/utils.ts +++ b/frontend/src/components/OperationsGrid/utils.ts @@ -2,18 +2,20 @@ import type { OperationState } from "@/lib/grpc-client/buildbarn/buildqueuestate import { BrowserPageType } from "@/types/BrowserPageType"; import { generateBrowserSplat } from "@/utils/urlGenerator"; -export const instanceNameFromOperationState = (operation: OperationState): string => { +export const instanceNameFromOperationState = ( + operation: OperationState, +): string => { const instanceNamePrefix = operation.invocationName?.sizeClassQueueName?.platformQueueName ?.instanceNamePrefix; const instanceNameSuffix = operation.instanceNameSuffix; - const instanceName = [] - if (instanceNamePrefix) instanceName.push(instanceNamePrefix) - if (instanceNameSuffix) instanceName.push(instanceNameSuffix) + const instanceName = []; + if (instanceNamePrefix) instanceName.push(instanceNamePrefix); + if (instanceNameSuffix) instanceName.push(instanceNameSuffix); - return instanceName.join("/") -} + return instanceName.join("/"); +}; export const operationsStateToBrowserSplat = ( operation: OperationState, @@ -25,6 +27,6 @@ export const operationsStateToBrowserSplat = ( instanceNameFromOperationState(operation), operation.digestFunction, operation.actionDigest, - BrowserPageType.Action - ) + BrowserPageType.Action, + ); }; diff --git a/frontend/src/components/OperationsInvocationFilter/index.tsx b/frontend/src/components/OperationsInvocationFilter/index.tsx index a79aa8b8..ebd18920 100644 --- a/frontend/src/components/OperationsInvocationFilter/index.tsx +++ b/frontend/src/components/OperationsInvocationFilter/index.tsx @@ -1,14 +1,14 @@ +import { useNavigate } from "@tanstack/react-router"; import { Button, Col, Divider, Row } from "antd"; -import styles from "./index.module.css"; import type { OperationsFilterParams } from "@/routes/operations.index"; -import { useNavigate } from "@tanstack/react-router"; +import styles from "./index.module.css"; interface Props { filter: OperationsFilterParams; } const OperationsInvocationFilter: React.FC = ({ filter }) => { - const navigate = useNavigate() + const navigate = useNavigate(); if (filter) { return ( @@ -19,7 +19,10 @@ const OperationsInvocationFilter: React.FC = ({ filter }) => {
    {JSON.stringify(filter, null, 2)}
    - diff --git a/frontend/src/components/PlatformQueuesTable/Columns.tsx b/frontend/src/components/PlatformQueuesTable/Columns.tsx index 67683385..3ae48e76 100644 --- a/frontend/src/components/PlatformQueuesTable/Columns.tsx +++ b/frontend/src/components/PlatformQueuesTable/Columns.tsx @@ -1,9 +1,9 @@ +import { Link } from "@tanstack/react-router"; import { type TableColumnsType, Typography } from "antd"; import type { ColumnType } from "antd/lib/table"; -import { Link } from '@tanstack/react-router'; +import { WorkerListStatus } from "@/routes/scheduler.worker"; import PropertyTagList from "../PropertyTagList"; import type { PlatformQueueTableState } from "./types"; -import { WorkerListStatus } from "@/routes/scheduler.worker"; const cellMergingLogic = (value: PlatformQueueTableState) => { if (value.isFirstSizeClass) { @@ -70,14 +70,14 @@ const executingWorkersColumn: ColumnType = { workerStatusFilter: WorkerListStatus.EXECUTING, sizeClassQueueName: { platformQueueName: { - instanceNamePrefix: record.name?.instanceNamePrefix || '', + instanceNamePrefix: record.name?.instanceNamePrefix || "", platform: { - properties: record.name?.platform?.properties || [] + properties: record.name?.platform?.properties || [], }, }, sizeClass: record.sizeClassQueues[0]?.sizeClass, }, - cursor: undefined + cursor: undefined, }} > {executingWorkers} ({percentage}%) @@ -106,15 +106,16 @@ const allWorkersColumn: ColumnType = { workerStatusFilter: WorkerListStatus.ALL, sizeClassQueueName: { platformQueueName: { - instanceNamePrefix: record.name?.instanceNamePrefix || '', + instanceNamePrefix: record.name?.instanceNamePrefix || "", platform: { - properties: record.name?.platform?.properties || [] + properties: record.name?.platform?.properties || [], }, }, sizeClass: record.sizeClassQueues[0]?.sizeClass, }, - cursor: undefined - }}> + cursor: undefined, + }} + > {record.sizeClassQueues[0].workersCount} ), diff --git a/frontend/src/components/PortalAlert/index.tsx b/frontend/src/components/PortalAlert/index.tsx index e3304d54..1a29715e 100644 --- a/frontend/src/components/PortalAlert/index.tsx +++ b/frontend/src/components/PortalAlert/index.tsx @@ -1,18 +1,24 @@ -import type React from 'react'; -import { Alert, Typography } from 'antd'; -import type { AlertProps } from 'antd/lib/alert'; -import styles from './index.module.css'; +import { Alert, Typography } from "antd"; +import type { AlertProps } from "antd/lib/alert"; +import type React from "react"; +import styles from "./index.module.css"; type Props = AlertProps; const PortalAlert: React.FC = ({ className, message, ...props }) => { - return ( - {message} : message} - {...props} - /> - ); + return ( + {message} + ) : ( + message + ) + } + {...props} + /> + ); }; export default PortalAlert; diff --git a/frontend/src/components/PortalCard/index.module.css b/frontend/src/components/PortalCard/index.module.css index 25a74aae..6dcb1d1b 100644 --- a/frontend/src/components/PortalCard/index.module.css +++ b/frontend/src/components/PortalCard/index.module.css @@ -56,4 +56,4 @@ rgba(0, 0, 0, 0.06) 0 2px 4px 0, rgba(0, 0, 0, 0.06) 0 2px 12px -1px, rgba(0, 0, 0, 0.06) 0 4px 8px 0 !important; -} \ No newline at end of file +} diff --git a/frontend/src/components/PortalCard/index.tsx b/frontend/src/components/PortalCard/index.tsx index f0d5ef27..a0c519bc 100644 --- a/frontend/src/components/PortalCard/index.tsx +++ b/frontend/src/components/PortalCard/index.tsx @@ -1,9 +1,16 @@ -import React from 'react'; -import { forwardRef, useLayoutEffect, useRef, useState } from 'react'; -import { Button, Card, type CardProps, List, Popover, Space, theme } from 'antd'; -import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'; -import styles from './index.module.css'; -import themeStyles from '@/theme/theme.module.css'; +import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons"; +import { + Button, + Card, + type CardProps, + List, + Popover, + Space, + theme, +} from "antd"; +import React, { forwardRef, useLayoutEffect, useRef, useState } from "react"; +import themeStyles from "@/theme/theme.module.css"; +import styles from "./index.module.css"; const { useToken } = theme; @@ -12,22 +19,29 @@ interface HeaderProps { className?: string; } -const Header = forwardRef(({ headerBits, className }, ref) => { - const actualClassName = [styles.header, className].join(' '); - return ( - - {headerBits.map( - (headerBit, index) => - headerBit && ( -
    - {headerBit} -
    - ), - )} -
    - ); -}); -Header.displayName = 'Header'; +const Header = forwardRef( + ({ headerBits, className }, ref) => { + const actualClassName = [styles.header, className].join(" "); + return ( + + {headerBits.map( + (headerBit, index) => + headerBit && ( +
    + {headerBit} +
    + ), + )} +
    + ); + }, +); +Header.displayName = "Header"; interface ExtraMenuProps { extraBits: React.ReactNode[]; @@ -35,13 +49,19 @@ interface ExtraMenuProps { setIsExtraMenuOpened: React.Dispatch>; } -const ExtraMenu: React.FC = ({ extraBits, isExtraMenuOpen, setIsExtraMenuOpened }) => { +const ExtraMenu: React.FC = ({ + extraBits, + isExtraMenuOpen, + setIsExtraMenuOpened, +}) => { return ( {item}} + renderItem={(item) => ( + {item} + )} size="small" className={styles.list} /> @@ -52,23 +72,34 @@ const ExtraMenu: React.FC = ({ extraBits, isExtraMenuOpen, setIs placement="leftTop" > ); }; -interface Props extends Omit { +interface Props extends Omit { icon: React.ReactNode; titleBits: React.ReactNode[]; extraBits?: React.ReactNode[]; className?: string; } -export const PortalCard: React.FC = ({ icon, titleBits, extraBits, className, ...cardProps }) => { +export const PortalCard: React.FC = ({ + icon, + titleBits, + extraBits, + className, + ...cardProps +}) => { const { token } = useToken(); const [isExtraMenuOpen, setIsExtraMenuOpened] = useState(false); - const [isExtraMenuDisplayed, setIsExtraMenuDisplayed] = useState(true); + const [isExtraMenuDisplayed, setIsExtraMenuDisplayed] = + useState(true); const cardRef = useRef(null); const titleRef = useRef(null); const extraRef = useRef(null); @@ -78,7 +109,9 @@ export const PortalCard: React.FC = ({ icon, titleBits, extraBits, classN if (cardRef.current && titleRef.current && extraRef.current) { return ( cardRef.current.clientWidth < - titleRef.current.clientWidth + extraRef.current.clientWidth + minimumSpaceBetweenTitleAndExtra + titleRef.current.clientWidth + + extraRef.current.clientWidth + + minimumSpaceBetweenTitleAndExtra ); } return true; @@ -90,12 +123,18 @@ export const PortalCard: React.FC = ({ icon, titleBits, extraBits, classN } }; handleResize(); - window.addEventListener('resize', handleResize); + window.addEventListener("resize", handleResize); return () => { - window.removeEventListener('resize', handleResize); + window.removeEventListener("resize", handleResize); }; }, [isExtraMenuDisplayed, minimumSpaceBetweenTitleAndExtra]); - const title =
    ; + const title = ( +
    + ); const extra = extraBits?.length && (
    @@ -110,10 +149,24 @@ export const PortalCard: React.FC = ({ icon, titleBits, extraBits, classN
    ); - const extendedClassName = cardProps.type === 'inner' ? className : [className, styles.outer].join(' '); + const extendedClassName = + cardProps.type === "inner" + ? className + : [className, styles.outer].join(" "); return ( - - + + {cardProps.children} diff --git a/frontend/src/components/PortalDuration/index.tsx b/frontend/src/components/PortalDuration/index.tsx index 45a9728f..3faeedda 100644 --- a/frontend/src/components/PortalDuration/index.tsx +++ b/frontend/src/components/PortalDuration/index.tsx @@ -3,7 +3,10 @@ import { Divider, Popover, Tag } from "antd"; import type React from "react"; import dayjs from "@/lib/dayjs"; import themeStyles from "@/theme/theme.module.css"; -import { type ReadableFormatConfig, readableDurationFromDates } from "@/utils/time"; +import { + type ReadableFormatConfig, + readableDurationFromDates, +} from "@/utils/time"; import styles from "./index.module.css"; interface Props { diff --git a/frontend/src/components/PreviousExecuteStatsPlot/index.tsx b/frontend/src/components/PreviousExecuteStatsPlot/index.tsx index c25a559c..320bd663 100644 --- a/frontend/src/components/PreviousExecuteStatsPlot/index.tsx +++ b/frontend/src/components/PreviousExecuteStatsPlot/index.tsx @@ -1,6 +1,3 @@ -import type { PreviousExecutionStats } from "@/lib/grpc-client/buildbarn/iscc/iscc"; -import { readableDurationFromSeconds } from "@/utils/time"; -import { protobufDurationToSeconds } from "@/utils/time"; import { Legend, ReferenceArea, @@ -10,6 +7,11 @@ import { XAxis, YAxis, } from "recharts"; +import type { PreviousExecutionStats } from "@/lib/grpc-client/buildbarn/iscc/iscc"; +import { + protobufDurationToSeconds, + readableDurationFromSeconds, +} from "@/utils/time"; interface Props { prevStats: PreviousExecutionStats; diff --git a/frontend/src/components/ProfileDropdown/index.tsx b/frontend/src/components/ProfileDropdown/index.tsx index c2fd32e9..2a7e2c47 100644 --- a/frontend/src/components/ProfileDropdown/index.tsx +++ b/frontend/src/components/ProfileDropdown/index.tsx @@ -1,12 +1,12 @@ -import type { BazelInvocationInfoFragment } from "@/graphql/__generated__/graphql"; -import { digestFunctionValueFromString } from "@/utils/digestFunctionUtils"; -import { generateFileUrl } from "@/utils/urlGenerator"; import { - DownOutlined, DownloadOutlined, + DownOutlined, ProjectOutlined, } from "@ant-design/icons"; import { Button, Dropdown, type MenuProps, Space } from "antd"; +import type { BazelInvocationInfoFragment } from "@/graphql/__generated__/graphql"; +import { digestFunctionValueFromString } from "@/utils/digestFunctionUtils"; +import { generateFileUrl } from "@/utils/urlGenerator"; type Profile = NonNullable; diff --git a/frontend/src/components/SchedulerStatistics/index.tsx b/frontend/src/components/SchedulerStatistics/index.tsx index c20e7709..fea24a0c 100644 --- a/frontend/src/components/SchedulerStatistics/index.tsx +++ b/frontend/src/components/SchedulerStatistics/index.tsx @@ -1,6 +1,6 @@ import { useQuery } from "@tanstack/react-query"; +import { Link } from "@tanstack/react-router"; import { Skeleton, Space, Statistic } from "antd"; -import { Link } from '@tanstack/react-router'; import type React from "react"; import { useGrpcClients } from "@/context/GrpcClientsContext"; import PortalAlert from "../PortalAlert"; diff --git a/frontend/src/components/SearchWidgets/index.tsx b/frontend/src/components/SearchWidgets/index.tsx index 58125690..f7df77da 100644 --- a/frontend/src/components/SearchWidgets/index.tsx +++ b/frontend/src/components/SearchWidgets/index.tsx @@ -1,19 +1,19 @@ -import type React from "react"; +import { blue } from "@ant-design/colors"; import { Button, + DatePicker, + Divider, Input, Space, - Divider, - DatePicker, type TimeRangePickerProps, Tooltip, } from "antd"; import type { FilterDropdownProps } from "antd/es/table/interface"; import dayjs from "dayjs"; -import { blue } from "@ant-design/colors"; +import timezone from "dayjs/plugin/timezone"; +import utc from "dayjs/plugin/utc"; +import type React from "react"; import styles from "@/components/SearchWidgets/index.module.css"; -import timezone from 'dayjs/plugin/timezone'; -import utc from 'dayjs/plugin/utc'; dayjs.extend(utc); dayjs.extend(timezone); diff --git a/frontend/src/components/SiderBar/index.tsx b/frontend/src/components/SiderBar/index.tsx index 295cde45..ef9acb29 100644 --- a/frontend/src/components/SiderBar/index.tsx +++ b/frontend/src/components/SiderBar/index.tsx @@ -1,17 +1,16 @@ - -import type React from 'react'; -import { type Key, useState } from 'react' -import { Layout, Menu, type MenuProps } from 'antd'; -import type { ItemType } from 'antd/es/menu/interface'; -import styles from '@/components/SiderBar/index.module.css'; -import { useLocation } from '@tanstack/react-router'; -import { getClosestKey } from '../Utilities/navigation'; +import { useLocation } from "@tanstack/react-router"; +import { Layout, Menu, type MenuProps } from "antd"; +import type { ItemType } from "antd/es/menu/interface"; +import type React from "react"; +import { type Key, useState } from "react"; +import styles from "@/components/SiderBar/index.module.css"; +import { getClosestKey } from "../Utilities/navigation"; export const SIDEBAR_MENU_INLINE_INDENT = 24; const DEFAULT_SIDER_WIDTH = 280; -const SIDER_EXPANDED_WIDTH_LOCAL_STORAGE_KEY = 'sider-width'; +const SIDER_EXPANDED_WIDTH_LOCAL_STORAGE_KEY = "sider-width"; interface Props { menuKey?: Key; @@ -19,7 +18,7 @@ interface Props { defaultSelectedKeys?: Key[]; defaultOpenKeys?: Key[]; openKeys?: Key[]; - onOpenChange?: MenuProps['onOpenChange']; + onOpenChange?: MenuProps["onOpenChange"]; expandedWidth?: number; } @@ -39,7 +38,9 @@ const SiderBar: React.FC = ({ : []; const [siderWidth, setSiderWidth] = useState(() => { - const cachedExpandedState = window.localStorage.getItem(SIDER_EXPANDED_WIDTH_LOCAL_STORAGE_KEY); + const cachedExpandedState = window.localStorage.getItem( + SIDER_EXPANDED_WIDTH_LOCAL_STORAGE_KEY, + ); if (cachedExpandedState) { return parseInt(cachedExpandedState, 10); } @@ -51,20 +52,28 @@ const SiderBar: React.FC = ({ key.toString())} - defaultOpenKeys={defaultOpenKeys?.map(key => key.toString())} + defaultSelectedKeys={defaultSelectedKeys?.map((key) => key.toString())} + defaultOpenKeys={defaultOpenKeys?.map((key) => key.toString())} selectedKeys={currentKeys} - openKeys={openKeys?.map(key => key.toString())} + openKeys={openKeys?.map((key) => key.toString())} onOpenChange={onOpenChange} onMouseMove={() => { const updatedSiderWidth = - expandedWidth && expandedWidth > DEFAULT_SIDER_WIDTH ? expandedWidth : DEFAULT_SIDER_WIDTH; + expandedWidth && expandedWidth > DEFAULT_SIDER_WIDTH + ? expandedWidth + : DEFAULT_SIDER_WIDTH; setSiderWidth(updatedSiderWidth); - localStorage.setItem(SIDER_EXPANDED_WIDTH_LOCAL_STORAGE_KEY, updatedSiderWidth.toString()); + localStorage.setItem( + SIDER_EXPANDED_WIDTH_LOCAL_STORAGE_KEY, + updatedSiderWidth.toString(), + ); }} onMouseLeave={() => { setSiderWidth(DEFAULT_SIDER_WIDTH); - localStorage.setItem(SIDER_EXPANDED_WIDTH_LOCAL_STORAGE_KEY, DEFAULT_SIDER_WIDTH.toString()); + localStorage.setItem( + SIDER_EXPANDED_WIDTH_LOCAL_STORAGE_KEY, + DEFAULT_SIDER_WIDTH.toString(), + ); }} className={styles.menu} style={{ width: `${siderWidth}px` }} diff --git a/frontend/src/components/SizeClassOutcome/index.tsx b/frontend/src/components/SizeClassOutcome/index.tsx index 1c86ccde..18dee062 100644 --- a/frontend/src/components/SizeClassOutcome/index.tsx +++ b/frontend/src/components/SizeClassOutcome/index.tsx @@ -1,6 +1,6 @@ +import { Space, Typography } from "antd"; import type { PerSizeClassStats } from "@/lib/grpc-client/buildbarn/iscc/iscc"; import { readableDurationFromProtobufDuration } from "@/utils/time"; -import { Space, Typography } from "antd"; import SizeClassOutcomeTag from "../SizeClassOutcomeTag"; interface Props { diff --git a/frontend/src/components/SizeClassOutcomeTag/index.tsx b/frontend/src/components/SizeClassOutcomeTag/index.tsx index 981780f0..c5e8dc80 100644 --- a/frontend/src/components/SizeClassOutcomeTag/index.tsx +++ b/frontend/src/components/SizeClassOutcomeTag/index.tsx @@ -1,5 +1,5 @@ -import themeStyles from "@/theme/theme.module.css"; import { Tag } from "antd"; +import themeStyles from "@/theme/theme.module.css"; interface Props { color?: string; diff --git a/frontend/src/components/SourceControlDisplay/index.tsx b/frontend/src/components/SourceControlDisplay/index.tsx index e937f838..c25d438d 100644 --- a/frontend/src/components/SourceControlDisplay/index.tsx +++ b/frontend/src/components/SourceControlDisplay/index.tsx @@ -1,10 +1,10 @@ +import { BranchesOutlined } from "@ant-design/icons"; +import { Descriptions, Row, Space } from "antd"; +import type React from "react"; import { type SourceControl, SourceControlProvider, } from "@/graphql/__generated__/graphql"; -import { BranchesOutlined } from "@ant-design/icons"; -import { Descriptions, Row, Space } from "antd"; -import type React from "react"; import PortalCard from "../PortalCard"; const getRepoUrl = ( @@ -38,20 +38,24 @@ const getRefLabelAndUrl = ( switch (sc?.provider) { case SourceControlProvider.Github: if (sc.refs.startsWith("refs/heads/")) { - return ["Branch", `${repoUrl}/tree/${sc.refs.substring("refs/heads/".length)}`]; + return [ + "Branch", + `${repoUrl}/tree/${sc.refs.substring("refs/heads/".length)}`, + ]; } if (sc.refs.startsWith("refs/tags/")) { - return ["Tag",`${repoUrl}/tree/${sc.refs.substring("refs/tags/".length)}`]; + return [ + "Tag", + `${repoUrl}/tree/${sc.refs.substring("refs/tags/".length)}`, + ]; } if (sc.refs.startsWith("refs/pull/")) { - const prNumber = sc.refs - .substring("refs/pull/".length) - .split("/")[0]; - return ["Pull request",`${repoUrl}/pull/${prNumber}`]; + const prNumber = sc.refs.substring("refs/pull/".length).split("/")[0]; + return ["Pull request", `${repoUrl}/pull/${prNumber}`]; } - return ["Ref",`${repoUrl}/tree/${sc.refs}`]; + return ["Ref", `${repoUrl}/tree/${sc.refs}`]; case SourceControlProvider.Gitlab: - return ["Branch",`${repoUrl}/-/tree/${sc.refs}`]; + return ["Branch", `${repoUrl}/-/tree/${sc.refs}`]; default: return [undefined, undefined]; } @@ -166,10 +170,16 @@ const SourceControlDisplay: React.FC<{ - + - + {sourceControlData?.eventName} diff --git a/frontend/src/components/SummaryPieChart/index.tsx b/frontend/src/components/SummaryPieChart/index.tsx index 614c9456..f6143854 100644 --- a/frontend/src/components/SummaryPieChart/index.tsx +++ b/frontend/src/components/SummaryPieChart/index.tsx @@ -1,9 +1,16 @@ +import { Link, type LinkOptions } from "@tanstack/react-router"; import { Space, theme } from "antd"; -import { Link, type LinkOptions } from '@tanstack/react-router'; -import { useState } from "react"; -import { Legend, type LegendPayload, Pie, PieChart, type PieSectorDataItem, type PieSectorShapeProps, Sector } from "recharts"; +import React, { useState } from "react"; +import { + Legend, + type LegendPayload, + Pie, + PieChart, + type PieSectorDataItem, + type PieSectorShapeProps, + Sector, +} from "recharts"; import { themeColor } from "./utils"; -import React from "react"; // The `Legend` component uses `value`, `fill`, // and `type` by default to render the data. @@ -37,49 +44,59 @@ const SummaryPieChart: React.FC = ({ .sort((a, b) => b.count - a.count) .map((item, index) => ({ ...item, - fill: item.fill ?? themeColor(token, index) + fill: item.fill ?? themeColor(token, index), })); - const [hoverIndex, setHoverIndex] = React.useState(undefined); - const handleMouseEnter = (_payload: LegendPayload | PieSectorDataItem, index: number) => { + const [hoverIndex, setHoverIndex] = React.useState( + undefined, + ); + const handleMouseEnter = ( + _payload: LegendPayload | PieSectorDataItem, + index: number, + ) => { setHoverIndex(index); }; const handleMouseLeave = () => { setHoverIndex(undefined); }; - const renderLegendText = (_value: string, entry: LegendPayload, index: number) => { - let item: SummaryChartItem | undefined + const renderLegendText = ( + _value: string, + entry: LegendPayload, + index: number, + ) => { + let item: SummaryChartItem | undefined; if (entry.payload) { - item = entry.payload as SummaryChartItem + item = entry.payload as SummaryChartItem; } - let legendText = <> - {item?.count ?? 0}{" "} - {item?.link ? {item.value} : item?.value} ( - {item?.percent}) - + let legendText = ( + <> + {item?.count ?? 0}{" "} + {item?.link ? {item.value} : item?.value} ( + {item?.percent}) + + ); if (index === hoverIndex) { - legendText = {legendText} + legendText = {legendText}; } - return ( - - {legendText} - - ); + return {legendText}; }; - const renderPieShape = ({ - cx, - cy, - innerRadius, - outerRadius, - startAngle, - endAngle, - fill, - }: PieSectorShapeProps, index: number) => { + const renderPieShape = ( + { + cx, + cy, + innerRadius, + outerRadius, + startAngle, + endAngle, + fill, + }: PieSectorShapeProps, + index: number, + ) => { return ( = ({ fill={fill} stroke="white" /> - {index === hoverIndex && + {index === hoverIndex && ( = ({ fill={fill} stroke="white" /> - } + )} ); }; - return - - - - -
    - - {/* Use a React Portal to move the legend to outside of the PieChart. This makes layout easier */} -
    -
    - + return ( + + + + + +
    + {/* Use a React Portal to move the legend to outside of the PieChart. This makes layout easier */} +
    +
    + + ); }; export default SummaryPieChart; diff --git a/frontend/src/components/SystemMetricsDisplay/index.tsx b/frontend/src/components/SystemMetricsDisplay/index.tsx index ed5e4685..454e4db0 100644 --- a/frontend/src/components/SystemMetricsDisplay/index.tsx +++ b/frontend/src/components/SystemMetricsDisplay/index.tsx @@ -1,47 +1,113 @@ +import { FieldTimeOutlined } from "@ant-design/icons"; +import { Row, Space, Statistic } from "antd"; import type React from "react"; -import { Statistic, Space, Row } from 'antd'; -import { FieldTimeOutlined, } from "@ant-design/icons"; -import type { SystemNetworkStats, TimingMetrics } from "@/graphql/__generated__/graphql"; -import PortalCard from "../PortalCard"; -import { readableDurationFromMilliseconds } from "@/utils/time"; +import type { + SystemNetworkStats, + TimingMetrics, +} from "@/graphql/__generated__/graphql"; import { readableFileSize } from "@/utils/filesize"; +import { readableDurationFromMilliseconds } from "@/utils/time"; +import PortalCard from "../PortalCard"; const SystemMetricsDisplay: React.FC<{ - timingMetrics: TimingMetrics | undefined, - systemNetworkStats: SystemNetworkStats | undefined -}> = ({ - timingMetrics, - systemNetworkStats -}) => { - return ( - - }> - - - - - - - - - - - }> - - - - - - - - - - - - - - - ) - } + timingMetrics: TimingMetrics | undefined; + systemNetworkStats: SystemNetworkStats | undefined; +}> = ({ timingMetrics, systemNetworkStats }) => { + return ( + + } + > + + + + + + + + + + + } + > + + + + + + + + + + + + + + + ); +}; -export default SystemMetricsDisplay; \ No newline at end of file +export default SystemMetricsDisplay; diff --git a/frontend/src/components/Targets/TargetGrid/Columns.tsx b/frontend/src/components/Targets/TargetGrid/Columns.tsx index bdb5ae2c..a4f72d12 100644 --- a/frontend/src/components/Targets/TargetGrid/Columns.tsx +++ b/frontend/src/components/Targets/TargetGrid/Columns.tsx @@ -1,6 +1,6 @@ import { SearchOutlined } from "@ant-design/icons"; +import { Link } from "@tanstack/react-router"; import type { TableColumnsType } from "antd/lib"; -import { Link } from '@tanstack/react-router'; import { SearchFilterIcon, SearchWidget } from "@/components/SearchWidgets"; import type { GetTargetsListQuery } from "@/graphql/__generated__/graphql"; @@ -36,7 +36,9 @@ export const columns: TableColumnsType = [ dataIndex: "label", filterSearch: true, render: (_, record) => ( - {record.label} + + {record.label} + ), filterDropdown: (filterProps) => ( diff --git a/frontend/src/components/Targets/TargetGridBtn/index.tsx b/frontend/src/components/Targets/TargetGridBtn/index.tsx index e35d6f1b..a0d8a061 100644 --- a/frontend/src/components/Targets/TargetGridBtn/index.tsx +++ b/frontend/src/components/Targets/TargetGridBtn/index.tsx @@ -1,43 +1,46 @@ -import type React from 'react'; import { - CheckCircleFilled, - CloseCircleFilled, - QuestionCircleFilled, -} from '@ant-design/icons'; -import { Button } from 'antd'; -import themeStyles from '@/theme/theme.module.css'; + CheckCircleFilled, + CloseCircleFilled, + QuestionCircleFilled, +} from "@ant-design/icons"; +import { Button } from "antd"; +import type React from "react"; +import themeStyles from "@/theme/theme.module.css"; interface Props { - status: boolean | null; - invocationId: string; + status: boolean | null; + invocationId: string; } function getIconForStatus(status: boolean | null) { - if (status == null) { - return - } - if (status === true) { - return - } - return + if (status == null) { + return ; + } + if (status === true) { + return ; + } + return ; } function getClassForStatus(status: boolean | null) { - if (status == null) { - return themeStyles.colorDisabled - } - if (status === true) { - return themeStyles.colorSuccess - } - return themeStyles.colorFailure + if (status == null) { + return themeStyles.colorDisabled; + } + if (status === true) { + return themeStyles.colorSuccess; + } + return themeStyles.colorFailure; } const TargetGridBtn: React.FC = ({ status, invocationId }) => { - const resultTag =