11package appllicationdashboard
22
33import (
4+ "fmt"
5+
46 "github.com/FredrikMWold/radix-tui/applicationDashboard/commands"
7+ contextswitcher "github.com/FredrikMWold/radix-tui/applicationDashboard/contextSwitcher"
58 "github.com/FredrikMWold/radix-tui/styles"
69 tea "github.com/charmbracelet/bubbletea"
710 "github.com/charmbracelet/lipgloss"
@@ -34,9 +37,45 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
3437 m .hasAuthRedirect = false
3538 return m , commands .GetApplications
3639
40+ case contextswitcher.ContextSelected :
41+ // Context was selected, save it and reload
42+ m .focused = m .previousFocused
43+ return m , commands .SetContext (string (msg ))
44+
45+ case contextswitcher.ContextCancelled :
46+ // Context switcher was cancelled, restore previous focus
47+ m .focused = m .previousFocused
48+ return m , nil
49+
50+ case commands.ContextChanged :
51+ // Context changed successfully, reload everything
52+ m .context = string (msg )
53+ m .applications = []string {}
54+ m .application = commands.Application {}
55+ m .isLoadingApplication = false
56+ m .loadError = ""
57+ m .appsLoaded = false
58+ m .focused = application
59+ m .keys = ApplicationTableKeys
60+ // Reinitialize context switcher with new context
61+ m .contextSwitcher = contextswitcher .New (m .context )
62+ return m , tea .Batch (commands .GetApplications , m .applicationsTable .Init ())
63+
3764 case tea.KeyMsg :
65+ // Handle context switch key globally (except when in context switcher)
66+ if msg .String () == "ctrl+s" && m .focused != contextSwitch {
67+ m .previousFocused = m .focused
68+ m .focused = contextSwitch
69+ m .contextSwitcher = contextswitcher .New (m .context )
70+ return m , nil
71+ }
72+
3873 switch msg .String () {
3974 case "ctrl+c" , "q" :
75+ if m .focused == contextSwitch {
76+ m .focused = m .previousFocused
77+ return m , nil
78+ }
4079 return m , tea .Quit
4180 case "ctrl+r" :
4281 if m .application .Name != "" {
@@ -54,6 +93,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
5493 m .keys = BuildDeployFormKeys
5594 }
5695 case "esc" :
96+ if m .focused == contextSwitch {
97+ m .focused = m .previousFocused
98+ return m , nil
99+ }
57100 if m .focused == pipeline {
58101 m .focused = application
59102 m .keys = ApplicationTableKeys
@@ -65,9 +108,30 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
65108 }
66109
67110 case commands.Applications :
68- // Loaded apps → clear any implicit auth-waiting state
69- m .applications = msg
70- m .hasAuthRedirect = false
111+ // Accept applications if:
112+ // 1. Context matches current context, OR
113+ // 2. We don't have a context yet (initial load - accept cache immediately)
114+ if msg .Context == m .context || m .context == "" {
115+ m .applications = msg .Apps
116+ m .hasAuthRedirect = false
117+ m .loadError = ""
118+ m .appsLoaded = true
119+ // If we didn't have context yet, set it from the message
120+ if m .context == "" {
121+ m .context = msg .Context
122+ }
123+ }
124+
125+ case commands.ApplicationsError :
126+ // Only show error if it's for the current context (or no context set yet)
127+ if msg .Context == m .context || m .context == "" {
128+ m .hasAuthRedirect = false
129+ m .loadError = fmt .Sprintf ("Failed to load applications for context '%s':\n %s\n \n Press ctrl+s to switch context or q to quit" , msg .Context , msg .Err .Error ())
130+ m .appsLoaded = true
131+ if m .context == "" {
132+ m .context = msg .Context
133+ }
134+ }
71135
72136 case Context :
73137 m .context = string (msg )
@@ -96,6 +160,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
96160 }
97161
98162 var pageCmd tea.Cmd
163+ if m .focused == contextSwitch {
164+ m .contextSwitcher , pageCmd = m .contextSwitcher .Update (msg )
165+ }
99166 if m .focused == buildAndDeploy {
100167 m .buildAndDeploy , pageCmd = m .buildAndDeploy .Update (msg )
101168 }
@@ -139,7 +206,7 @@ func (m Model) View() string {
139206 )
140207
141208 }
142- if len ( m . applications ) == 0 {
209+ if ! m . appsLoaded {
143210 if m .hasAuthRedirect {
144211 // During redirect, show text without spinner
145212 return lipgloss .NewStyle ().
@@ -149,6 +216,23 @@ func (m Model) View() string {
149216 AlignVertical (lipgloss .Center ).
150217 Render ("Waiting for authentication" )
151218 }
219+ if m .loadError != "" {
220+ // Show error message with option to switch context
221+ errorStyle := lipgloss .NewStyle ().
222+ Foreground (lipgloss .Color ("196" )).
223+ Bold (true )
224+ content := errorStyle .Render (m .loadError )
225+ // If context switcher is open, show it as overlay
226+ if m .focused == contextSwitch {
227+ return m .renderContextSwitcherOverlay (content )
228+ }
229+ return lipgloss .NewStyle ().
230+ Height (m .height ).
231+ Width (m .width ).
232+ AlignHorizontal (lipgloss .Center ).
233+ AlignVertical (lipgloss .Center ).
234+ Render (content )
235+ }
152236 // Otherwise, show normal loading with spinner
153237 return lipgloss .NewStyle ().
154238 Height (m .height ).
@@ -157,7 +241,24 @@ func (m Model) View() string {
157241 AlignVertical (lipgloss .Center ).
158242 Render ("Loading applications " + m .spinner .View ())
159243 }
160- return lipgloss .JoinHorizontal (
244+
245+ // Handle empty apps list (loaded successfully but no apps)
246+ if len (m .applications ) == 0 {
247+ content := fmt .Sprintf ("No applications found in context '%s'\n \n Press ctrl+s to switch context or q to quit" , m .context )
248+ // If context switcher is open, show it as overlay
249+ if m .focused == contextSwitch {
250+ return m .renderContextSwitcherOverlay (content )
251+ }
252+ return lipgloss .NewStyle ().
253+ Height (m .height ).
254+ Width (m .width ).
255+ AlignHorizontal (lipgloss .Center ).
256+ AlignVertical (lipgloss .Center ).
257+ Render (content )
258+ }
259+
260+ // Build the main view
261+ mainView := lipgloss .JoinHorizontal (
161262 lipgloss .Top ,
162263 lipgloss .JoinVertical (
163264 lipgloss .Top ,
@@ -167,4 +268,27 @@ func (m Model) View() string {
167268 ),
168269 m .getActivePageView (),
169270 ) + "\n " + m .help .View (m .keys )
271+
272+ // If context switcher is active, show it as a modal overlay
273+ if m .focused == contextSwitch {
274+ return m .renderContextSwitcherOverlay (mainView )
275+ }
276+
277+ return mainView
278+ }
279+
280+ func (m Model ) renderContextSwitcherOverlay (_ string ) string {
281+ // Create the modal
282+ modal := m .contextSwitcher .View ()
283+
284+ // Create a dimmed background effect by placing the modal on top
285+ return lipgloss .Place (
286+ m .width ,
287+ m .height ,
288+ lipgloss .Center ,
289+ lipgloss .Center ,
290+ modal ,
291+ lipgloss .WithWhitespaceChars (" " ),
292+ lipgloss .WithWhitespaceForeground (lipgloss .Color ("0" )),
293+ )
170294}
0 commit comments