@@ -11,12 +11,14 @@ import (
1111 "time"
1212
1313 "github.com/charmbracelet/bubbles/help"
14+ "github.com/charmbracelet/bubbles/table"
1415 tea "github.com/charmbracelet/bubbletea"
1516 "github.com/charmbracelet/lipgloss"
1617 "github.com/toeirei/keymaster/client"
1718 "github.com/toeirei/keymaster/core"
1819 "github.com/toeirei/keymaster/core/model"
1920 "github.com/toeirei/keymaster/i18n"
21+ "github.com/toeirei/keymaster/ui/tui/helpers/tablecontroll"
2022 "github.com/toeirei/keymaster/ui/tui/util"
2123 "github.com/toeirei/keymaster/uiadapters"
2224 "github.com/toeirei/keymaster/util/slicest"
@@ -38,6 +40,12 @@ type Data = struct {
3840// copied from keymaster core for now
3941type AuditLogEntry = model.AuditLogEntry
4042
43+ type recentActivityRow struct {
44+ Timestamp string
45+ Action string
46+ Details string
47+ }
48+
4149type Model struct {
4250 data Data
4351 err error
@@ -99,6 +107,8 @@ func (m Model) View() string {
99107 height = 24
100108 }
101109
110+ recentActivityRows := recentActivityTableRows (m .data .RecentLogs )
111+
102112 lines := []string {
103113 sectionTitleStyle .Render (i18n .T ("dashboard.system_status" )),
104114 "" ,
@@ -119,17 +129,31 @@ func (m Model) View() string {
119129 "" ,
120130 }
121131
122- if len (m . data . RecentLogs ) == 0 {
132+ if len (recentActivityRows ) == 0 {
123133 lines = append (lines , bodyStyle .Italic (true ).Render (i18n .T ("dashboard.no_recent_activity" )))
124134 } else {
125135 maxLogRows := util .Clamp (3 , height - 14 , 10 )
126- entryWidth := contentWidth - 4
127- for i , al := range m .data .RecentLogs {
128- if i >= maxLogRows {
129- break
130- }
131- lines = append (lines , bodyStyle .Render (formatLogEntry (al , entryWidth )))
136+ if len (recentActivityRows ) > maxLogRows {
137+ recentActivityRows = recentActivityRows [:maxLogRows ]
132138 }
139+
140+ recentActivityControll := tablecontroll .New (tablecontroll.Columns [recentActivityRow ]{
141+ {Title : i18n .T ("dashboard.log_col_time" ), View : func (row recentActivityRow ) string { return row .Timestamp }, MaxWidth : 0.18 },
142+ {Title : i18n .T ("dashboard.log_col_action" ), View : func (row recentActivityRow ) string { return row .Action }, MaxWidth : 0.24 },
143+ {Title : i18n .T ("dashboard.log_col_details" ), View : func (row recentActivityRow ) string { return row .Details }, MaxWidth : 0.58 },
144+ })
145+
146+ tableWidth := recentActivityControll .PreferredWidth (recentActivityRows , contentWidth )
147+ columns , rows := recentActivityControll .RenderBubblesTable (recentActivityRows , tableWidth )
148+
149+ tableModel := table .New ()
150+ tableModel .SetColumns (columns )
151+ tableModel .SetRows (rows )
152+ tableModel .SetCursor (- 1 )
153+ tableModel .SetWidth (tableWidth )
154+ tableModel .SetHeight (len (rows ) + 1 )
155+
156+ lines = append (lines , strings .Split (tableModel .View (), "\n " )... )
133157 }
134158
135159 return lipgloss .JoinVertical (lipgloss .Left , lines ... )
@@ -149,28 +173,6 @@ func formatSystemKeySerial(serial int) string {
149173 return fmt .Sprintf (i18n .T ("dashboard.system_key_serial" ), serial )
150174}
151175
152- func formatLogEntry (al AuditLogEntry , width int ) string {
153- ts := parseTimestamp (al .Timestamp )
154- action := titleFromUnderscore (strings .TrimSpace (al .Action ))
155- details := strings .TrimSpace (strings .ReplaceAll (al .Details , "\n " , " " ))
156- width = max (width , 38 )
157- actionWidth := 20
158- // TODO use bubbles table (like everywhere else)
159- base := fmt .Sprintf ("%s | %s | " , ts , action )
160- maxDetails := width - lipgloss .Width (base )
161- maxDetails = max (maxDetails , 8 )
162- if lipgloss .Width (details ) > maxDetails {
163- details = truncateRight (details , maxDetails )
164- }
165-
166- timestampStyle := lipgloss .NewStyle ().Foreground (lipgloss .Color ("8" ))
167- actionStyle := lipgloss .NewStyle ().Foreground (lipgloss .Color ("6" )).Bold (true )
168- detailStyle := lipgloss .NewStyle ().Foreground (lipgloss .Color ("7" ))
169-
170- actionPadded := padRight (action , actionWidth )
171- return timestampStyle .Render (padRight (ts , 12 )) + " | " + actionStyle .Render (actionPadded ) + " | " + detailStyle .Render (details )
172- }
173-
174176func formatAlgoSpread (algoCounts map [string ]int , style lipgloss.Style ) string {
175177 if len (algoCounts ) == 0 {
176178 return "-"
@@ -189,6 +191,16 @@ func formatAlgoSpread(algoCounts map[string]int, style lipgloss.Style) string {
189191 return strings .Join (parts , ", " )
190192}
191193
194+ func recentActivityTableRows (logs []AuditLogEntry ) []recentActivityRow {
195+ return slicest .Map (logs , func (al AuditLogEntry ) recentActivityRow {
196+ return recentActivityRow {
197+ Timestamp : parseTimestamp (al .Timestamp ),
198+ Action : titleFromUnderscore (strings .TrimSpace (al .Action )),
199+ Details : strings .TrimSpace (strings .ReplaceAll (al .Details , "\n " , " " )),
200+ }
201+ })
202+ }
203+
192204// TODO decide if this function handles date, time or datetime
193205func parseTimestamp (raw string ) string {
194206 raw = strings .TrimSpace (raw )
@@ -207,18 +219,6 @@ func parseTimestamp(raw string) string {
207219 return raw
208220}
209221
210- // TODO use lipgloss
211- func truncateRight (s string , max int ) string {
212- if max <= 1 || lipgloss .Width (s ) <= max {
213- return s
214- }
215- r := []rune (s )
216- if len (r ) >= max {
217- return string (r [:max - 3 ]) + "..."
218- }
219- return s
220- }
221-
222222// TODO use lipgloss
223223func titleFromUnderscore (action string ) string {
224224 if action == "" {
@@ -235,16 +235,6 @@ func titleFromUnderscore(action string) string {
235235 }
236236 return strings .Join (parts , " " )
237237}
238-
239- // TODO use lipgloss
240- func padRight (s string , width int ) string {
241- w := lipgloss .Width (s )
242- if w >= width {
243- return s
244- }
245- return s + strings .Repeat (" " , width - w )
246- }
247-
248238func (m * Model ) Focus (parentKeyMap help.KeyMap ) tea.Cmd {
249239 return tea .Batch (
250240 m .reload (),
0 commit comments