phase 0.3
This commit is contained in:
commit
4a94bfc4e7
52 changed files with 11653 additions and 0 deletions
121
internal/adminui/app.go
Normal file
121
internal/adminui/app.go
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
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()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue