Skip to content

Commit b30bb6c

Browse files
authored
add page with summary of targets data from rbe (#10252)
flag-guarded, but defaulted to true
1 parent e1d8518 commit b30bb6c

File tree

22 files changed

+790
-61
lines changed

22 files changed

+790
-61
lines changed

app/router/router.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class Router {
5757
shortcuts.registerSequence([KeyCombo.g, KeyCombo.g], () => {
5858
this.navigateToSettings();
5959
});
60+
shortcuts.registerSequence([KeyCombo.g, KeyCombo.s], () => {
61+
this.navigateToTargets();
62+
});
6063

6164
this.redirectIfNecessary();
6265
}
@@ -228,6 +231,10 @@ class Router {
228231
this.navigateTo(Path.settingsPath);
229232
}
230233

234+
navigateToTargets() {
235+
this.navigateTo(Path.targetsPath);
236+
}
237+
231238
navigateToTrends() {
232239
this.navigateTo(Path.trendsPath);
233240
}
@@ -621,6 +628,7 @@ export class Path {
621628
static codePath = "/code/";
622629
static reviewsPath = "/reviews/";
623630
static codesearchPath = "/search/";
631+
static targetsPath = "/targets/";
624632
}
625633

626634
export type TrendsChartId = "builds" | "duration" | "cache" | "cas" | "savings" | "build_time";
@@ -649,6 +657,7 @@ function getUnavailableMessage(matchedPath: string) {
649657
case Path.codePath:
650658
case Path.settingsPath:
651659
case Path.trendsPath:
660+
case Path.targetsPath:
652661
case Path.executorsPath:
653662
case Path.tapPath:
654663
case Path.userHistoryPath:

enterprise/app/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ genrule(
5151
"//enterprise/app/sidekick/buildfile:buildfile.css",
5252
"//enterprise/app/sidekick/module:module.css",
5353
"//enterprise/app/tap:tap.css",
54+
"//enterprise/app/targets:targets.css",
5455
"//enterprise/app/trends:trends.css",
5556
"//enterprise/app/usage:usage.css",
5657
"//enterprise/app/root:root.css",

enterprise/app/root/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ ts_library(
4949
"//enterprise/app/shortcuts",
5050
"//enterprise/app/sidebar",
5151
"//enterprise/app/tap",
52+
"//enterprise/app/targets",
5253
"//enterprise/app/trends",
5354
"//enterprise/app/usage",
5455
"//enterprise/app/workflows",

enterprise/app/root/root.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import SettingsComponent from "../settings/settings";
2525
import ShortcutsComponent from "../shortcuts/shortcuts";
2626
import SidebarComponent from "../sidebar/sidebar";
2727
import TapComponent from "../tap/tap";
28+
import TargetsComponent from "../targets/targets";
2829
import TrendsComponent from "../trends/trends";
2930
import UsageComponent from "../usage/usage";
3031
import WorkflowsComponent from "../workflows/workflows";
@@ -64,6 +65,7 @@ capabilities.register("BuildBuddy Enterprise", true, [
6465
Path.workflowsPath,
6566
Path.settingsPath,
6667
Path.trendsPath,
68+
Path.targetsPath,
6769
Path.executorsPath,
6870
Path.tapPath,
6971
Path.codePath,
@@ -218,6 +220,7 @@ export default class EnterpriseRootComponent extends React.Component {
218220
let orgJoinAuthenticated = this.state.path.startsWith(Path.joinOrgPath) && this.state.user;
219221
let orgAccessDenied = this.state.user && this.state.path === Path.orgAccessDeniedPath;
220222
let trends = this.state.user && this.state.path.startsWith("/trends");
223+
let targets = this.state.user && this.state.path.startsWith("/targets");
221224
let usage = this.state.user && this.state.path.startsWith("/usage/");
222225
let auditLogs = this.state.user && this.state.path.startsWith("/audit-logs/");
223226
let executors = this.state.user && this.state.path.startsWith("/executors");
@@ -236,6 +239,7 @@ export default class EnterpriseRootComponent extends React.Component {
236239
!orgJoinAuthenticated &&
237240
!orgAccessDenied &&
238241
!trends &&
242+
!targets &&
239243
!usage &&
240244
!executors &&
241245
!tests &&
@@ -396,6 +400,7 @@ export default class EnterpriseRootComponent extends React.Component {
396400
<TrendsComponent user={this.state.user} search={this.state.search} tab={this.state.tab} />
397401
</Suspense>
398402
)}
403+
{targets && this.state.user && <TargetsComponent user={this.state.user} search={this.state.search} />}
399404
{usage && this.state.user && <UsageComponent user={this.state.user} />}
400405
{auditLogs && this.state.user && <AuditLogsComponent user={this.state.user} />}
401406
{executors && this.state.user && <ExecutorsComponent path={this.state.path} user={this.state.user} />}

enterprise/app/shortcuts/shortcuts.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ export default class ShortcutsComponent extends React.Component<Props, State> {
6363
<td className="keyboard-shortcut-key">g-t</td>
6464
<td>Go to Tests page</td>
6565
</tr>
66+
<tr>
67+
<td className="keyboard-shortcut-key">g-s</td>
68+
<td>Go to Targets page</td>
69+
</tr>
6670
<tr>
6771
<td className="keyboard-shortcut-key">g-x</td>
6872
<td>Go to Executors page</td>

enterprise/app/sidebar/sidebar.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
SearchCode,
1919
Sliders,
2020
Snowflake,
21+
Target,
2122
Terminal,
2223
Users,
2324
ZoomIn,
@@ -63,6 +64,10 @@ export default class SidebarComponent extends React.Component<Props, State> {
6364
return this.props.path.startsWith("/trends/");
6465
}
6566

67+
isTargetsSelected() {
68+
return this.props.path.startsWith("/targets/");
69+
}
70+
6671
isExecutorsSelected() {
6772
return this.props.path.startsWith("/executors/");
6873
}
@@ -164,6 +169,12 @@ export default class SidebarComponent extends React.Component<Props, State> {
164169
<span className="sidebar-item-text">Drilldown</span>
165170
</SidebarLink>
166171
)}
172+
{capabilities.config.targetsPageEnabled && (
173+
<SidebarLink selected={this.isTargetsSelected()} href={Path.targetsPath} title="Targets">
174+
<Target className="icon" />
175+
<span className="sidebar-item-text">Targets</span>
176+
</SidebarLink>
177+
)}
167178
{capabilities.test && (
168179
<SidebarLink
169180
selected={this.isTapSelected() && this.props.tab != "#flakes"}

enterprise/app/targets/BUILD

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
load("//rules/typescript:index.bzl", "ts_library")
2+
3+
package(default_visibility = ["//enterprise:__subpackages__"])
4+
5+
exports_files(["targets.css"])
6+
7+
ts_library(
8+
name = "targets",
9+
srcs = ["targets.tsx"],
10+
deps = [
11+
"//:node_modules/@types/react",
12+
"//:node_modules/react",
13+
"//:node_modules/recharts",
14+
"//:node_modules/tslib",
15+
"//app/auth:user",
16+
"//app/components/button",
17+
"//app/components/filter_input",
18+
"//app/components/select",
19+
"//app/components/spinner",
20+
"//app/router",
21+
"//app/service:rpc_service",
22+
"//enterprise/app/filter",
23+
"//enterprise/app/filter:filter_util",
24+
"//enterprise/app/trends:common",
25+
"//enterprise/app/trends:trends_chart",
26+
"//proto:stat_filter_ts_proto",
27+
"//proto:stats_ts_proto",
28+
],
29+
)

enterprise/app/targets/targets.css

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
.targets {
2+
margin-top: 32px;
3+
}
4+
5+
.targets .targets-header {
6+
display: flex;
7+
align-items: center;
8+
justify-content: space-between;
9+
margin-bottom: 16px;
10+
}
11+
12+
.targets .targets-title {
13+
font-size: 32px;
14+
font-weight: 700;
15+
}
16+
17+
.targets .targets-controls {
18+
margin-bottom: 24px;
19+
}
20+
21+
.targets .targets-controls .controls.row {
22+
display: flex;
23+
align-items: center;
24+
gap: 12px;
25+
margin-bottom: 16px;
26+
}
27+
28+
.targets .targets-controls .controls.row:last-child {
29+
margin-bottom: 0;
30+
}
31+
32+
.targets .targets-controls .controls.row label {
33+
font-weight: 600;
34+
color: #333;
35+
min-width: 60px;
36+
}
37+
38+
.targets .targets-metric-select {
39+
min-width: 150px;
40+
}
41+
42+
.targets .loading-section {
43+
display: flex;
44+
justify-content: center;
45+
align-items: center;
46+
padding: 40px;
47+
}
48+
49+
.targets .error-section {
50+
display: flex;
51+
justify-content: center;
52+
align-items: center;
53+
padding: 40px;
54+
}
55+
56+
.targets .error-message {
57+
color: #d32f2f;
58+
font-size: 16px;
59+
}
60+
61+
.targets .targets-section-title {
62+
font-size: 20px;
63+
font-weight: 600;
64+
margin-bottom: 16px;
65+
color: #333;
66+
}
67+
68+
.targets .targets-chart-section {
69+
margin-bottom: 16px;
70+
}
71+
72+
.targets .targets-chart-container {
73+
display: flex;
74+
justify-content: center;
75+
margin-bottom: 16px;
76+
overflow-x: auto;
77+
}
78+
79+
.targets .targets-table-section {
80+
margin-bottom: 40px;
81+
}
82+
83+
.targets .targets-table-container {
84+
background: white;
85+
border-radius: 8px;
86+
overflow: hidden;
87+
}
88+
89+
.targets .targets-table-container .results-table {
90+
border: 1px solid #e0e0e0;
91+
border-radius: 4px;
92+
overflow: hidden;
93+
}
94+
95+
.targets .targets-table-container .row {
96+
display: flex;
97+
border-bottom: 1px solid #e0e0e0;
98+
}
99+
100+
.targets .targets-table-container .row:last-child {
101+
border-bottom: none;
102+
}
103+
104+
.targets .targets-table-container .column-headers {
105+
font-weight: 600;
106+
color: #333;
107+
}
108+
109+
.targets .targets-table-container .result-row {
110+
transition: background-color 0.2s ease;
111+
}
112+
113+
.targets .targets-table-container .result-row:hover {
114+
background-color: #f9f9f9;
115+
}
116+
117+
.targets .targets-table-container .result-row.clickable {
118+
cursor: pointer;
119+
}
120+
121+
.targets .targets-table-container .result-row.clickable:hover {
122+
background-color: #f0f0f0;
123+
}
124+
125+
.targets .targets-table-container .name-column {
126+
flex: 1;
127+
padding: 16px 32px;
128+
min-width: 0;
129+
}
130+
131+
.targets .targets-table-container .value-column {
132+
flex: 0 0 150px;
133+
padding: 16px 32px;
134+
text-align: right;
135+
}
136+
137+
.targets .table-footer-controls {
138+
display: flex;
139+
justify-content: center;
140+
padding: 16px;
141+
border-top: 1px solid #e0e0e0;
142+
}
143+
144+
.targets .load-more-button {
145+
display: flex;
146+
align-items: center;
147+
gap: 8px;
148+
}
149+
150+
.targets .targets-table-target {
151+
word-break: break-all;
152+
overflow: hidden;
153+
text-overflow: ellipsis;
154+
}
155+
156+
.targets .targets-table-value {
157+
font-weight: 500;
158+
white-space: nowrap;
159+
}
160+
161+
.targets .targets-empty {
162+
display: flex;
163+
justify-content: center;
164+
align-items: center;
165+
padding: 80px 40px;
166+
}
167+
168+
.targets .empty-message {
169+
color: #666;
170+
font-size: 16px;
171+
text-align: center;
172+
}
173+
174+
.targets .empty-message a {
175+
text-decoration: underline;
176+
}
177+
178+
.targets .trend-chart-hover {
179+
background: white;
180+
border: 1px solid #ccc;
181+
border-radius: 4px;
182+
padding: 8px 12px;
183+
font-size: 12px;
184+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
185+
}

0 commit comments

Comments
 (0)