rc
This commit is contained in:
1 parent
e39ad275b5
commit
3d7e3a88f2
14 files changed
+710
-234
No files matched your search
+93
-31
@@ -2,6 +2,7 @@ package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/list"
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
@@ -14,14 +15,18 @@ func (m *Model) refreshGroupsList() {
|
||||
st := m.loadState()
|
||||
items := make([]list.Item, 0, len(st.Groups))
|
||||
for _, g := range st.Groups {
|
||||
desc := fmt.Sprintf("%d хостов", len(g.Hosts))
|
||||
hosts := map[string]bool{}
|
||||
for _, ref := range g.Services {
|
||||
hosts[ref.Host] = true
|
||||
}
|
||||
desc := fmt.Sprintf("%d сервисов на %d хостах", len(g.Services), len(hosts))
|
||||
if g.Description != "" {
|
||||
desc += " — " + g.Description
|
||||
}
|
||||
items = append(items, simpleItem{id: g.Name, title: g.Name, desc: desc})
|
||||
}
|
||||
m.groupsList = newList(items,
|
||||
"Группы [n]новая [e]изменить [enter]хосты группы [d]удалить [esc]назад",
|
||||
"Группы [n]новая [e]изменить [enter]сервисы группы [d]удалить [esc]назад",
|
||||
m.groupsList.Width(), m.groupsList.Height())
|
||||
}
|
||||
|
||||
@@ -73,17 +78,25 @@ func (m Model) updateGroups(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.selectedGroup = sel.id
|
||||
st := m.loadState()
|
||||
g := st.FindGroup(sel.id)
|
||||
m.groupHostsPickSel = map[string]bool{}
|
||||
|
||||
m.groupPickSel = map[string]bool{}
|
||||
if g != nil {
|
||||
for _, hn := range g.Hosts {
|
||||
m.groupHostsPickSel[hn] = true
|
||||
for _, ref := range g.Services {
|
||||
m.groupPickSel[ref.Host+"/"+ref.Service] = true
|
||||
}
|
||||
}
|
||||
m.groupHostsPickOrder = nil
|
||||
m.groupPickRows = nil
|
||||
m.groupPickServices = map[string][]string{}
|
||||
for _, h := range st.Hosts {
|
||||
m.groupHostsPickOrder = append(m.groupHostsPickOrder, h.Name)
|
||||
m.groupPickRows = append(m.groupPickRows, pickRow{isHeader: true, host: h.Name})
|
||||
var svcNames []string
|
||||
for _, svc := range h.Services {
|
||||
m.groupPickRows = append(m.groupPickRows, pickRow{host: h.Name, service: svc.Name})
|
||||
svcNames = append(svcNames, svc.Name)
|
||||
}
|
||||
m.groupPickServices[h.Name] = svcNames
|
||||
}
|
||||
m.groupHostsPickCursor = 0
|
||||
m.groupPickCursor = 0
|
||||
m.screen = scrGroupHostsPick
|
||||
return m, nil
|
||||
case "d":
|
||||
@@ -180,7 +193,18 @@ func (m Model) viewGroupForm() string {
|
||||
return borderStyle.Render(body)
|
||||
}
|
||||
|
||||
// --- pick which catalog hosts belong to the selected group ---
|
||||
// --- pick which catalog services belong to the selected group, shown as a
|
||||
// host -> services tree: a host row toggles all of its services at once,
|
||||
// a service row toggles just that one, so access is granted per-service
|
||||
// (not blanket per-host) while still being quick to pick a whole host. ---
|
||||
|
||||
// pickRow is one line of the host/service picker tree: either a host
|
||||
// header (isHeader, service == "") or a service leaf under that host.
|
||||
type pickRow struct {
|
||||
isHeader bool
|
||||
host string
|
||||
service string
|
||||
}
|
||||
|
||||
func (m Model) updateGroupHostsPick(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if key, ok := msg.(tea.KeyMsg); ok {
|
||||
@@ -190,35 +214,55 @@ func (m Model) updateGroupHostsPick(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.refreshGroupsList()
|
||||
return m, nil
|
||||
case "up", "k":
|
||||
if m.groupHostsPickCursor > 0 {
|
||||
m.groupHostsPickCursor--
|
||||
if m.groupPickCursor > 0 {
|
||||
m.groupPickCursor--
|
||||
}
|
||||
return m, nil
|
||||
case "down", "j":
|
||||
if m.groupHostsPickCursor < len(m.groupHostsPickOrder)-1 {
|
||||
m.groupHostsPickCursor++
|
||||
if m.groupPickCursor < len(m.groupPickRows)-1 {
|
||||
m.groupPickCursor++
|
||||
}
|
||||
return m, nil
|
||||
case " ":
|
||||
if len(m.groupHostsPickOrder) > 0 {
|
||||
h := m.groupHostsPickOrder[m.groupHostsPickCursor]
|
||||
m.groupHostsPickSel[h] = !m.groupHostsPickSel[h]
|
||||
if len(m.groupPickRows) == 0 {
|
||||
return m, nil
|
||||
}
|
||||
row := m.groupPickRows[m.groupPickCursor]
|
||||
if row.isHeader {
|
||||
allSelected := true
|
||||
for _, svc := range m.groupPickServices[row.host] {
|
||||
if !m.groupPickSel[row.host+"/"+svc] {
|
||||
allSelected = false
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, svc := range m.groupPickServices[row.host] {
|
||||
m.groupPickSel[row.host+"/"+svc] = !allSelected
|
||||
}
|
||||
} else {
|
||||
key := row.host + "/" + row.service
|
||||
m.groupPickSel[key] = !m.groupPickSel[key]
|
||||
}
|
||||
return m, nil
|
||||
case "enter":
|
||||
var hosts []string
|
||||
for _, h := range m.groupHostsPickOrder {
|
||||
if m.groupHostsPickSel[h] {
|
||||
hosts = append(hosts, h)
|
||||
var refs []model.ServiceRef
|
||||
for key, on := range m.groupPickSel {
|
||||
if !on {
|
||||
continue
|
||||
}
|
||||
host, svc, found := strings.Cut(key, "/")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, model.ServiceRef{Host: host, Service: svc})
|
||||
}
|
||||
groupName := m.selectedGroup
|
||||
m.mutate("изменён список хостов группы "+groupName, func(st *model.State) error {
|
||||
m.mutate("изменён список сервисов группы "+groupName, func(st *model.State) error {
|
||||
g := st.FindGroup(groupName)
|
||||
if g == nil {
|
||||
return fmt.Errorf("group %s not found", groupName)
|
||||
}
|
||||
g.Hosts = hosts
|
||||
g.Services = refs
|
||||
return resyncGroupUsers(st, groupName)
|
||||
})
|
||||
m.screen = scrGroups
|
||||
@@ -230,21 +274,39 @@ func (m Model) updateGroupHostsPick(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
func (m Model) viewGroupHostsPick() string {
|
||||
body := fmt.Sprintf("Какие хосты каталога входят в группу %q\n\n", m.selectedGroup)
|
||||
if len(m.groupHostsPickOrder) == 0 {
|
||||
body := fmt.Sprintf("Какие сервисы каталога входят в группу %q\n\n", m.selectedGroup)
|
||||
if len(m.groupPickRows) == 0 {
|
||||
body += warnStyle.Render("В каталоге ещё нет хостов — сначала добавьте их в разделе «Хосты и сервисы».") + "\n"
|
||||
}
|
||||
for i, h := range m.groupHostsPickOrder {
|
||||
for i, row := range m.groupPickRows {
|
||||
cursor := " "
|
||||
if i == m.groupHostsPickCursor {
|
||||
if i == m.groupPickCursor {
|
||||
cursor = "> "
|
||||
}
|
||||
check := "[ ]"
|
||||
if m.groupHostsPickSel[h] {
|
||||
check = "[x]"
|
||||
if row.isHeader {
|
||||
svcs := m.groupPickServices[row.host]
|
||||
selected := 0
|
||||
for _, svc := range svcs {
|
||||
if m.groupPickSel[row.host+"/"+svc] {
|
||||
selected++
|
||||
}
|
||||
}
|
||||
check := "[ ]"
|
||||
switch {
|
||||
case len(svcs) > 0 && selected == len(svcs):
|
||||
check = "[x]"
|
||||
case selected > 0:
|
||||
check = "[~]"
|
||||
}
|
||||
body += fmt.Sprintf("%s%s %s\n", cursor, check, row.host)
|
||||
} else {
|
||||
check := "[ ]"
|
||||
if m.groupPickSel[row.host+"/"+row.service] {
|
||||
check = "[x]"
|
||||
}
|
||||
body += fmt.Sprintf("%s %s %s\n", cursor, check, row.service)
|
||||
}
|
||||
body += fmt.Sprintf("%s%s %s\n", cursor, check, h)
|
||||
}
|
||||
body += "\n[space] отметить [enter] сохранить [esc] назад"
|
||||
body += "\n[space] отметить (на хосте — все его сервисы) [enter] сохранить [esc] назад"
|
||||
return borderStyle.Render(body)
|
||||
}
|
||||
Reference in new issue
Block a user