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() }