diff --git a/internal/api/ui.go b/internal/api/ui.go index 0cb080b..e808e17 100644 --- a/internal/api/ui.go +++ b/internal/api/ui.go @@ -2679,6 +2679,36 @@ func (h uiHandlers) handleComponentModels(w http.ResponseWriter, r *http.Request sort.Slice(vendorChartList, func(i, j int) bool { return vendorChartList[i].Value > vendorChartList[j].Value }) + // Group small vendors: keep top 14, collapse the rest (and any <1%) into "Others" + const maxVendorSlices = 15 + if len(vendorChartList) > 0 { + grandTotal := 0 + for _, e := range vendorChartList { + grandTotal += e.Value + } + cutoff := maxVendorSlices - 1 // reserve last slot for "Others" + othersValue := 0 + keep := vendorChartList + if len(vendorChartList) > cutoff { + for _, e := range vendorChartList[cutoff:] { + othersValue += e.Value + } + keep = vendorChartList[:cutoff] + } + // Also absorb entries <1% from the kept list into Others + filtered := keep[:0] + for _, e := range keep { + if grandTotal > 0 && float64(e.Value)/float64(grandTotal) < 0.01 { + othersValue += e.Value + } else { + filtered = append(filtered, e) + } + } + if othersValue > 0 { + filtered = append(filtered, vendorChartEntry{Label: "Others", Value: othersValue}) + } + vendorChartList = filtered + } vendorChartJSON, _ := json.Marshal(vendorChartList) page := parsePage(r.URL.Query(), "page") diff --git a/internal/history/service.go b/internal/history/service.go index e6f52d7..953099b 100644 --- a/internal/history/service.go +++ b/internal/history/service.go @@ -1062,7 +1062,7 @@ func validatePatchOps(entityType, changeType, sourceType string, ops []PatchOp) if entityType == "asset" && sourceType == "user" && (strings.HasPrefix(op.Path, "/inventory/") || strings.HasPrefix(op.Path, "/metadata/")) { return fmt.Errorf("%w: path %q is server-only", ErrInvalidPatch, op.Path) } - if entityType == "component" && sourceType == "user" && strings.HasPrefix(op.Path, "/installation/") && op.Path != "/installation/slot_name" { + if entityType == "component" && sourceType == "user" && changeType != "COMPONENT_REMOVED" && strings.HasPrefix(op.Path, "/installation/") && op.Path != "/installation/slot_name" { return fmt.Errorf("%w: path %q is server-only", ErrInvalidPatch, op.Path) } if entityType == "component" && sourceType == "user" && op.Path == "/metadata/component_type" {