Skip to content

Commit 8b0eb35

Browse files
committed
feat(browser_lb): added pagination for 7rules details
Signed-off-by: olivier dubo <olivier.dubo@ovhcloud.com>
1 parent a0fdca7 commit 8b0eb35

1 file changed

Lines changed: 82 additions & 88 deletions

File tree

internal/services/browser/manager.go

Lines changed: 82 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ type Model struct {
652652
selectedLBL7Policy map[string]interface{} // Currently selected policy for detail view
653653
lbL7PolicyDetailActionIdx int // Selected action in policy detail (0=Edit, 1=Delete)
654654
lbL7PolicyDetailConfirm bool // Confirm mode in policy detail
655+
lbL7RuleDetailIdx int // Currently displayed rule index in L7 Rules view
655656
// Background detail-view refresh (set by auto-refresh timer, cleared by data handlers)
656657
detailRefreshId string
657658
detailRefreshName string
@@ -2184,6 +2185,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
21842185
case lbL7RuleCreatedMsg:
21852186
m.wizard = WizardData{}
21862187
m.mode = LBL7RulesView
2188+
m.lbL7RuleDetailIdx = 0
21872189
if msg.err != nil {
21882190
m.notification = fmt.Sprintf("❌ Erreur: %s", msg.err.Error())
21892191
m.notificationExpiry = time.Now().Add(8 * time.Second)
@@ -3321,6 +3323,17 @@ func (m Model) renderContentBox(width int) string {
33213323
// Combine title and content
33223324
fullContent := title + "\n\n" + contentStr
33233325

3326+
// Clamp content height so the nav bars and footer are never pushed off screen.
3327+
// ~12 lines are consumed by header, nav bars, spacing and footer outside this box.
3328+
if m.height > 14 {
3329+
maxLines := m.height - 12
3330+
lines := strings.Split(fullContent, "\n")
3331+
if len(lines) > maxLines {
3332+
lines = lines[:maxLines]
3333+
fullContent = strings.Join(lines, "\n")
3334+
}
3335+
}
3336+
33243337
// Level 3 (inTableFocus): highlight content box border in green to show focus is inside the table
33253338
boxStyle := contentBoxStyle
33263339
if m.inTableFocus {
@@ -7179,104 +7192,64 @@ func (m Model) renderLBL7RulesView(width int) string {
71797192
actionsBox := renderBox("Actions (Enter to execute)", createBtn, width-4)
71807193
content.WriteString(actionsBox + "\n\n")
71817194

7182-
var rulesContent strings.Builder
7183-
if len(rules) == 0 {
7184-
rulesContent.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("#666666")).
7185-
Render(" No L7 Rules defined for this policy."))
7186-
} else {
7187-
// Table header
7188-
hType := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Width(18).Render("Type")
7189-
hComp := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Width(16).Render("Comparison")
7190-
hKey := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Width(16).Render("Key")
7191-
hVal := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Width(22).Render("Value")
7192-
hInv := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Width(8).Render("Invert")
7193-
hSt := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Width(14).Render("Status")
7194-
rulesContent.WriteString(" " + hType + hComp + hKey + hVal + hInv + hSt + "\n")
7195-
rulesContent.WriteString(" " + strings.Repeat("─", min(width-10, 92)) + "\n")
7196-
7197-
for _, r := range rules {
7198-
rType := truncate(getStringValue(r, "type", "-"), 16)
7199-
rComp := truncate(getStringValue(r, "compareType", "-"), 14)
7200-
rKey := truncate(getStringValue(r, "key", "-"), 14)
7201-
rVal := truncate(getStringValue(r, "value", "-"), 20)
7202-
invertStr := "No"
7203-
if inv, ok := r["invert"].(bool); ok && inv {
7204-
invertStr = "Yes"
7205-
}
7206-
rStatus := getStringValue(r, "provisioningStatus", getStringValue(r, "operatingStatus", "-"))
7195+
// Summary line: rule count + pagination hint
7196+
titleLine := fmt.Sprintf("L7 Rules (%d) — Policy: %s", len(rules), policyName)
72077197

7208-
statusColor := lipgloss.Color("#00FF7F")
7209-
if strings.ToLower(rStatus) != "active" && strings.ToLower(rStatus) != "online" {
7210-
if strings.ToLower(rStatus) == "error" {
7211-
statusColor = lipgloss.Color("#FF6B6B")
7212-
} else {
7213-
statusColor = lipgloss.Color("#FFD700")
7214-
}
7215-
}
7216-
invertColor := lipgloss.Color("#CCCCCC")
7217-
if invertStr == "Yes" {
7218-
invertColor = lipgloss.Color("#FFD700")
7219-
}
7198+
if len(rules) == 0 {
7199+
empty := lipgloss.NewStyle().Foreground(lipgloss.Color("#666666")).Render(" No L7 Rules defined for this policy.")
7200+
content.WriteString(renderBox(titleLine, empty, width-4))
7201+
return content.String()
7202+
}
72207203

7221-
rulesContent.WriteString(
7222-
" " +
7223-
lipgloss.NewStyle().Foreground(lipgloss.Color("#FFFFFF")).Width(18).Render(rType) +
7224-
lipgloss.NewStyle().Foreground(lipgloss.Color("#CCCCCC")).Width(16).Render(rComp) +
7225-
lipgloss.NewStyle().Foreground(lipgloss.Color("#CCCCCC")).Width(16).Render(rKey) +
7226-
lipgloss.NewStyle().Foreground(lipgloss.Color("#CCCCCC")).Width(22).Render(rVal) +
7227-
lipgloss.NewStyle().Foreground(invertColor).Width(8).Render(invertStr) +
7228-
lipgloss.NewStyle().Foreground(statusColor).Width(14).Render(rStatus) + "\n",
7229-
)
7230-
}
7204+
// Clamp index
7205+
idx := m.lbL7RuleDetailIdx
7206+
if idx < 0 {
7207+
idx = 0
7208+
}
7209+
if idx >= len(rules) {
7210+
idx = len(rules) - 1
72317211
}
72327212

7233-
rulesBox := renderBox(fmt.Sprintf("L7 Rules (%d) — Policy: %s", len(rules), policyName), rulesContent.String(), width-4)
7234-
content.WriteString(rulesBox)
7213+
// Pagination indicator in title
7214+
pagTitle := fmt.Sprintf("%s [◄ %d/%d ►]", titleLine, idx+1, len(rules))
72357215

7236-
// If rules exist, show detail of first rule below (expanded view per rule)
7237-
if len(rules) > 0 {
7238-
content.WriteString("\n")
7239-
for idx, r := range rules {
7240-
rType := getStringValue(r, "type", "N/A")
7241-
rComp := getStringValue(r, "compareType", "N/A")
7242-
rKey := getStringValue(r, "key", "-")
7243-
rVal := getStringValue(r, "value", "-")
7244-
invertStr := "No"
7245-
if inv, ok := r["invert"].(bool); ok && inv {
7246-
invertStr = "Yes"
7247-
}
7248-
rStatus := getStringValue(r, "provisioningStatus", getStringValue(r, "operatingStatus", "N/A"))
7249-
rID := getStringValue(r, "id", "N/A")
7250-
7251-
var detailContent strings.Builder
7252-
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("ID"), valueSt.Render(truncate(rID, 36))))
7253-
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("Type"), valueSt.Render(rType)))
7254-
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("Comparison"), valueSt.Render(rComp)))
7255-
if rKey != "-" {
7256-
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("Key"), valueSt.Render(rKey)))
7257-
}
7258-
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("Value"), valueSt.Render(rVal)))
7259-
invertStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#CCCCCC"))
7260-
if invertStr == "Yes" {
7261-
invertStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFD700"))
7262-
}
7263-
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("Invert"), invertStyle.Render(invertStr)))
7216+
r := rules[idx]
7217+
rType := getStringValue(r, "ruleType", getStringValue(r, "type", "N/A"))
7218+
rComp := getStringValue(r, "compareType", "N/A")
7219+
rKey := getStringValue(r, "key", "-")
7220+
rVal := getStringValue(r, "value", "-")
7221+
rID := getStringValue(r, "id", "N/A")
7222+
invertStr := "No"
7223+
if inv, ok := r["invert"].(bool); ok && inv {
7224+
invertStr = "Yes"
7225+
}
7226+
rStatus := getStringValue(r, "provisioningStatus", getStringValue(r, "operatingStatus", "N/A"))
72647227

