installation.go RawHistoryFolder
1package models
2
3import (
4 "fmt"
5 "strings"
6 "time"
7
8 "github.com/bnema/archup/internal/application/dto"
9 tea "github.com/charmbracelet/bubbletea"
10 "github.com/charmbracelet/lipgloss"
11)
12
13// InstallationModelImpl implements InstallationModel interface
14type InstallationModelImpl struct {
15 BaseModel
16
17 status *dto.InstallationStatus
18 err string
19 notice string
20 complete bool
21}
22
23// NewInstallationModel creates a new installation model
24func NewInstallationModel() *InstallationModelImpl {
25 return &InstallationModelImpl{
26 status: &dto.InstallationStatus{},
27 err: "",
28 notice: "",
29 complete: false,
30 }
31}
32
33// Init initializes the model
34func (im *InstallationModelImpl) Init() tea.Cmd {
35 return nil
36}
37
38// Update handles messages
39func (im *InstallationModelImpl) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
40 switch msg := msg.(type) {
41 case tea.KeyMsg:
42 if msg.String() == "q" || msg.String() == "ctrl+c" {
43 return im, tea.Quit
44 }
45 }
46 return im, nil
47}
48
49// View renders the installation status or summary
50// Deprecated: Use views.RenderSummary instead
51func (im *InstallationModelImpl) View() string {
52 if im.err != "" {
53 return im.ViewError()
54 }
55
56 if im.complete {
57 return im.renderSummary()
58 }
59
60 return im.renderStatus()
61}
62
63// ViewError renders the error screen
64// Deprecated: Use views.RenderError instead
65func (im *InstallationModelImpl) ViewError() string {
66 var b strings.Builder
67
68 b.WriteString("\n")
69 b.WriteString(lipgloss.NewStyle().
70 Bold(true).
71 Foreground(lipgloss.Color("1")).
72 Render("Installation Failed"))
73 b.WriteString("\n\n")
74
75 b.WriteString(lipgloss.NewStyle().
76 Foreground(lipgloss.Color("1")).
77 Render(im.err))
78 b.WriteString("\n\n")
79
80 b.WriteString(lipgloss.NewStyle().
81 Faint(true).
82 Render("Press 'q' or Ctrl+C to exit"))
83
84 return b.String()
85}
86
87// SetError sets an error message
88func (im *InstallationModelImpl) SetError(message string) {
89 im.err = message
90}
91
92func (im *InstallationModelImpl) SetNotice(message string) {
93 im.notice = message
94}
95
96// SetComplete marks installation as complete
97func (im *InstallationModelImpl) SetComplete() {
98 im.complete = true
99}
100
101// SetStatus sets the installation status
102func (im *InstallationModelImpl) SetStatus(status *dto.InstallationStatus) {
103 im.status = status
104}
105
106// GetError returns the error message if any
107func (im *InstallationModelImpl) GetError() string {
108 return im.err
109}
110
111func (im *InstallationModelImpl) GetNotice() string {
112 return im.notice
113}
114
115// IsComplete returns whether the installation is complete
116func (im *InstallationModelImpl) IsComplete() bool {
117 return im.complete
118}
119
120// GetStatus returns the installation status
121func (im *InstallationModelImpl) GetStatus() *dto.InstallationStatus {
122 return im.status
123}
124
125// Helper methods
126
127func (im *InstallationModelImpl) renderStatus() string {
128 var b strings.Builder
129
130 b.WriteString("\n")
131 b.WriteString(lipgloss.NewStyle().
132 Bold(true).
133 Foreground(lipgloss.Color("12")).
134 Render("Installation Status"))
135 b.WriteString("\n\n")
136
137 // Current phase
138 if im.status.CurrentPhase != "" {
139 b.WriteString("Phase: ")
140 b.WriteString(lipgloss.NewStyle().
141 Foreground(lipgloss.Color("11")).
142 Render(im.status.CurrentPhase))
143 b.WriteString("\n")
144 }
145
146 // Progress
147 if im.status.Progress > 0 {
148 b.WriteString(fmt.Sprintf("Progress: %d%%\n", im.status.Progress))
149 }
150
151 // Status
152 if im.status.State != "" {
153 b.WriteString(fmt.Sprintf("State: %s\n", im.status.State))
154 }
155
156 b.WriteString("\n")
157 b.WriteString(lipgloss.NewStyle().
158 Faint(true).
159 Render("Installation in progress... Press Ctrl+C to cancel"))
160
161 return b.String()
162}
163
164func (im *InstallationModelImpl) renderSummary() string {
165 var b strings.Builder
166
167 b.WriteString("\n")
168 b.WriteString(lipgloss.NewStyle().
169 Bold(true).
170 Foreground(lipgloss.Color("10")).
171 Render("Installation Complete!"))
172 b.WriteString("\n\n")
173
174 // System information
175 if im.status.Hostname != "" {
176 b.WriteString("Hostname: ")
177 b.WriteString(lipgloss.NewStyle().
178 Foreground(lipgloss.Color("11")).
179 Render(im.status.Hostname))
180 b.WriteString("\n")
181 }
182
183 if im.status.Username != "" {
184 b.WriteString("User: ")
185 b.WriteString(lipgloss.NewStyle().
186 Foreground(lipgloss.Color("11")).
187 Render(im.status.Username))
188 b.WriteString("\n")
189 }
190
191 if im.status.TargetDisk != "" {
192 b.WriteString("Target Disk: ")
193 b.WriteString(lipgloss.NewStyle().
194 Foreground(lipgloss.Color("11")).
195 Render(im.status.TargetDisk))
196 b.WriteString("\n")
197 }
198
199 // Duration
200 if im.status.StartedAt != nil && im.status.CompletedAt != nil {
201 duration := im.status.CompletedAt.Sub(*im.status.StartedAt)
202 b.WriteString(fmt.Sprintf("Duration: %s\n", formatDuration(duration)))
203 }
204
205 b.WriteString("\n")
206 b.WriteString(lipgloss.NewStyle().
207 Faint(true).
208 Render("Press 'r' to unmount and reboot, or 'q' to exit"))
209
210 return b.String()
211}
212
213func formatDuration(d time.Duration) string {
214 hours := int(d.Hours())
215 minutes := int(d.Minutes()) % 60
216 seconds := int(d.Seconds()) % 60
217
218 if hours > 0 {
219 return fmt.Sprintf("%dh %dm %ds", hours, minutes, seconds)
220 }
221 if minutes > 0 {
222 return fmt.Sprintf("%dm %ds", minutes, seconds)
223 }
224 return fmt.Sprintf("%ds", seconds)
225}