package ui import ( "fmt" "sort" "strings" "time" "tower/internal/model" ) // getRollupSamples extracts sample IDs from a rollup issue's evidence. func getRollupSamples(iss model.Issue) []string { samplesStr := iss.Evidence["samples"] if samplesStr == "" { return nil } parts := strings.Split(samplesStr, " | ") result := make([]string, 0, len(parts)) for _, p := range parts { p = strings.TrimSpace(p) if p != "" { result = append(result, p) } } return result } // isRollupIssue checks if an issue is a rollup issue. func isRollupIssue(iss model.Issue) bool { if strings.HasPrefix(iss.ID, "k8s:rollup:") { return true } if iss.Category == model.CategoryKubernetes && strings.Contains(strings.ToLower(iss.Title), "rollup") { return true } return false } func renderIssueDetails(now time.Time, mode AgeMode, iss model.Issue) string { var b strings.Builder fmt.Fprintf(&b, "Title: %s\n", oneLine(iss.Title)) fmt.Fprintf(&b, "Priority: %s Category: %s State: %s\n", iss.Priority, iss.Category, iss.State) fmt.Fprintf(&b, "FirstSeen: %s\n", fmtTime(iss.FirstSeen)) fmt.Fprintf(&b, "LastSeen: %s\n", fmtTime(iss.LastSeen)) fmt.Fprintf(&b, "Age: %s\n", formatAgeWithMode(iss.Age(now), mode)) if strings.TrimSpace(iss.Details) != "" { b.WriteString("\nDetails\n") b.WriteString(indentBlock(strings.TrimSpace(iss.Details), " ")) b.WriteString("\n") } // Show affected issues for rollup issues if isRollupIssue(iss) { samples := getRollupSamples(iss) if len(samples) > 0 { b.WriteString("\nAffected Issues\n") // Show up to 10 samples maxSamples := 10 if len(samples) > maxSamples { samples = samples[:maxSamples] } for _, sample := range samples { fmt.Fprintf(&b, " • %s\n", sample) } } } if len(iss.Evidence) > 0 { b.WriteString("\nEvidence\n") keys := make([]string, 0, len(iss.Evidence)) for k := range iss.Evidence { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { fmt.Fprintf(&b, " %s: %s\n", k, iss.Evidence[k]) } } if strings.TrimSpace(iss.SuggestedFix) != "" { b.WriteString("\nSuggested Fix\n") b.WriteString(indentBlock(strings.TrimSpace(iss.SuggestedFix), " ")) b.WriteString("\n") } return strings.TrimRight(b.String(), "\n") } func fmtTime(t time.Time) string { if t.IsZero() { return "-" } return t.Local().Format("2006-01-02 15:04:05") } func indentBlock(s, prefix string) string { lines := strings.Split(s, "\n") for i := range lines { lines[i] = prefix + lines[i] } return strings.Join(lines, "\n") }