7265-
statusColor := lipgloss.Color("#00FF7F")
7266-
if strings.ToLower(rStatus) != "active" && strings.ToLower(rStatus) != "online" {
7267-
statusColor = lipgloss.Color("#FFD700")
7268-
if strings.ToLower(rStatus) == "error" {
7269-
statusColor = lipgloss.Color("#FF6B6B")
7270-
}
7271-
}
7272-
detailContent.WriteString(fmt.Sprintf("%s %s", labelSt.Render("Supply Status"),
7273-
lipgloss.NewStyle().Foreground(statusColor).Render(rStatus)))
7228+
var detailContent strings.Builder
7229+
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("ID"), valueSt.Render(truncate(rID, 36))))
7230+
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("Type"), valueSt.Render(rType)))
7231+
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("Comparison"), valueSt.Render(rComp)))
7232+
if rKey != "-" {
7233+
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("Key"), valueSt.Render(rKey)))
7234+
}
7235+
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("Value"), valueSt.Render(rVal)))
7236+
invertStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#CCCCCC"))
7237+
if invertStr == "Yes" {
7238+
invertStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFD700"))
7239+
}
7240+
detailContent.WriteString(fmt.Sprintf("%s %s\n", labelSt.Render("Invert"), invertStyle.Render(invertStr)))
72747241

