skyrama_tui/internal/adminui/app.go

121 lines
2.8 KiB
Go
Raw Normal View History

2026-06-04 20:51:20 +03:00
package adminui
import (
"strings"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"skyrama-tui/internal/db"
)
var tabNames = []string{"Products", "Countries", "Buildings", "Planes", "Users", "Airports", "Factions"}
// App is the root admin model. It owns the tab bar and routes messages.
type App struct {
tabs []Tab
activeTab int
width int
height int
}
// NewApp constructs the admin app and wires up all tab sub-models.
func NewApp(client *db.AdminClient) App {
return App{
tabs: []Tab{
newProductsTab(client),
newCountriesTab(client),
newBuildingsTab(client),
newPlanesTab(client),
newUsersTab(client),
newAirportsTab(client),
newFactionsTab(client),
},
}
}
func (a App) Init() tea.Cmd {
var cmds []tea.Cmd
for _, t := range a.tabs {
if cmd := t.Init(); cmd != nil {
cmds = append(cmds, cmd)
}
}
return tea.Batch(cmds...)
}
func (a App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch m := msg.(type) {
case tea.WindowSizeMsg:
a.width, a.height = m.Width, m.Height
// Fan out window size to all tabs
for i, t := range a.tabs {
updated, _ := t.Update(msg)
a.tabs[i] = updated.(Tab)
}
return a, nil
case tea.KeyMsg:
activeInForm := a.tabs[a.activeTab].InForm()
// Global quit always works
if m.String() == "ctrl+c" {
return a, tea.Quit
}
// Tab switching only when not in a form
if !activeInForm {
switch m.String() {
case "1", "2", "3", "4", "5", "6", "7":
idx := int(m.String()[0] - '1')
if idx < len(a.tabs) {
a.activeTab = idx
}
return a, nil
case "tab":
a.activeTab = (a.activeTab + 1) % len(a.tabs)
return a, nil
case "shift+tab":
a.activeTab = (a.activeTab - 1 + len(a.tabs)) % len(a.tabs)
return a, nil
}
}
}
// Fan out non-key messages to all tabs; key messages only go to active tab
if _, isKey := msg.(tea.KeyMsg); !isKey {
var cmds []tea.Cmd
for i, t := range a.tabs {
updated, cmd := t.Update(msg)
a.tabs[i] = updated.(Tab)
if cmd != nil {
cmds = append(cmds, cmd)
}
}
return a, tea.Batch(cmds...)
}
updated, cmd := a.tabs[a.activeTab].Update(msg)
a.tabs[a.activeTab] = updated.(Tab)
return a, cmd
}
func (a App) View() string {
// Tab bar
var tabs []string
for i, name := range tabNames {
key := dimSt.Render(string(rune('1'+i))) + " "
if i == a.activeTab {
tabs = append(tabs, lipgloss.NewStyle().Bold(true).
Foreground(lipgloss.Color("#E94560")).
Render("["+key+name+"]"))
} else {
tabs = append(tabs, dimSt.Render(" "+key+name+" "))
}
}
bar := strings.Join(tabs, dimSt.Render("│"))
header := titleSt.Render("✈ SKYRAMA ADMIN") + " " + bar
divider := dimSt.Render(strings.Repeat("─", a.width))
return header + "\n" + divider + "\n" + a.tabs[a.activeTab].View()
}