184 lines
4.5 KiB
Go
184 lines
4.5 KiB
Go
package ui
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
"github.com/atridad/wrapped-cli/pkg/models"
|
|
)
|
|
|
|
type ReportScreen struct {
|
|
stats *models.UserStats
|
|
page int
|
|
maxPages int
|
|
exportFile string
|
|
}
|
|
|
|
func NewReportScreen(stats *models.UserStats) *ReportScreen {
|
|
exportFile, _ := ExportReport(stats)
|
|
return &ReportScreen{
|
|
stats: stats,
|
|
page: 0,
|
|
maxPages: 3,
|
|
exportFile: exportFile,
|
|
}
|
|
}
|
|
|
|
func (rs *ReportScreen) Init() tea.Cmd {
|
|
return nil
|
|
}
|
|
|
|
func (rs *ReportScreen) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
switch msg.String() {
|
|
case "q", "ctrl+c":
|
|
return rs, tea.Quit
|
|
case "right", "l", "n":
|
|
if rs.page < rs.maxPages-1 {
|
|
rs.page++
|
|
}
|
|
case "left", "h", "p":
|
|
if rs.page > 0 {
|
|
rs.page--
|
|
}
|
|
}
|
|
}
|
|
return rs, nil
|
|
}
|
|
|
|
func (rs *ReportScreen) View() string {
|
|
header := lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("212")).
|
|
Bold(true).
|
|
Render(fmt.Sprintf("Gitea Wrapped %d", 2026)) + "\n"
|
|
|
|
var content string
|
|
switch rs.page {
|
|
case 0:
|
|
content = rs.renderOverviewPage()
|
|
case 1:
|
|
content = rs.renderLanguagesPage()
|
|
case 2:
|
|
content = rs.renderActivityPage()
|
|
}
|
|
|
|
footer := lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("8")).
|
|
Render(fmt.Sprintf("Page %d/%d | ← → to navigate | q to quit", rs.page+1, rs.maxPages))
|
|
|
|
report := header + "\n" + content + "\n\n" + footer
|
|
if rs.exportFile != "" {
|
|
report += "\n" + lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("10")).
|
|
Render(fmt.Sprintf("✓ Report saved: %s", rs.exportFile))
|
|
}
|
|
|
|
return report
|
|
}
|
|
|
|
func (rs *ReportScreen) renderOverviewPage() string {
|
|
var s strings.Builder
|
|
|
|
s.WriteString(lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("14")).
|
|
Bold(true).
|
|
Render("Your 2026 Overview") + "\n\n")
|
|
|
|
s.WriteString(fmt.Sprintf("Username: %s\n", rs.stats.Username))
|
|
s.WriteString(fmt.Sprintf("Total Commits: %d\n", rs.stats.TotalCommits))
|
|
s.WriteString(fmt.Sprintf("Total Repositories: %d\n", rs.stats.TotalRepositories))
|
|
s.WriteString(fmt.Sprintf("Average Commits/Day: %.2f\n", rs.stats.AverageCommitsPerDay))
|
|
s.WriteString(fmt.Sprintf("Most Active Day: %s\n", rs.stats.MostActiveDay))
|
|
s.WriteString(fmt.Sprintf("Most Active Month: %s\n", rs.stats.MostActiveMonth))
|
|
|
|
s.WriteString("\nTop Repositories:\n")
|
|
for i, repo := range rs.stats.TopRepositories {
|
|
if i >= 5 {
|
|
break
|
|
}
|
|
s.WriteString(fmt.Sprintf(" %d. %s\n", i+1, repo.Name))
|
|
}
|
|
|
|
return s.String()
|
|
}
|
|
|
|
func (rs *ReportScreen) renderLanguagesPage() string {
|
|
var s strings.Builder
|
|
|
|
s.WriteString(lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("14")).
|
|
Bold(true).
|
|
Render("Languages You Love") + "\n\n")
|
|
|
|
if len(rs.stats.Languages) == 0 {
|
|
s.WriteString("No language data available.\n")
|
|
return s.String()
|
|
}
|
|
|
|
maxLength := 30
|
|
maxCount := 0
|
|
for _, lang := range rs.stats.Languages {
|
|
if lang.Count > maxCount {
|
|
maxCount = lang.Count
|
|
}
|
|
}
|
|
|
|
for i, lang := range rs.stats.Languages {
|
|
if i >= 10 {
|
|
break
|
|
}
|
|
|
|
barLength := int(float64(lang.Count) / float64(maxCount) * float64(maxLength))
|
|
bar := strings.Repeat("█", barLength) + strings.Repeat("░", maxLength-barLength)
|
|
s.WriteString(fmt.Sprintf("%-12s %s %d (%.1f%%)\n", lang.Language, bar, lang.Count, lang.Percent))
|
|
}
|
|
|
|
return s.String()
|
|
}
|
|
|
|
func (rs *ReportScreen) renderActivityPage() string {
|
|
var s strings.Builder
|
|
|
|
s.WriteString(lipgloss.NewStyle().
|
|
Foreground(lipgloss.Color("14")).
|
|
Bold(true).
|
|
Render("Your Activity Patterns") + "\n\n")
|
|
|
|
s.WriteString("Most Active Days of the Week:\n")
|
|
if len(rs.stats.CommitsByWeekday) > 0 {
|
|
maxCount := rs.stats.CommitsByWeekday[0].Count
|
|
for i, day := range rs.stats.CommitsByWeekday {
|
|
if i >= 7 {
|
|
break
|
|
}
|
|
barLength := int(float64(day.Count) / float64(maxCount) * 20)
|
|
bar := strings.Repeat("█", barLength) + strings.Repeat("░", 20-barLength)
|
|
s.WriteString(fmt.Sprintf("%-12s %s %d\n", day.Day, bar, day.Count))
|
|
}
|
|
} else {
|
|
s.WriteString("No weekday data available.\n")
|
|
}
|
|
|
|
s.WriteString("\nMost Active Months:\n")
|
|
if len(rs.stats.CommitsByMonth) > 0 {
|
|
maxCount := rs.stats.CommitsByMonth[0].Count
|
|
for i, month := range rs.stats.CommitsByMonth {
|
|
if i >= 12 {
|
|
break
|
|
}
|
|
barLength := int(float64(month.Count) / float64(maxCount) * 20)
|
|
bar := strings.Repeat("█", barLength) + strings.Repeat("░", 20-barLength)
|
|
monthStr := month.Date.Format("Jan 2006")
|
|
s.WriteString(fmt.Sprintf("%-12s %s %d\n", monthStr, bar, month.Count))
|
|
}
|
|
} else {
|
|
s.WriteString("No monthly data available.\n")
|
|
}
|
|
|
|
return s.String()
|
|
}
|