7275-
box := renderBox(fmt.Sprintf("Rule #%d", idx+1), detailContent.String(), (width-4)/2)
7276-
content.WriteString(box + "\n")
7242+
statusColor := lipgloss.Color("#00FF7F")
7243+
if strings.ToLower(rStatus) != "active" && strings.ToLower(rStatus) != "online" {
7244+
statusColor = lipgloss.Color("#FFD700")
7245+
if strings.ToLower(rStatus) == "error" {
7246+
statusColor = lipgloss.Color("#FF6B6B")
72777247
}
72787248
}
7249+
detailContent.WriteString(fmt.Sprintf("%s %s", labelSt.Render("Prov. Status"),
7250+
lipgloss.NewStyle().Foreground(statusColor).Render(rStatus)))
72797251

7252+
content.WriteString(renderBox(pagTitle, detailContent.String(), width-4))
72807253
return content.String()
72817254
}
72827255

@@ -7855,6 +7828,8 @@ func (m Model) renderFooter() string {
78557828
help = "Type key name • Enter: Confirm • Esc: Cancel"
78567829
} else if m.wizard.step == LBL7RuleWizardStepValue {
78577830
help = "Type value • Enter: Confirm • Esc: Cancel"
7831+
} else if m.mode == LBL7RulesView {
7832+
help = "←→: Navigate rules • Enter: Create Rule • Esc: Back"
78587833
} else {
78597834
help = "↑↓: Navigate • d: Debug • Enter: Select • ←: Back • Esc: Cancel"
78607835
}
@@ -8027,6 +8002,16 @@ func (m Model) handleKeyPress(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
80278002
}
80288003
return m, nil
80298004
}
8005+
// In LBL7RulesView, paginate backwards through rules
8006+
if m.mode == LBL7RulesView {
8007+
policyID := getStringValue(m.selectedLBL7Policy, "id", "")
8008+
if m.lbL7RuleDetailIdx > 0 {
8009+
m.lbL7RuleDetailIdx--
8010+
} else {
8011+
m.lbL7RuleDetailIdx = len(m.lbL7Rules[policyID]) - 1
8012+
}
8013+
return m, nil
8014+
}
80308015
// In LBListenerDetailView, navigate actions (only when not navigating policies)
80318016
if m.mode == LBListenerDetailView && m.lbL7PolicyListIdx < 0 {
80328017
if m.lbListenerDetailActionIdx > 0 {
@@ -8164,6 +8149,15 @@ func (m Model) handleKeyPress(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
81648149
}
81658150
return m, nil
81668151
}
8152+
// In LBL7RulesView, paginate forward through rules
8153+
if m.mode == LBL7RulesView {
8154+
policyID := getStringValue(m.selectedLBL7Policy, "id", "")
8155+
count := len(m.lbL7Rules[policyID])
8156+
if count > 0 {
8157+
m.lbL7RuleDetailIdx = (m.lbL7RuleDetailIdx + 1) % count
8158+
}
8159+
return m, nil
8160+
}
81678161
// In LBListenerDetailView, navigate actions (0=Edit, 1=Delete, 2=L7 Policies) — only when not navigating policies
81688162
if m.mode == LBListenerDetailView && m.lbL7PolicyListIdx < 0 {
81698163
if m.lbListenerDetailActionIdx < 2 {

0 commit comments

Comments
 (0)