From 6870144f391900a73c0a44e34b4921e730cb2505 Mon Sep 17 00:00:00 2001 From: jb Date: Mon, 9 Mar 2026 11:37:43 -0300 Subject: [PATCH] feat: main flow created --- cmd/main.go | 2 +- go.mod | 2 +- internal/docker/docker.go | 27 +++++ internal/tui/cmds.go | 47 +++++++++ internal/tui/form.go | 186 +++++++++++++++++++++++++++++++++ internal/tui/model.go | 112 ++++++++++++++++++++ {tui => internal/tui}/steps.go | 8 +- internal/tui/styles.go | 56 ++++++++++ internal/tui/update.go | 137 ++++++++++++++++++++++++ internal/tui/view.go | 115 ++++++++++++++++++++ tui/cmds.go | 28 ----- tui/docker.go | 1 - tui/model.go | 25 ----- tui/styles.go | 12 --- tui/update.go | 76 -------------- tui/view.go | 65 ------------ 16 files changed, 689 insertions(+), 210 deletions(-) create mode 100644 internal/docker/docker.go create mode 100644 internal/tui/cmds.go create mode 100644 internal/tui/form.go create mode 100644 internal/tui/model.go rename {tui => internal/tui}/steps.go (63%) create mode 100644 internal/tui/styles.go create mode 100644 internal/tui/update.go create mode 100644 internal/tui/view.go delete mode 100644 tui/cmds.go delete mode 100644 tui/docker.go delete mode 100644 tui/model.go delete mode 100644 tui/styles.go delete mode 100644 tui/update.go delete mode 100644 tui/view.go diff --git a/cmd/main.go b/cmd/main.go index d8cc2de..0bfc5e3 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -5,7 +5,7 @@ import ( "os" tea "charm.land/bubbletea/v2" - "git.davinti.com.br/davinTI/app-dono/tui/tui" + "git.davinti.com.br/davinTI/app-dono/tui/internal/tui" ) func main() { diff --git a/go.mod b/go.mod index f24a8b0..ade2210 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,10 @@ go 1.25.0 require ( charm.land/bubbles/v2 v2.0.0 charm.land/bubbletea/v2 v2.0.1 + charm.land/lipgloss/v2 v2.0.0 ) require ( - charm.land/lipgloss/v2 v2.0.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/charmbracelet/colorprofile v0.4.2 // indirect github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect diff --git a/internal/docker/docker.go b/internal/docker/docker.go new file mode 100644 index 0000000..0cec139 --- /dev/null +++ b/internal/docker/docker.go @@ -0,0 +1,27 @@ +package tui + +import ( + "fmt" + "os/exec" +) + +func RunContainer(image string, name string, port int) error { + + cmd := exec.Command( + "docker", "run", + "-d", + "--name", name, + "-p", fmt.Sprintf("%d:%d", port, port), + image, + ) + + return cmd.Run() +} + +func PullImage(image string) (string, error) { + + cmd := exec.Command("docker", "pull", image) + + out, err := cmd.CombinedOutput() + return string(out), err +} diff --git a/internal/tui/cmds.go b/internal/tui/cmds.go new file mode 100644 index 0000000..f52aa00 --- /dev/null +++ b/internal/tui/cmds.go @@ -0,0 +1,47 @@ +package tui + +import ( + "os/exec" + "time" + + tea "charm.land/bubbletea/v2" +) + +// --- Messages --- + +// Docker installation check +type DockerCheckedMsg struct{ Installed bool } +type DockerInstalledMsg struct{ Err error } +type TickMsg struct{} + +// Image downloading +type ImageDownloadFinishedMsg struct { + Err error +} +type DownloadTickMsg struct{} + +// --- Commands --- + +func CheckDockerCmd() tea.Cmd { + return func() tea.Msg { + _, err := exec.LookPath("docker") + return DockerCheckedMsg{Installed: err == nil} + } +} + +func tickCmd() tea.Cmd { + return tea.Tick(100*time.Millisecond, func(t time.Time) tea.Msg { + return TickMsg{} + }) +} + +func downloadImageCmd() tea.Cmd { + return func() tea.Msg { + + err := docker.PullImage("hub.davinti.com.br:443/app-dono/app-cliente:latest") + + return ImageDownloadFinishedMsg{ + Err: err, + } + } +} diff --git a/internal/tui/form.go b/internal/tui/form.go new file mode 100644 index 0000000..a220a68 --- /dev/null +++ b/internal/tui/form.go @@ -0,0 +1,186 @@ +package tui + +import ( + "fmt" + "strings" + + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" +) + +type FieldType int + +const ( + FieldTypeText FieldType = iota + FieldTypeSelect + FieldTypePassword + FieldTypeNumber +) + +type FormField struct { + Label string + Placeholder string + Default string + Type FieldType + Options []string // for FieldTypeSelect + optionIdx int // for FieldTypeSelect + CharLimit int + input textinput.Model +} + +type FormStep struct { + Title string + Fields []FormField + focus int +} + +func NewFormStep(title string, fields []FormField) FormStep { + f := FormStep{Title: title, Fields: fields} + for i := range f.Fields { + if f.Fields[i].Type == FieldTypeSelect { + for j, opt := range f.Fields[i].Options { + // Apply default option + if opt == f.Fields[i].Default { + f.Fields[i].optionIdx = j + break + } + } + continue + } + + ti := textinput.New() + ti.Placeholder = f.Fields[i].Placeholder + if f.Fields[i].Default != "" { + ti.SetValue(f.Fields[i].Default) + } + + if f.Fields[i].CharLimit > 0 { + ti.CharLimit = f.Fields[i].CharLimit + } + + if f.Fields[i].Type == FieldTypePassword { + ti.EchoMode = textinput.EchoPassword + } + + if i == 0 { + ti.Focus() + } + + f.Fields[i].input = ti + } + return f +} + +// Update +func (f *FormStep) Update(msg tea.Msg) (done bool, cmd tea.Cmd) { + focused := &f.Fields[f.focus] + isSelect := focused.Type == FieldTypeSelect + + switch msg := msg.(type) { + case tea.KeyPressMsg: + switch msg.String() { + // Select specific inputs + case "left", "h": + if isSelect && focused.optionIdx > 0 { + focused.optionIdx-- + } + + case "right", "l": + if isSelect && focused.optionIdx < len(focused.Options)-1 { + focused.optionIdx++ + } + + // Generic inputs + case "tab", "down": + f.blurCurrent() + f.focus = (f.focus + 1) % len(f.Fields) + f.focusCurrent() + + case "shift+tab", "up": + f.blurCurrent() + f.focus = (f.focus - 1 + len(f.Fields)) % len(f.Fields) + f.focusCurrent() + + case "enter": + if f.focus == len(f.Fields)-1 { + return true, nil + } + f.blurCurrent() + f.focus++ + f.focusCurrent() + } + } + + var c tea.Cmd + f.Fields[f.focus].input, c = f.Fields[f.focus].input.Update(msg) + return false, c +} + +func (f *FormStep) Values() map[string]string { + out := make(map[string]string) + for _, field := range f.Fields { + if field.Type == FieldTypeSelect { + out[field.Label] = field.Options[field.optionIdx] + } else { + out[field.Label] = field.input.Value() + } + } + return out +} + +// View +func (f FormStep) View() string { + pad := strings.Repeat(" ", padding) + var sb strings.Builder + + sb.WriteString(pad + TitleStyle.Render(f.Title) + "") + + for i, field := range f.Fields { + sb.WriteString(renderField(field, i == f.focus)) + } + + return sb.String() +} + +func renderField(field FormField, focused bool) string { + pad := strings.Repeat(" ", padding) + + label := " " + field.Label + if focused { + label = CursorStyle.Render("> " + field.Label) + } + + var value string + switch field.Type { + case FieldTypeSelect: + var opts []string + for i, opt := range field.Options { + if i == field.optionIdx { + opts = append(opts, SelectedStyle.Render("[ "+opt+" ]")) + } else { + opts = append(opts, DimStyle.Render(" "+opt+" ")) + } + } + value = strings.Join(opts, " ") + if focused { + value += HelpStyle.Render(" ← →") + } + default: + value = field.input.View() + } + + return fmt.Sprintf("\n\n%s%s\n%s %s", pad, label, pad, value) +} + +// Helpers +func (f *FormStep) blurCurrent() { + if f.Fields[f.focus].Type != FieldTypeSelect { + f.Fields[f.focus].input.Blur() + } +} + +func (f *FormStep) focusCurrent() { + if f.Fields[f.focus].Type != FieldTypeSelect { + f.Fields[f.focus].input.Focus() + } +} diff --git a/internal/tui/model.go b/internal/tui/model.go new file mode 100644 index 0000000..3f27844 --- /dev/null +++ b/internal/tui/model.go @@ -0,0 +1,112 @@ +package tui + +import ( + tea "charm.land/bubbletea/v2" +) + +type Model struct { + currentStep step + cursor int + + dockerInstalled bool + checkDockerDone bool + checkProgress float64 + isPublicIP bool + + serverForm FormStep + dbForm FormStep + certForm FormStep + configValues ConfigValues + + err error +} + +type ConfigValues struct { + Server map[string]string + Database map[string]string + Cert map[string]string +} + +func InitialModel() Model { + return Model{ + currentStep: StepCheckDocker, + serverForm: NewFormStep("Servidor", []FormField{ + { + Label: "Porta", + Placeholder: "8081", + Default: "8081", + Type: FieldTypeNumber, + CharLimit: 4, + }, + { + Label: "Timeout (Segundos)", + Placeholder: "30", + Default: "30", + Type: FieldTypeNumber, + CharLimit: 3, + }, + { + Label: "Ambiente", + Default: "production", + Type: FieldTypeSelect, + Options: []string{"development", "production"}, + }, + }), + dbForm: NewFormStep("Banco de Dados", []FormField{ + { + Label: "Tipo do Banco", + Default: "postgres", + Type: FieldTypeSelect, + Options: []string{"postgres", "oracle"}, + }, + { + Label: "URL de acesso", + Placeholder: "postgres://usuario:senha@banco:5432/app_dono_db", + Default: "postgres://usuario:senha@banco:5432/app_dono_db", + Type: FieldTypeText, + }, + { + Label: "Conexões ativas (máximo)", + Placeholder: "10", + Default: "10", + Type: FieldTypeNumber, + }, + { + Label: "Conexões ativas (mínimo)", + Placeholder: "2", + Default: "2", + Type: FieldTypeNumber, + }, + }), + certForm: NewFormStep("Certificado", []FormField{ + { + Label: "Caminho para o arquivo do certificado", + Placeholder: "/caminho/para/certificado.crt", + Default: "/caminho/para/certificado.crt", + Type: FieldTypeText, + }, + { + Label: "Caminho para o arquivo da chave", + Placeholder: "/caminho/para/chave.key", + Default: "/caminho/para/chave.key", + Type: FieldTypeText, + }, + { + Label: "Caminho para o arquivo da autoridade certificadora", + Placeholder: "/caminho/para/chaveCA.crt", + Default: "/caminho/para/chaveCA.crt", + Type: FieldTypeText, + }, + { + Label: "Nome do servidor", + Placeholder: "client", + Default: "client", + Type: FieldTypeText, + }, + }), + } +} + +func (m Model) Init() tea.Cmd { + return tea.Batch(CheckDockerCmd(), tickCmd()) +} diff --git a/tui/steps.go b/internal/tui/steps.go similarity index 63% rename from tui/steps.go rename to internal/tui/steps.go index 274e11e..b2c4242 100644 --- a/tui/steps.go +++ b/internal/tui/steps.go @@ -5,8 +5,14 @@ type step int const ( StepCheckDocker step = iota StepDockerInstall + StepDownloadImage + StepIPQuestion StepInstallWireguard - StepTextInputs + + StepServerConfig + StepDatabaseConfig + StepCertConfig + StepDone ) diff --git a/internal/tui/styles.go b/internal/tui/styles.go new file mode 100644 index 0000000..a326280 --- /dev/null +++ b/internal/tui/styles.go @@ -0,0 +1,56 @@ +package tui + +import "charm.land/lipgloss/v2" + +const padding = 2 + +const ( + primary = "#005F87" // Deep Sea Blue + secondary = "#00D7FF" // Cyan/Data highlight + success = "#06D6A0" // Emerald Green + warning = "#FFD166" // Soft Gold + danger = "#EF476F" // Muted Crimson + neutral = "#8E9AAF" // Slate Gray + darkGray = "#353B48" +) + +var ( + // Core Text Styles + TitleStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color(primary)). + Bold(true). + MarginBottom(1) + + CursorStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color(secondary)) + + HelpStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#626262")) + + // Status Styles + ErrorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(danger)) + InfoStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(primary)) + WarnStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(warning)) + + // Form & Selection + SelectedStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color(secondary)). + Bold(true). + PaddingLeft(1) + + DimStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#4A4A4A")) + + // Data Components (Progress / Charts) + ProgressFillStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color(primary)) + + ProgressEmptyStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color(darkGray)) + + // Table Styles + HeaderStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color(secondary)). + Bold(true). + Border(lipgloss.NormalBorder(), false, false, true) +) diff --git a/internal/tui/update.go b/internal/tui/update.go new file mode 100644 index 0000000..1a8ba5c --- /dev/null +++ b/internal/tui/update.go @@ -0,0 +1,137 @@ +package tui + +import ( + "math/rand" + + tea "charm.land/bubbletea/v2" +) + +func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if key, ok := msg.(tea.KeyPressMsg); ok { + switch key.String() { + case "ctrl+c": + return m, tea.Quit + } + } + + switch m.currentStep { + case StepCheckDocker: + return m.updateCheckDocker(msg) + case StepDockerInstall: + return m.updateDockerInstall(msg) + case StepDownloadImage: + return m.updateDownloadImage(msg) + case StepIPQuestion: + return m.updateIPQuestion(msg) + //case StepInstallWireguard + // return m.updateInstallWireguard(msg) + case StepServerConfig: + done, cmd := m.serverForm.Update(msg) + + if done { + m.currentStep = StepDatabaseConfig + } + + return m, cmd + case StepDatabaseConfig: + done, cmd := m.dbForm.Update(msg) + + if done { + m.currentStep = StepCertConfig + } + + return m, cmd + case StepCertConfig: + done, cmd := m.certForm.Update(msg) + + if done { + m.currentStep = StepDone + } + + return m, cmd + case StepDone: + return m, tea.Quit + } + + return m, nil +} + +func (m Model) updateCheckDocker(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case DockerCheckedMsg: + m.dockerInstalled = msg.Installed + m.checkDockerDone = true + + case TickMsg: + if m.checkProgress < 1.0 { + m.checkProgress += 0.15 + (rand.Float64()*0.15 - 0.15) + if m.checkProgress > 1.0 { + m.checkProgress = 1.0 + } + return m, tickCmd() + } + case tea.KeyPressMsg: + if m.checkDockerDone { + if m.dockerInstalled { + m.currentStep = StepIPQuestion + } else { + m.currentStep = StepDockerInstall + } + } + } + + return m, nil +} + +func (m Model) updateDockerInstall(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyPressMsg: + return m, tea.Quit + case DockerInstalledMsg: + if msg.Err != nil { + m.err = msg.Err + } else { + m.currentStep = StepIPQuestion + } + } + + return m, nil +} + +func (m Model) updateIPQuestion(msg tea.Msg) (tea.Model, tea.Cmd) { + numOptions := 2 + + switch msg := msg.(type) { + case tea.KeyPressMsg: + switch msg.String() { + case "up", "k": + if m.cursor > 0 { + m.cursor-- + } + case "down", "j": + if m.cursor < numOptions-1 { + m.cursor++ + } + case "enter": + // Yes + if m.cursor == 0 { + m.currentStep = StepServerConfig + return m, nil + } + + m.currentStep = StepInstallWireguard + return m, nil + } + } + + return m, nil +} + +func (m Model) updateWaitForProgramQuit(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg.(type) { + case tea.KeyPressMsg: + return m, tea.Quit + } + + return m, nil +} diff --git a/internal/tui/view.go b/internal/tui/view.go new file mode 100644 index 0000000..a3fc738 --- /dev/null +++ b/internal/tui/view.go @@ -0,0 +1,115 @@ +package tui + +import ( + "fmt" + "strings" + + tea "charm.land/bubbletea/v2" +) + +const ( + header = "App do Dono - Instalador Cliente" +) + +func (m Model) View() tea.View { + pad := strings.Repeat(" ", padding) + var body string + helpMsg := "ctrl+c: sair" + + switch m.currentStep { + case StepCheckDocker: + body = m.viewCheckDocker() + case StepDockerInstall: + body = m.viewDockerInstall() + helpMsg = "qualquer tecla: sair" + case StepIPQuestion: + body = m.viewIPQuestion() + case StepServerConfig: + body = m.serverForm.View() + helpMsg = "tab: próximo campo • enter: confirmar • ctrl+c: sair" + case StepDatabaseConfig: + body = m.dbForm.View() + helpMsg = "tab: próximo campo • enter: confirmar • ctrl+c: sair" + case StepCertConfig: + body = m.certForm.View() + helpMsg = "tab: próximo campo • enter: confirmar • ctrl+c: sair" + } + + help := HelpStyle.Render(helpMsg) + + v := tea.NewView(fmt.Sprintf("\n%s%s\n\n%s\n\n%s%s\n", + pad, TitleStyle.Render(header), + body, + pad, help, + )) + v.AltScreen = true + + return v +} + +func (m Model) viewCheckDocker() string { + pad := strings.Repeat(" ", padding) + + barWidth := 40 + filled := int(m.checkProgress * float64(barWidth)) + empty := barWidth - filled + + bar := ProgressFillStyle.Render(strings.Repeat("█", filled)) + + ProgressEmptyStyle.Render(strings.Repeat("░", empty)) + + percent := fmt.Sprintf(" %d%%", int(m.checkProgress*100)) + + var sb strings.Builder + sb.WriteString(pad + "Avaliando instalação do Docker...\n\n") + sb.WriteString(pad + bar + HelpStyle.Render(percent)) + + if m.checkDockerDone && m.checkProgress == 1 { + found := "" + + if m.dockerInstalled { + found = "encontrado" + } else { + found = "não encontrado" + } + + sb.WriteString("\n\n" + pad + fmt.Sprintf("Docker %s na máquina. Pressione qualquer tecla para seguir.", found)) + } + + return sb.String() +} + +func (m Model) viewDockerInstall() string { + pad := strings.Repeat(" ", padding) + + var sb strings.Builder + sb.WriteString(pad + "Nenhuma versão do Docker encontrada no sistema.\n") + sb.WriteString(pad + "Instale o Docker manualmente e execute este instalador novamente para concluir a configuração.") + + return sb.String() +} + +func (m Model) viewIPQuestion() string { + pad := strings.Repeat(" ", padding) + + var options = []string{"Sim", "Não"} + + var sb strings.Builder + + sb.WriteString(pad + "Existe IP público disponível para a máquina?\n") + + for i, opt := range options { + cursor := " " + text := "" + isSelected := m.cursor == i + + if isSelected { + cursor = CursorStyle.Render("> ") + text = CursorStyle.Render(opt) + } else { + text = opt + } + sb.WriteString("\n" + pad + cursor + text) + } + + return sb.String() +} diff --git a/tui/cmds.go b/tui/cmds.go deleted file mode 100644 index 5fb4c74..0000000 --- a/tui/cmds.go +++ /dev/null @@ -1,28 +0,0 @@ -package tui - -import ( - "os/exec" - - tea "charm.land/bubbletea/v2" -) - -// --- Messages --- - -type DockerCheckedMsg struct{ Installed bool } -type DockerInstalledMsg struct{ Err error } - -// --- Commands --- - -func CheckDockerCmd() tea.Cmd { - return func() tea.Msg { - _, err := exec.LookPath("docker") - return DockerCheckedMsg{Installed: err == nil} - } -} - -func InstallDockerCmd() tea.Cmd { - return func() tea.Msg { - err := exec.Command("sh", "-c", "curl -fsSL https://get.docker.com | sh").Run() - return DockerInstalledMsg{Err: err} - } -} diff --git a/tui/docker.go b/tui/docker.go deleted file mode 100644 index 89aa0ee..0000000 --- a/tui/docker.go +++ /dev/null @@ -1 +0,0 @@ -package tui diff --git a/tui/model.go b/tui/model.go deleted file mode 100644 index 83874e1..0000000 --- a/tui/model.go +++ /dev/null @@ -1,25 +0,0 @@ -package tui - -import ( - "charm.land/bubbles/v2/textinput" - tea "charm.land/bubbletea/v2" -) - -type Model struct { - currentStep step - dockerInstalled bool - isPublicIP bool - inputs []textinput.Model - cursor int - err error -} - -func InitialModel() Model { - return Model{ - currentStep: StepCheckDocker, - } -} - -func (m Model) Init() tea.Cmd { - return CheckDockerCmd() -} diff --git a/tui/styles.go b/tui/styles.go deleted file mode 100644 index 570c259..0000000 --- a/tui/styles.go +++ /dev/null @@ -1,12 +0,0 @@ -package tui - -import "charm.land/lipgloss/v2" - -const padding = 2 - -var ( - TitleStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#FAFAFA")) - HelpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")) - CursorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FF75B7")) - ErrorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FF5555")) -) diff --git a/tui/update.go b/tui/update.go deleted file mode 100644 index 403f80c..0000000 --- a/tui/update.go +++ /dev/null @@ -1,76 +0,0 @@ -package tui - -import tea "charm.land/bubbletea/v2" - -func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - // Global keys first — always handled regardless of step - if key, ok := msg.(tea.KeyPressMsg); ok { - switch key.String() { - case "ctrl+c", "q": - return m, tea.Quit - } - } - - switch m.currentStep { - case StepCheckDocker: - return m.updateCheckDocker(msg) - case StepDockerInstall: - return m.updateDockerInstall(msg) - case StepIPQuestion: - return m.updateIPQuestion(msg) - case StepDone: - return m, tea.Quit - } - - return m, nil -} - -func (m Model) updateCheckDocker(msg tea.Msg) (tea.Model, tea.Cmd) { - if msg, ok := msg.(DockerCheckedMsg); ok { - m.dockerInstalled = msg.Installed - if msg.Installed { - m.currentStep = StepIPQuestion - } else { - m.currentStep = StepDockerInstall - m.cursor = 0 // reset cursor on enter - } - } - return m, nil -} - -func (m Model) updateDockerInstall(msg tea.Msg) (tea.Model, tea.Cmd) { - const numOptions = 2 - - switch msg := msg.(type) { - case tea.KeyPressMsg: - switch msg.String() { - case "up", "k": - if m.cursor > 0 { - m.cursor-- - } - case "down", "j": - if m.cursor < numOptions-1 { - m.cursor++ - } - case "enter": - switch m.cursor { - case 0: - return m, InstallDockerCmd() - case 1: - return m, tea.Quit - } - } - case DockerInstalledMsg: - if msg.Err != nil { - m.err = msg.Err - } else { - m.currentStep = StepIPQuestion - } - } - - return m, nil -} - -func (m Model) updateIPQuestion(msg tea.Msg) (tea.Model, tea.Cmd) { - return m, nil -} diff --git a/tui/view.go b/tui/view.go deleted file mode 100644 index 2cb1caa..0000000 --- a/tui/view.go +++ /dev/null @@ -1,65 +0,0 @@ -package tui - -import ( - "fmt" - "strings" - - tea "charm.land/bubbletea/v2" -) - -const header = "App do Dono - Instalador Cliente" - -func (m Model) View() tea.View { - pad := strings.Repeat(" ", padding) - var body string - - switch m.currentStep { - case StepCheckDocker: - body = m.viewCheckDocker() - case StepDockerInstall: - body = m.viewDockerInstall() - case StepIPQuestion: - body = m.viewIPQuestion() - } - - help := HelpStyle.Render("Pressione q ou ctrl+c para sair") - - return tea.NewView(fmt.Sprintf("\n%s%s\n\n%s\n\n%s%s\n", - pad, TitleStyle.Render(header), - body, - pad, help, - )) -} - -func (m Model) viewCheckDocker() string { - pad := strings.Repeat(" ", padding) - return pad + "Avaliando instalação do Docker. Aguarde..." -} - -func (m Model) viewDockerInstall() string { - pad := strings.Repeat(" ", padding) - options := []string{"Instalar automaticamente", "Instalar manualmente"} - - var sb strings.Builder - sb.WriteString(pad + "Nenhuma versão do Docker encontrada.\n") - sb.WriteString(pad + "Deseja:\n\n") - - for i, opt := range options { - cursor := " " - if m.cursor == i { - cursor = CursorStyle.Render("> ") - } - sb.WriteString(pad + cursor + opt + "\n") - } - - if m.err != nil { - sb.WriteString("\n" + pad + ErrorStyle.Render("Erro: "+m.err.Error())) - } - - return sb.String() -} - -func (m Model) viewIPQuestion() string { - pad := strings.Repeat(" ", padding) - return pad + "Existe IP público disponível para a máquina?" -}