add tooltips to metricConfig, rendered in metricSelect, rework availability display

This commit is contained in:
Christoph Kluge
2026-07-28 17:36:29 +02:00
parent 1bd3f25371
commit 053c44d7b5
9 changed files with 257 additions and 23 deletions
+2
View File
@@ -143,6 +143,7 @@ type MetricConfig {
alert: Float! alert: Float!
lowerIsBetter: Boolean lowerIsBetter: Boolean
subClusters: [SubClusterConfig!]! subClusters: [SubClusterConfig!]!
tooltip: String
} }
type Tag { type Tag {
@@ -300,6 +301,7 @@ type GlobalMetricListItem {
unit: Unit! unit: Unit!
scope: MetricScope! scope: MetricScope!
footprint: String footprint: String
tooltip: String
availability: [ClusterSupport!]! availability: [ClusterSupport!]!
} }
+153 -13
View File
@@ -32,7 +32,9 @@ type Config = graphql.Config[ResolverRoot, DirectiveRoot, ComplexityRoot]
type ResolverRoot interface { type ResolverRoot interface {
Cluster() ClusterResolver Cluster() ClusterResolver
GlobalMetricListItem() GlobalMetricListItemResolver
Job() JobResolver Job() JobResolver
MetricConfig() MetricConfigResolver
MetricValue() MetricValueResolver MetricValue() MetricValueResolver
Mutation() MutationResolver Mutation() MutationResolver
Node() NodeResolver Node() NodeResolver
@@ -100,6 +102,7 @@ type ComplexityRoot struct {
Footprint func(childComplexity int) int Footprint func(childComplexity int) int
Name func(childComplexity int) int Name func(childComplexity int) int
Scope func(childComplexity int) int Scope func(childComplexity int) int
Tooltip func(childComplexity int) int
Unit func(childComplexity int) int Unit func(childComplexity int) int
} }
@@ -219,6 +222,7 @@ type ComplexityRoot struct {
Scope func(childComplexity int) int Scope func(childComplexity int) int
SubClusters func(childComplexity int) int SubClusters func(childComplexity int) int
Timestep func(childComplexity int) int Timestep func(childComplexity int) int
Tooltip func(childComplexity int) int
Unit func(childComplexity int) int Unit func(childComplexity int) int
} }
@@ -441,6 +445,9 @@ type ComplexityRoot struct {
type ClusterResolver interface { type ClusterResolver interface {
Partitions(ctx context.Context, obj *schema.Cluster) ([]string, error) Partitions(ctx context.Context, obj *schema.Cluster) ([]string, error)
} }
type GlobalMetricListItemResolver interface {
Tooltip(ctx context.Context, obj *schema.GlobalMetricListItem) (*string, error)
}
type JobResolver interface { type JobResolver interface {
StartTime(ctx context.Context, obj *schema.Job) (*time.Time, error) StartTime(ctx context.Context, obj *schema.Job) (*time.Time, error)
@@ -452,6 +459,9 @@ type JobResolver interface {
MetaData(ctx context.Context, obj *schema.Job) (any, error) MetaData(ctx context.Context, obj *schema.Job) (any, error)
UserData(ctx context.Context, obj *schema.Job) (*model.User, error) UserData(ctx context.Context, obj *schema.Job) (*model.User, error)
} }
type MetricConfigResolver interface {
Tooltip(ctx context.Context, obj *schema.MetricConfig) (*string, error)
}
type MetricValueResolver interface { type MetricValueResolver interface {
Name(ctx context.Context, obj *schema.MetricValue) (*string, error) Name(ctx context.Context, obj *schema.MetricValue) (*string, error)
} }
@@ -690,6 +700,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
} }
return e.ComplexityRoot.GlobalMetricListItem.Scope(childComplexity), true return e.ComplexityRoot.GlobalMetricListItem.Scope(childComplexity), true
case "GlobalMetricListItem.tooltip":
if e.ComplexityRoot.GlobalMetricListItem.Tooltip == nil {
break
}
return e.ComplexityRoot.GlobalMetricListItem.Tooltip(childComplexity), true
case "GlobalMetricListItem.unit": case "GlobalMetricListItem.unit":
if e.ComplexityRoot.GlobalMetricListItem.Unit == nil { if e.ComplexityRoot.GlobalMetricListItem.Unit == nil {
break break
@@ -1217,6 +1233,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
} }
return e.ComplexityRoot.MetricConfig.Timestep(childComplexity), true return e.ComplexityRoot.MetricConfig.Timestep(childComplexity), true
case "MetricConfig.tooltip":
if e.ComplexityRoot.MetricConfig.Tooltip == nil {
break
}
return e.ComplexityRoot.MetricConfig.Tooltip(childComplexity), true
case "MetricConfig.unit": case "MetricConfig.unit":
if e.ComplexityRoot.MetricConfig.Unit == nil { if e.ComplexityRoot.MetricConfig.Unit == nil {
break break
@@ -2418,6 +2440,7 @@ type MetricConfig {
alert: Float! alert: Float!
lowerIsBetter: Boolean lowerIsBetter: Boolean
subClusters: [SubClusterConfig!]! subClusters: [SubClusterConfig!]!
tooltip: String
} }
type Tag { type Tag {
@@ -2575,6 +2598,7 @@ type GlobalMetricListItem {
unit: Unit! unit: Unit!
scope: MetricScope! scope: MetricScope!
footprint: String footprint: String
tooltip: String
availability: [ClusterSupport!]! availability: [ClusterSupport!]!
} }
@@ -2957,6 +2981,8 @@ func (ec *executionContext) childFields_GlobalMetricListItem(ctx context.Context
return ec.fieldContext_GlobalMetricListItem_scope(ctx, field) return ec.fieldContext_GlobalMetricListItem_scope(ctx, field)
case "footprint": case "footprint":
return ec.fieldContext_GlobalMetricListItem_footprint(ctx, field) return ec.fieldContext_GlobalMetricListItem_footprint(ctx, field)
case "tooltip":
return ec.fieldContext_GlobalMetricListItem_tooltip(ctx, field)
case "availability": case "availability":
return ec.fieldContext_GlobalMetricListItem_availability(ctx, field) return ec.fieldContext_GlobalMetricListItem_availability(ctx, field)
} }
@@ -3187,6 +3213,8 @@ func (ec *executionContext) childFields_MetricConfig(ctx context.Context, field
return ec.fieldContext_MetricConfig_lowerIsBetter(ctx, field) return ec.fieldContext_MetricConfig_lowerIsBetter(ctx, field)
case "subClusters": case "subClusters":
return ec.fieldContext_MetricConfig_subClusters(ctx, field) return ec.fieldContext_MetricConfig_subClusters(ctx, field)
case "tooltip":
return ec.fieldContext_MetricConfig_tooltip(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type MetricConfig", field.Name) return nil, fmt.Errorf("no field named %q was found under type MetricConfig", field.Name)
} }
@@ -5159,6 +5187,29 @@ func (ec *executionContext) fieldContext_GlobalMetricListItem_footprint(_ contex
return graphql.NewScalarFieldContext("GlobalMetricListItem", field, false, false, errors.New("field of type String does not have child fields")) return graphql.NewScalarFieldContext("GlobalMetricListItem", field, false, false, errors.New("field of type String does not have child fields"))
} }
func (ec *executionContext) _GlobalMetricListItem_tooltip(ctx context.Context, field graphql.CollectedField, obj *schema.GlobalMetricListItem) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return ec.fieldContext_GlobalMetricListItem_tooltip(ctx, field)
},
func(ctx context.Context) (any, error) {
return ec.Resolvers.GlobalMetricListItem().Tooltip(ctx, obj)
},
nil,
func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler {
return ec.marshalOString2ᚖstring(ctx, selections, v)
},
true,
false,
)
}
func (ec *executionContext) fieldContext_GlobalMetricListItem_tooltip(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
return graphql.NewScalarFieldContext("GlobalMetricListItem", field, true, true, errors.New("field of type String does not have child fields"))
}
func (ec *executionContext) _GlobalMetricListItem_availability(ctx context.Context, field graphql.CollectedField, obj *schema.GlobalMetricListItem) (ret graphql.Marshaler) { func (ec *executionContext) _GlobalMetricListItem_availability(ctx context.Context, field graphql.CollectedField, obj *schema.GlobalMetricListItem) (ret graphql.Marshaler) {
return graphql.ResolveField( return graphql.ResolveField(
ctx, ctx,
@@ -7349,6 +7400,29 @@ func (ec *executionContext) fieldContext_MetricConfig_subClusters(_ context.Cont
return fc, nil return fc, nil
} }
func (ec *executionContext) _MetricConfig_tooltip(ctx context.Context, field graphql.CollectedField, obj *schema.MetricConfig) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return ec.fieldContext_MetricConfig_tooltip(ctx, field)
},
func(ctx context.Context) (any, error) {
return ec.Resolvers.MetricConfig().Tooltip(ctx, obj)
},
nil,
func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler {
return ec.marshalOString2ᚖstring(ctx, selections, v)
},
true,
false,
)
}
func (ec *executionContext) fieldContext_MetricConfig_tooltip(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
return graphql.NewScalarFieldContext("MetricConfig", field, true, true, errors.New("field of type String does not have child fields"))
}
func (ec *executionContext) _MetricFootprints_metric(ctx context.Context, field graphql.CollectedField, obj *model.MetricFootprints) (ret graphql.Marshaler) { func (ec *executionContext) _MetricFootprints_metric(ctx context.Context, field graphql.CollectedField, obj *model.MetricFootprints) (ret graphql.Marshaler) {
return graphql.ResolveField( return graphql.ResolveField(
ctx, ctx,
@@ -13269,24 +13343,57 @@ func (ec *executionContext) _GlobalMetricListItem(ctx context.Context, sel ast.S
case "name": case "name":
out.Values[i] = ec._GlobalMetricListItem_name(ctx, field, obj) out.Values[i] = ec._GlobalMetricListItem_name(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "unit": case "unit":
out.Values[i] = ec._GlobalMetricListItem_unit(ctx, field, obj) out.Values[i] = ec._GlobalMetricListItem_unit(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "scope": case "scope":
out.Values[i] = ec._GlobalMetricListItem_scope(ctx, field, obj) out.Values[i] = ec._GlobalMetricListItem_scope(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "footprint": case "footprint":
out.Values[i] = ec._GlobalMetricListItem_footprint(ctx, field, obj) out.Values[i] = ec._GlobalMetricListItem_footprint(ctx, field, obj)
case "tooltip":
field := field
innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._GlobalMetricListItem_tooltip(ctx, field, obj)
return res
}
if field.Deferrable != nil {
dfs, ok := deferred[field.Deferrable.Label]
di := 0
if ok {
dfs.AddField(field)
di = len(dfs.Values) - 1
} else {
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
deferred[field.Deferrable.Label] = dfs
}
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
return innerFunc(ctx, dfs)
})
// don't run the out.Concurrently() call below
out.Values[i] = graphql.Null
continue
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
case "availability": case "availability":
out.Values[i] = ec._GlobalMetricListItem_availability(ctx, field, obj) out.Values[i] = ec._GlobalMetricListItem_availability(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
@@ -14209,52 +14316,85 @@ func (ec *executionContext) _MetricConfig(ctx context.Context, sel ast.Selection
case "name": case "name":
out.Values[i] = ec._MetricConfig_name(ctx, field, obj) out.Values[i] = ec._MetricConfig_name(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "unit": case "unit":
out.Values[i] = ec._MetricConfig_unit(ctx, field, obj) out.Values[i] = ec._MetricConfig_unit(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "scope": case "scope":
out.Values[i] = ec._MetricConfig_scope(ctx, field, obj) out.Values[i] = ec._MetricConfig_scope(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "aggregation": case "aggregation":
out.Values[i] = ec._MetricConfig_aggregation(ctx, field, obj) out.Values[i] = ec._MetricConfig_aggregation(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "timestep": case "timestep":
out.Values[i] = ec._MetricConfig_timestep(ctx, field, obj) out.Values[i] = ec._MetricConfig_timestep(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "peak": case "peak":
out.Values[i] = ec._MetricConfig_peak(ctx, field, obj) out.Values[i] = ec._MetricConfig_peak(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "normal": case "normal":
out.Values[i] = ec._MetricConfig_normal(ctx, field, obj) out.Values[i] = ec._MetricConfig_normal(ctx, field, obj)
case "caution": case "caution":
out.Values[i] = ec._MetricConfig_caution(ctx, field, obj) out.Values[i] = ec._MetricConfig_caution(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "alert": case "alert":
out.Values[i] = ec._MetricConfig_alert(ctx, field, obj) out.Values[i] = ec._MetricConfig_alert(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "lowerIsBetter": case "lowerIsBetter":
out.Values[i] = ec._MetricConfig_lowerIsBetter(ctx, field, obj) out.Values[i] = ec._MetricConfig_lowerIsBetter(ctx, field, obj)
case "subClusters": case "subClusters":
out.Values[i] = ec._MetricConfig_subClusters(ctx, field, obj) out.Values[i] = ec._MetricConfig_subClusters(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ atomic.AddUint32(&out.Invalids, 1)
} }
case "tooltip":
field := field
innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._MetricConfig_tooltip(ctx, field, obj)
return res
}
if field.Deferrable != nil {
dfs, ok := deferred[field.Deferrable.Label]
di := 0
if ok {
dfs.AddField(field)
di = len(dfs.Values) - 1
} else {
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
deferred[field.Deferrable.Label] = dfs
}
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
return innerFunc(ctx, dfs)
})
// don't run the out.Concurrently() call below
out.Values[i] = graphql.Null
continue
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
+20
View File
@@ -32,6 +32,11 @@ func (r *clusterResolver) Partitions(ctx context.Context, obj *schema.Cluster) (
return r.Repo.Partitions(obj.Name) return r.Repo.Partitions(obj.Name)
} }
// Tooltip is the resolver for the tooltip field.
func (r *globalMetricListItemResolver) Tooltip(ctx context.Context, obj *schema.GlobalMetricListItem) (*string, error) {
return &obj.Tooltip, nil
}
// StartTime is the resolver for the startTime field. // StartTime is the resolver for the startTime field.
func (r *jobResolver) StartTime(ctx context.Context, obj *schema.Job) (*time.Time, error) { func (r *jobResolver) StartTime(ctx context.Context, obj *schema.Job) (*time.Time, error) {
timestamp := time.Unix(obj.StartTime, 0) timestamp := time.Unix(obj.StartTime, 0)
@@ -127,6 +132,11 @@ func (r *jobResolver) UserData(ctx context.Context, obj *schema.Job) (*model.Use
return repository.GetUserRepository().FetchUserInCtx(ctx, obj.User) return repository.GetUserRepository().FetchUserInCtx(ctx, obj.User)
} }
// Tooltip is the resolver for the tooltip field.
func (r *metricConfigResolver) Tooltip(ctx context.Context, obj *schema.MetricConfig) (*string, error) {
return &obj.Tooltip, nil
}
// Name is the resolver for the name field. // Name is the resolver for the name field.
func (r *metricValueResolver) Name(ctx context.Context, obj *schema.MetricValue) (*string, error) { func (r *metricValueResolver) Name(ctx context.Context, obj *schema.MetricValue) (*string, error) {
panic(fmt.Errorf("not implemented: Name - name")) panic(fmt.Errorf("not implemented: Name - name"))
@@ -1054,9 +1064,17 @@ func (r *subClusterResolver) NumberOfNodes(ctx context.Context, obj *schema.SubC
// Cluster returns generated.ClusterResolver implementation. // Cluster returns generated.ClusterResolver implementation.
func (r *Resolver) Cluster() generated.ClusterResolver { return &clusterResolver{r} } func (r *Resolver) Cluster() generated.ClusterResolver { return &clusterResolver{r} }
// GlobalMetricListItem returns generated.GlobalMetricListItemResolver implementation.
func (r *Resolver) GlobalMetricListItem() generated.GlobalMetricListItemResolver {
return &globalMetricListItemResolver{r}
}
// Job returns generated.JobResolver implementation. // Job returns generated.JobResolver implementation.
func (r *Resolver) Job() generated.JobResolver { return &jobResolver{r} } func (r *Resolver) Job() generated.JobResolver { return &jobResolver{r} }
// MetricConfig returns generated.MetricConfigResolver implementation.
func (r *Resolver) MetricConfig() generated.MetricConfigResolver { return &metricConfigResolver{r} }
// MetricValue returns generated.MetricValueResolver implementation. // MetricValue returns generated.MetricValueResolver implementation.
func (r *Resolver) MetricValue() generated.MetricValueResolver { return &metricValueResolver{r} } func (r *Resolver) MetricValue() generated.MetricValueResolver { return &metricValueResolver{r} }
@@ -1073,7 +1091,9 @@ func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
func (r *Resolver) SubCluster() generated.SubClusterResolver { return &subClusterResolver{r} } func (r *Resolver) SubCluster() generated.SubClusterResolver { return &subClusterResolver{r} }
type clusterResolver struct{ *Resolver } type clusterResolver struct{ *Resolver }
type globalMetricListItemResolver struct{ *Resolver }
type jobResolver struct{ *Resolver } type jobResolver struct{ *Resolver }
type metricConfigResolver struct{ *Resolver }
type metricValueResolver struct{ *Resolver } type metricValueResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver } type mutationResolver struct{ *Resolver }
type nodeResolver struct{ *Resolver } type nodeResolver struct{ *Resolver }
+7 -2
View File
@@ -63,8 +63,11 @@ func initClusterConfig() error {
if _, ok := metricLookup[mc.Name]; !ok { if _, ok := metricLookup[mc.Name]; !ok {
metricLookup[mc.Name] = schema.GlobalMetricListItem{ metricLookup[mc.Name] = schema.GlobalMetricListItem{
Name: mc.Name, Scope: mc.Scope, Unit: mc.Unit, Footprint: mc.Footprint, Name: mc.Name, Scope: mc.Scope, Unit: mc.Unit, Footprint: mc.Footprint, Tooltip: mc.Tooltip,
} }
} else if item := metricLookup[mc.Name]; item.Tooltip == "" && mc.Tooltip != "" {
item.Tooltip = mc.Tooltip
metricLookup[mc.Name] = item
} }
availability := schema.ClusterSupport{Cluster: cluster.Name} availability := schema.ClusterSupport{Cluster: cluster.Name}
@@ -139,8 +142,10 @@ func initClusterConfig() error {
userItem, ok := userMetricLookup[mc.Name] userItem, ok := userMetricLookup[mc.Name]
if !ok { if !ok {
userItem = schema.GlobalMetricListItem{ userItem = schema.GlobalMetricListItem{
Name: mc.Name, Scope: mc.Scope, Unit: mc.Unit, Footprint: mc.Footprint, Name: mc.Name, Scope: mc.Scope, Unit: mc.Unit, Footprint: mc.Footprint, Tooltip: mc.Tooltip,
} }
} else if userItem.Tooltip == "" && mc.Tooltip != "" {
userItem.Tooltip = mc.Tooltip
} }
userItem.Availability = append(userItem.Availability, userAvailability) userItem.Availability = append(userItem.Availability, userAvailability)
userMetricLookup[mc.Name] = userItem userMetricLookup[mc.Name] = userItem
+4
View File
@@ -60,6 +60,8 @@
let presetProject = $derived(filterPresets?.project ? filterPresets.project : ""); let presetProject = $derived(filterPresets?.project ? filterPresets.project : "");
let selectedCluster = $derived(filterPresets?.cluster ? filterPresets.cluster : null); let selectedCluster = $derived(filterPresets?.cluster ? filterPresets.cluster : null);
let selectedSubCluster = $derived(filterPresets?.partition ? filterPresets.partition : null); let selectedSubCluster = $derived(filterPresets?.partition ? filterPresets.partition : null);
const maxClusters = $derived($initq?.data?.clusters?.length || 0);
const maxSubClusters = $derived($initq?.data?.clusters?.find((c) => c.name == selectedCluster)?.subClusters?.length || 0);
let metrics = $derived.by(() => { let metrics = $derived.by(() => {
if (thisInit && ccconfig) { if (thisInit && ccconfig) {
if (selectedCluster) { if (selectedCluster) {
@@ -243,6 +245,8 @@
presetMetrics={metrics} presetMetrics={metrics}
cluster={selectedCluster} cluster={selectedCluster}
subCluster={selectedSubCluster} subCluster={selectedSubCluster}
{maxClusters}
{maxSubClusters}
configName="metricConfig_jobListMetrics" configName="metricConfig_jobListMetrics"
footprintSelect footprintSelect
{globalMetrics} {globalMetrics}
+3 -1
View File
@@ -63,8 +63,9 @@
let pendingHostnameFilter = $state(""); let pendingHostnameFilter = $state("");
let isMetricsSelectionOpen = $state(false); let isMetricsSelectionOpen = $state(false);
/* Derived Init Return */ /* Derived Init Returns */
const thisInit = $derived($initq?.data ? true : false); const thisInit = $derived($initq?.data ? true : false);
const maxSubClusters = $derived($initq?.data?.clusters?.find((c) => c.name == cluster)?.subClusters?.length || 0);
/* Derived States */ /* Derived States */
const ccconfig = $derived(thisInit ? getContext("cc-config") : null); const ccconfig = $derived(thisInit ? getContext("cc-config") : null);
@@ -284,6 +285,7 @@
{cluster} {cluster}
{subCluster} {subCluster}
{globalMetrics} {globalMetrics}
maxSubClusters={subCluster? null: maxSubClusters}
configName="nodeList_selectedMetrics" configName="nodeList_selectedMetrics"
applyMetrics={(newMetrics) => applyMetrics={(newMetrics) =>
selectedMetrics = [...newMetrics] selectedMetrics = [...newMetrics]
+4
View File
@@ -90,6 +90,8 @@
const shortDuration = $derived(ccconfig?.jobList_hideShortRunningJobs); const shortDuration = $derived(ccconfig?.jobList_hideShortRunningJobs);
let selectedCluster = $derived(filterPresets?.cluster ? filterPresets.cluster : null); let selectedCluster = $derived(filterPresets?.cluster ? filterPresets.cluster : null);
let selectedSubCluster = $derived(filterPresets?.partition ? filterPresets.partition : null); let selectedSubCluster = $derived(filterPresets?.partition ? filterPresets.partition : null);
const maxClusters = $derived($initq?.data?.clusters?.length || 0);
const maxSubClusters = $derived($initq?.data?.clusters?.find((c) => c.name == selectedCluster)?.subClusters?.length || 0);
let metrics = $derived.by(() => { let metrics = $derived.by(() => {
if (thisInit && ccconfig) { if (thisInit && ccconfig) {
if (selectedCluster) { if (selectedCluster) {
@@ -531,6 +533,8 @@
presetMetrics={metrics} presetMetrics={metrics}
cluster={selectedCluster} cluster={selectedCluster}
subCluster={selectedSubCluster} subCluster={selectedSubCluster}
{maxClusters}
{maxSubClusters}
configName="metricConfig_jobListMetrics" configName="metricConfig_jobListMetrics"
footprintSelect footprintSelect
{globalMetrics} {globalMetrics}
@@ -22,6 +22,8 @@
ModalFooter, ModalFooter,
Button, Button,
ListGroup, ListGroup,
Icon,
Tooltip
} from "@sveltestrap/sveltestrap"; } from "@sveltestrap/sveltestrap";
import { gql, getContextClient, mutationStore } from "@urql/svelte"; import { gql, getContextClient, mutationStore } from "@urql/svelte";
@@ -33,6 +35,8 @@
presetMetrics = [], presetMetrics = [],
cluster = null, cluster = null,
subCluster = null, subCluster = null,
maxClusters = null,
maxSubClusters = null,
footprintSelect = false, footprintSelect = false,
configName, configName,
globalMetrics, globalMetrics,
@@ -86,20 +90,47 @@
return availableMetrics; return availableMetrics;
} }
function printAvailabilityCount(metric, cluster) {
const avail = globalMetrics.find((gm) => gm.name === metric)?.availability
if (avail) {
if (!cluster) {
return `${avail.length} / ${maxClusters} Cluster`
} else {
const subAvail = avail.find((av) => av.cluster === cluster)?.subClusters
if (subAvail) {
return `${subAvail.length} / ${maxSubClusters} SubCluster`
} else {
return `0 / ${maxSubClusters} SubCluster`
}
}
}
return `0 / ${maxClusters} Cluster`
}
function printAvailability(metric, cluster) { function printAvailability(metric, cluster) {
const avail = globalMetrics.find((gm) => gm.name === metric)?.availability const avail = globalMetrics.find((gm) => gm.name === metric)?.availability
if (avail) { if (avail) {
if (!cluster) { if (!cluster) {
return avail.map((av) => av.cluster).join(', ') console.log(metric, avail.map((av) => av.cluster))
return avail.map((av) => av.cluster)
} else { } else {
const subAvail = avail.find((av) => av.cluster === cluster)?.subClusters const subAvail = avail.find((av) => av.cluster === cluster)?.subClusters
if (subAvail) { if (subAvail) {
return subAvail.join(', ') console.log(metric, subAvail)
return subAvail
} else { } else {
return `Not available for ${cluster}` return [`Not available for ${cluster}`]
} }
} }
} }
return [`Not available for ${cluster}`]
}
function printTooltip(metric) {
const toolt = globalMetrics.find((gm) => gm.name === metric)?.tooltip
if (toolt) {
return toolt
}
return "" return ""
} }
@@ -172,7 +203,7 @@
</script> </script>
<Modal {isOpen} toggle={() => (isOpen = !isOpen)}> <Modal {isOpen} toggle={() => (isOpen = !isOpen)}>
<ModalHeader>Configure columns (Metric availability shown)</ModalHeader> <ModalHeader>Configure columns</ModalHeader>
<ModalBody> <ModalBody>
<ListGroup> <ListGroup>
{#if footprintSelect} {#if footprintSelect}
@@ -213,9 +244,34 @@
/> />
{/if} {/if}
{metric} {metric}
<span style="float: right; text-align: justify;"> {#if maxClusters !== null || maxSubClusters !== null}
{printAvailability(metric, cluster)} <span style="float: right;" class="ms-1">
<Button id={`${metric}-avail-info`} outline color="secondary" size="sm" class="ml-2">
<b>{ printAvailabilityCount(metric, cluster) }</b>
</Button>
<Tooltip target={`${metric}-avail-info`} placement="right">
<b>Availability</b>
<ul style="text-align: left; padding-left: 1.0rem; margin-bottom: 0.25rem;">
{#each printAvailability(metric, cluster) as avail}
<li>{avail}</li>
{/each}
</ul>
</Tooltip>
</span> </span>
{/if}
{#if printTooltip(metric) !== ""}
<span style="float: right;">
<Button id={`${metric}-kind-info`} outline color="secondary" size="sm" class="ml-2">
<Icon name="info-square" />
</Button>
<Tooltip target={`${metric}-kind-info`} placement="right">
<b>Information</b>
<p style="text-align: left; margin-bottom: 0.25rem;">
{ printTooltip(metric) }
</p>
</Tooltip>
</span>
{/if}
</li> </li>
{/each} {/each}
</ListGroup> </ListGroup>
+1
View File
@@ -81,6 +81,7 @@ export function init(extraInitQuery = "") {
name name
scope scope
footprint footprint
tooltip
unit { base, prefix } unit { base, prefix }
availability { cluster, subClusters } availability { cluster, subClusters }
} }