package tui import ( "fmt" "strings" tea "charm.land/bubbletea/v2" "charm.land/lipgloss/v2" ) const ( header = "App do Dono - Instalador Cliente" defaultMsg = "ctrl+c: sair" anyKeyOutMsg = "qualquer tecla: sair" formMsg = "tab: próximo campo • enter: confirmar • ctrl+c: sair" ) func (m Model) View() tea.View { pad := strings.Repeat(" ", padding) var body string helpMsg := defaultMsg switch m.currentStep { // Docker stuff case StepCheckDocker: body = m.viewCheckDocker() case StepDockerInstall: body = m.viewDockerInstall() helpMsg = anyKeyOutMsg case StepDockerLogin: body = m.loginForm.View() helpMsg = formMsg case StepDownloadImage: body = m.viewDownloadImage() // IP Stuff case StepIPQuestion: body = m.viewIPQuestion() case StepWireguardConfig: body = m.wireguardForm.View() helpMsg = formMsg case StepGenerateWireguardFile: body = m.viewGenerateFile() if m.finishedFile && m.configFileError != nil { helpMsg = anyKeyOutMsg } case StepDownloadWireguard: body = m.viewDownloadImage() case StepRunWireguard: body = m.viewDockerRun() if m.finishedDockerRun && m.dockerRunError != nil { helpMsg = anyKeyOutMsg } // App Config Stuff case StepServerConfig: body = m.serverForm.View() helpMsg = formMsg case StepDatabaseConfig: body = m.dbForm.View() helpMsg = formMsg case StepCertConfig: body = m.certForm.View() helpMsg = formMsg // Finalize case StepGenerateFile: body = m.viewGenerateFile() if m.finishedFile && m.configFileError != nil { helpMsg = anyKeyOutMsg } case StepRunDocker: body = m.viewDockerRun() if m.finishedDockerRun && m.dockerRunError != nil { helpMsg = anyKeyOutMsg } case StepDone: body = m.viewDoneMessage() helpMsg = anyKeyOutMsg } 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) viewDownloadImage() string { pad := strings.Repeat(" ", padding) var sb strings.Builder if !m.downloadDone { sb.WriteString(pad + fmt.Sprintf("%s Baixando imagem do container...\n\n", m.spinner.View())) return sb.String() } if m.downloadError == nil { sb.WriteString(pad + "Instalação concluída com êxito.\n\n") sb.WriteString(pad + "Pressione qualquer tecla para continuar.") } else { sb.WriteString(pad + "Instalação finalizada com erros.\n\n") dualPad := pad + pad errText := dualPad + m.downloadMessage + "\n" + dualPad + m.downloadError.Error() sb.WriteString(ErrorStyle.Width(m.width - (padding * 2)).Align(lipgloss.Left).Render(errText)) sb.WriteString("\n\n" + pad + "Pressione qualquer tecla para sair.") } 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() } func (m Model) viewGenerateFile() string { pad := strings.Repeat(" ", padding) var sb strings.Builder if !m.finishedFile { sb.WriteString(pad + m.spinner.View() + "Gerando arquivo. Aguarde...") } else { sb.WriteString(pad + "Geração do arquivo finalizada.\n\n") if m.configFileError == nil { sb.WriteString(pad + "Arquivo gerado com sucesso. Pressione qualquer tecla para seguir.") } else { sb.WriteString(pad + "Ocorreu um erro ao gerar o arquivo.\n\n") dualPad := pad + pad errText := dualPad + m.configFileError.Error() sb.WriteString(ErrorStyle.Width(m.width - (padding * 2)).Align(lipgloss.Left).Render(errText)) sb.WriteString("\n\n" + pad + "Tente novamente.") } } return sb.String() } func (m Model) viewDockerRun() string { pad := strings.Repeat(" ", padding) var sb strings.Builder if !m.finishedDockerRun { sb.WriteString(pad + m.spinner.View() + "Tentando iniciar o container Docker. Aguarde...") } else { sb.WriteString(pad + "Inicialização finalizada.\n\n") if m.dockerRunError == nil { sb.WriteString(pad + "Container Docker iniciado com sucesso. Pressione qualquer tecla para seguir.") } else { sb.WriteString(pad + "Ocorreu um erro ao iniciar o container.\n\n") dualPad := pad + pad errText := dualPad + m.dockerRunError.Error() sb.WriteString(ErrorStyle.Width(m.width - (padding * 2)).Align(lipgloss.Left).Render(errText)) sb.WriteString("\n\n" + pad + "Tente novamente.") } } return sb.String() } func (m Model) viewDoneMessage() string { pad := strings.Repeat(" ", padding) var sb strings.Builder sb.WriteString(pad + "Instalação realizada com sucesso!") return sb.String() }