From 3b5a92ef0c367b33b8b26397b3cb5f715714d9d2 Mon Sep 17 00:00:00 2001 From: k1nho Date: Sun, 3 Sep 2023 16:41:46 -0400 Subject: [PATCH] improving error handling flow, and adding basic dashboard responsiveness for horizontal layouts --- cmd/show/contributors.go | 104 ++++++++++++++-------- cmd/show/dashboard.go | 183 +++++++++++++++++++++++---------------- cmd/show/show.go | 24 +++-- cmd/show/styles.go | 89 +++++++++---------- cmd/show/tui.go | 36 ++++---- pkg/api/client.go | 14 ++- pkg/api/constants.go | 4 +- 7 files changed, 269 insertions(+), 185 deletions(-) diff --git a/cmd/show/contributors.go b/cmd/show/contributors.go index 6695562..7ce97bf 100644 --- a/cmd/show/contributors.go +++ b/cmd/show/contributors.go @@ -2,6 +2,7 @@ package show import ( "context" + "errors" "fmt" "sync" @@ -12,42 +13,36 @@ import ( // ContributorModel holds all the information related to a contributor type ContributorModel struct { + username string userInfo *client.DbUser userPrs []client.DbPullRequest } -// BackMsg: message to signal main model that we are back to dashboard when backspace is pressed -type BackMsg struct{} +type ( + // BackMsg: message to signal main model that we are back to dashboard when backspace is pressed + BackMsg struct{} + + // ContributorErrMsg: message to signal that an error ocurred when fetching contributor information + ContributorErrMsg struct { + name string + err error + } +) // InitContributor: initializes the contributorModel -func InitContributor(contributorName string) (ContributorModel, tea.Cmd) { +func InitContributor(contributorName string) (ContributorModel, error) { var contributorModel ContributorModel - var wg sync.WaitGroup - - wg.Add(2) - go func() { - defer wg.Done() - // TODO: handle error - userInfo, _ := fetchContributorInfo(contributorName) - contributorModel.userInfo = userInfo + contributorModel.username = contributorName - }() - - go func() { - defer wg.Done() - // TODO: handle error - userPRs, _ := fetchContributorPRs(contributorName) - contributorModel.userPrs = userPRs - }() - - wg.Wait() + err := contributorModel.fetchUser() + if err != nil { + return contributorModel, err + } - return contributorModel, func() tea.Msg { return SuccessMsg{} } + return contributorModel, nil } -func (m ContributorModel) Init() tea.Cmd { - return nil -} +func (m ContributorModel) Init() tea.Cmd { return nil } func (m ContributorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd @@ -56,7 +51,7 @@ func (m ContributorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg.String() { case "backspace": return m, func() tea.Msg { return BackMsg{} } - case "q", "esc": + case "q", "esc", "ctrl+c", "ctrl+d": return m, tea.Quit } } @@ -64,10 +59,49 @@ func (m ContributorModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m ContributorModel) View() string { - if m.userInfo != nil { - return m.drawContributorView() + return m.drawContributorView() +} + +// fetchUser: fetches all the user information (general info, and pull requests) +func (model *ContributorModel) fetchUser() error { + var ( + wg sync.WaitGroup + errChan = make(chan error, 2) + ) + + wg.Add(2) + go func() { + defer wg.Done() + userInfo, err := fetchContributorInfo(model.username) + if err != nil { + errChan <- err + return + } + model.userInfo = userInfo + + }() + + go func() { + defer wg.Done() + userPRs, err := fetchContributorPRs(model.username) + if err != nil { + errChan <- err + return + } + model.userPrs = userPRs + }() + + wg.Wait() + close(errChan) + if len(errChan) > 0 { + var allErrors error + for err := range errChan { + allErrors = errors.Join(allErrors, err) + } + return allErrors } - return "🚧 Not found" + + return nil } // fetchContributorInfo: fetches the contributor info @@ -108,7 +142,7 @@ func fetchContributorPRs(name string) ([]client.DbPullRequest, error) { // drawContributorView: view of the contributor model func (m *ContributorModel) drawContributorView() string { - return viewport().Render(lipgloss.JoinVertical(lipgloss.Center, m.drawContributorInfo(), m.drawPullRequests())) + return Viewport.Copy().Render(lipgloss.JoinVertical(lipgloss.Center, m.drawContributorInfo(), m.drawPullRequests())) } // drawContributorInfo: view of the contributor info (open issues, pr velocity, pr count, maintainer) @@ -118,13 +152,13 @@ func (m *ContributorModel) drawContributorInfo() string { prVelocity := fmt.Sprintf("🔥 PR Velocity (30d): %d%%", m.userInfo.RecentPullRequestVelocityCount) prCount := fmt.Sprintf("🚀 PR Count (30d): %d", m.userInfo.RecentPullRequestsCount) - prStats := lipgloss.JoinVertical(lipgloss.Left, textContainer().Render(prVelocity), textContainer().Render(prCount)) - issuesAndMaintainer := lipgloss.JoinVertical(lipgloss.Center, textContainer().Render(userOpenIssues), textContainer().Render(isUserMaintainer)) + prStats := lipgloss.JoinVertical(lipgloss.Left, TextContainer.Render(prVelocity), TextContainer.Render(prCount)) + issuesAndMaintainer := lipgloss.JoinVertical(lipgloss.Center, TextContainer.Render(userOpenIssues), TextContainer.Render(isUserMaintainer)) contributorInfo := lipgloss.JoinHorizontal(lipgloss.Center, prStats, issuesAndMaintainer) contributorView := lipgloss.JoinVertical(lipgloss.Center, m.userInfo.Login, contributorInfo) - return squareBorder().Render(contributorView) + return SquareBorder.Render(contributorView) } // drawPullRequests: view of the contributor pull requests (draws the last 5 pull requests) @@ -143,7 +177,7 @@ func (m *ContributorModel) drawPullRequests() string { } for i := 0; i < numberOfPrs; i++ { - prContainer := textContainer().Render(fmt.Sprintf("#%d %s\n%s\n(%s)", m.userPrs[i].Number, m.userPrs[i].GetFullName(), + prContainer := TextContainer.Render(fmt.Sprintf("#%d %s\n%s\n(%s)", m.userPrs[i].Number, m.userPrs[i].GetFullName(), m.userPrs[i].Title, m.userPrs[i].State)) pullRequests = append(pullRequests, prContainer) } @@ -152,5 +186,5 @@ func (m *ContributorModel) drawPullRequests() string { title := lipgloss.NewStyle().AlignHorizontal(lipgloss.Center).Render("✨ Latest Pull Requests") pullRequestView := lipgloss.JoinVertical(lipgloss.Center, title, formattedPrs) - return widgetContainer().Render(pullRequestView) + return WidgetContainer.Render(pullRequestView) } diff --git a/cmd/show/dashboard.go b/cmd/show/dashboard.go index a834c62..6924cd1 100644 --- a/cmd/show/dashboard.go +++ b/cmd/show/dashboard.go @@ -1,6 +1,7 @@ package show import ( + "context" "errors" "fmt" "strconv" @@ -23,7 +24,11 @@ type DashboardModel struct { newContributorsTable table.Model alumniContributorsTable table.Model RepositoryInfo *client.DbRepo + contributorErr string tableView int + queryOptions [3]int + APIClient *client.APIClient + serverContext context.Context } // SelectMsg: message to signal the main model that we want to go to the contributor model when 'enter' is pressed @@ -31,7 +36,7 @@ type SelectMsg struct { contributorName string } -// InitDashboard: initializes the dashboard model +// FetchRepoInfo: initializes the dashboard model func InitDashboard(opts *Options) (DashboardModel, error) { model := DashboardModel{} err := validateShowQuery(opts) @@ -49,58 +54,37 @@ func InitDashboard(opts *Options) (DashboardModel, error) { return model, fmt.Errorf("HTTP status: %d", r.StatusCode) } + // configuring the dashboardModel model.RepositoryInfo = resp + model.queryOptions = [3]int{opts.Page, opts.Limit, opts.Range} + model.APIClient = opts.APIClient + model.serverContext = opts.ServerContext - var ( - errorChan = make(chan error) - doneChan = make(chan bool) - wg sync.WaitGroup - ) - - wg.Add(2) - go func() { - defer wg.Done() - newContributors, err := FetchNewContributors(opts, resp.Id) - if err != nil { - errorChan <- err - return - } - model.newContributorsTable = setupContributorsTable(newContributors) - }() - - go func() { - defer wg.Done() - alumniContributors, err := FetchAlumniContributors(opts, resp.Id) - if err != nil { - errorChan <- err - return - } - model.alumniContributorsTable = setupContributorsTable(alumniContributors) - }() - - go func() { - wg.Wait() - close(doneChan) - }() - - select { - case <-doneChan: - break - case err := <-errorChan: - close(errorChan) + // Fetching all contributors + err = model.FetchAllContributors() + if err != nil { return model, err } return model, nil } -func (m DashboardModel) Init() tea.Cmd { - return nil -} +func (m DashboardModel) Init() tea.Cmd { return nil } func (m DashboardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { + case ContributorErrMsg: + m.contributorErr = fmt.Sprintf("🚧 could not fetch %s: %s", msg.name, msg.err.Error()) + default: + m.contributorErr = "" + + case tea.WindowSizeMsg: + WindowSize = msg + width, _ := m.alumniContributorsTable.Width(), m.alumniContributorsTable.Height() + m.alumniContributorsTable.SetWidth(max(msg.Width-width, 5)) + m.newContributorsTable.SetWidth(max(msg.Width-width, 5)) + case tea.KeyMsg: switch msg.String() { case "right", "l": @@ -111,7 +95,7 @@ func (m DashboardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } else { m.tableView-- } - case "q", "ctrl+c": + case "q", "esc", "ctrl+c", "ctrl+d": return m, tea.Quit case "enter": var contributorName string @@ -119,13 +103,13 @@ func (m DashboardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case newContributorsView: if len(m.newContributorsTable.Rows()) > 0 { contributorName = m.newContributorsTable.SelectedRow()[1] + return m, func() tea.Msg { return SelectMsg{contributorName} } } - return m, func() tea.Msg { return SelectMsg{contributorName} } case alumniContributorsView: - if len(m.newContributorsTable.Rows()) > 0 { + if len(m.alumniContributorsTable.Rows()) > 0 { contributorName = m.alumniContributorsTable.SelectedRow()[1] + return m, func() tea.Msg { return SelectMsg{contributorName} } } - return m, func() tea.Msg { return SelectMsg{contributorName} } } } @@ -145,7 +129,7 @@ func (m DashboardModel) View() string { } // drawTitle: view of PIZZA -func (m DashboardModel) drawTitle() string { +func (m *DashboardModel) drawTitle() string { titleRunes1 := []rune{'█', '█', '█', '█', '█', '█', '╗', ' ', '█', '█', '╗', '█', '█', '█', '█', '█', '█', '█', '╗', '█', '█', '█', '█', '█', '█', '█', '╗', ' ', '█', '█', '█', '█', '█', '╗', ' '} titleRunes2 := []rune{'█', '█', '╔', '═', '═', '█', '█', '╗', '█', '█', '║', '╚', '═', '═', '█', '█', '█', '╔', '╝', '╚', '═', '═', '█', '█', '█', '╔', '╝', '█', '█', '╔', '═', '═', '█', '█', '╗'} titleRunes3 := []rune{'█', '█', '█', '█', '█', '█', '╔', '╝', '█', '█', '║', ' ', ' ', '█', '█', '█', '╔', '╝', ' ', ' ', ' ', '█', '█', '█', '╔', '╝', ' ', '█', '█', '█', '█', '█', '█', '█', '║'} @@ -160,25 +144,25 @@ func (m DashboardModel) drawTitle() string { title5 := lipgloss.JoinHorizontal(lipgloss.Left, string(titleRunes5)) title6 := lipgloss.JoinHorizontal(lipgloss.Left, string(titleRunes6)) title := lipgloss.JoinVertical(lipgloss.Center, title1, title2, title3, title4, title5, title6) - titleView := lipgloss.NewStyle().Width(m.newContributorsTable.Width()).Margin(2, 60).Foreground(color()).Render(title) + titleView := lipgloss.NewStyle().Foreground(Color).Render(title) return titleView } -// drawRepositoryTitle: view of the repository info (name, stars, size, and issues) -func (m DashboardModel) drawRepositoryInfo() string { +// drawRepositoryInfo: view of the repository info (name, stars, size, and issues) +func (m *DashboardModel) drawRepositoryInfo() string { repoName := lipgloss.NewStyle().Bold(true).AlignHorizontal(lipgloss.Center).Render(fmt.Sprintf("Repository: %s", m.RepositoryInfo.FullName)) repoStars := fmt.Sprintf("🌟 stars: %d", m.RepositoryInfo.Stars) repoSize := fmt.Sprintf("💾 size: %dB", m.RepositoryInfo.Size) repoIssues := fmt.Sprintf("📄 issues: %d", m.RepositoryInfo.Issues) repoForks := fmt.Sprintf("⑂ forks: %d", m.RepositoryInfo.Forks) - issuesAndForks := lipgloss.JoinVertical(lipgloss.Center, textContainer().Render(repoIssues), textContainer().Render(repoForks)) - sizeAndStars := lipgloss.JoinVertical(lipgloss.Left, textContainer().Render(repoSize), textContainer().Render(repoStars)) + issuesAndForks := lipgloss.JoinVertical(lipgloss.Center, TextContainer.Render(repoIssues), TextContainer.Render(repoForks)) + sizeAndStars := lipgloss.JoinVertical(lipgloss.Left, TextContainer.Render(repoSize), TextContainer.Render(repoStars)) repoGeneralSection := lipgloss.JoinHorizontal(lipgloss.Center, issuesAndForks, sizeAndStars) repositoryInfoView := lipgloss.JoinVertical(lipgloss.Center, repoName, repoGeneralSection) - frame := squareBorder().Render(repositoryInfoView) + frame := SquareBorder.Render(repositoryInfoView) return frame } @@ -186,26 +170,28 @@ func (m DashboardModel) drawRepositoryInfo() string { // drawMetrics: view of metrics includes. // - new contributors table // - alumni contributors table -func (m DashboardModel) drawMetrics() string { +func (m *DashboardModel) drawMetrics() string { var newContributorsDisplay, alumniContributorsDisplay string switch m.tableView { case newContributorsView: - newContributorsDisplay = lipgloss.JoinVertical(lipgloss.Center, tableTitle().Render("🍕 New Contributors"), activeStyle().Render(m.newContributorsTable.View())) - alumniContributorsDisplay = lipgloss.JoinVertical(lipgloss.Center, tableTitle().Render("🍁 Alumni Contributors"), inactiveStyle().Render(m.alumniContributorsTable.View())) + newContributorsDisplay = lipgloss.JoinVertical(lipgloss.Center, TableTitle.Render("🍕 New Contributors"), ActiveStyle.Render(m.newContributorsTable.View())) + alumniContributorsDisplay = lipgloss.JoinVertical(lipgloss.Center, TableTitle.Render("🍁 Alumni Contributors"), InactiveStyle.Render(m.alumniContributorsTable.View())) case alumniContributorsView: - newContributorsDisplay = lipgloss.JoinVertical(lipgloss.Center, tableTitle().Render("🍕 New Contributors"), inactiveStyle().Render(m.newContributorsTable.View())) - alumniContributorsDisplay = lipgloss.JoinVertical(lipgloss.Center, tableTitle().Render("🍁 Alumni Contributors"), activeStyle().Render(m.alumniContributorsTable.View())) + newContributorsDisplay = lipgloss.JoinVertical(lipgloss.Center, TableTitle.Render("🍕 New Contributors"), InactiveStyle.Render(m.newContributorsTable.View())) + alumniContributorsDisplay = lipgloss.JoinVertical(lipgloss.Center, TableTitle.Render("🍁 Alumni Contributors"), ActiveStyle.Render(m.alumniContributorsTable.View())) } - contributorsMetrics := lipgloss.JoinHorizontal(lipgloss.Center, widgetContainer().Render(newContributorsDisplay), widgetContainer().Render(alumniContributorsDisplay)) - metricsView := container().Render(contributorsMetrics) + contributorsMetrics := lipgloss.JoinHorizontal(lipgloss.Center, WidgetContainer.Render(newContributorsDisplay), WidgetContainer.Render(alumniContributorsDisplay)) - return metricsView + return contributorsMetrics } // drawDashboardView: this is the main model view (shows repository info and tables) -func (m DashboardModel) drawDashboardView() string { +func (m *DashboardModel) drawDashboardView() string { + if WindowSize.Width == 0 { + return "Loading..." + } var wg sync.WaitGroup wg.Add(3) @@ -226,8 +212,14 @@ func (m DashboardModel) drawDashboardView() string { }() wg.Wait() - mainView := lipgloss.JoinVertical(lipgloss.Center, titleView, repoInfoView, metricsView) - return mainView + mainView := lipgloss.JoinVertical(lipgloss.Center, titleView, repoInfoView, metricsView, m.contributorErr) + _, h := lipgloss.Size(mainView) + if WindowSize.Height < h { + contentLeft := lipgloss.JoinVertical(lipgloss.Center, titleView, repoInfoView) + mainView = lipgloss.JoinHorizontal(lipgloss.Center, contentLeft, metricsView) + } + frame := Viewport.Render(mainView) + return frame } // validateShowQuery: validates fields set to query the contributor tables @@ -253,12 +245,51 @@ func validateShowQuery(opts *Options) error { return nil } -// FetchNewContributors: Returns all the new contributors -func FetchNewContributors(opts *Options, repoID int32) ([]client.DbPullRequestContributor, error) { - id := int(repoID) +// FetchAllContributors: fetchs and sets all the contributors (new, alumni) +func (model *DashboardModel) FetchAllContributors() error { + var ( + errorChan = make(chan error, 2) + wg sync.WaitGroup + ) + + wg.Add(2) + go func() { + defer wg.Done() + newContributors, err := model.FetchNewContributors() + if err != nil { + errorChan <- err + return + } + model.newContributorsTable = setupContributorsTable(newContributors) + }() - resp, r, err := opts.APIClient.ContributorsServiceAPI.NewPullRequestContributors(opts.ServerContext).Page(int32(opts.Page)). - Limit(int32(opts.Limit)).RepoIds(strconv.Itoa(id)).Execute() + go func() { + defer wg.Done() + alumniContributors, err := model.FetchAlumniContributors() + if err != nil { + errorChan <- err + return + } + model.alumniContributorsTable = setupContributorsTable(alumniContributors) + }() + + wg.Wait() + close(errorChan) + if len(errorChan) > 0 { + var allErrors error + for err := range errorChan { + allErrors = errors.Join(allErrors, err) + } + return allErrors + } + + return nil +} + +// FetchNewContributors: Returns all the new contributors +func (model *DashboardModel) FetchNewContributors() ([]client.DbPullRequestContributor, error) { + resp, r, err := model.APIClient.ContributorsServiceAPI.NewPullRequestContributors(model.serverContext).Page(int32(model.queryOptions[0])). + Limit(int32(model.queryOptions[1])).RepoIds(strconv.Itoa(int(model.RepositoryInfo.Id))).Execute() if err != nil { return nil, err } @@ -272,11 +303,10 @@ func FetchNewContributors(opts *Options, repoID int32) ([]client.DbPullRequestCo } // FetchAlumniContributors: Returns all alumni contributors -func FetchAlumniContributors(opts *Options, repoID int32) ([]client.DbPullRequestContributor, error) { - id := int(repoID) - - resp, r, err := opts.APIClient.ContributorsServiceAPI.FindAllChurnPullRequestContributors(opts.ServerContext).Page(int32(opts.Page)). - Limit(int32(opts.Limit)).Range_(int32(opts.Range)).RepoIds(strconv.Itoa(id)).Execute() +func (model *DashboardModel) FetchAlumniContributors() ([]client.DbPullRequestContributor, error) { + resp, r, err := model.APIClient.ContributorsServiceAPI.FindAllChurnPullRequestContributors(model.serverContext). + Page(int32(model.queryOptions[0])).Limit(int32(model.queryOptions[1])). + Range_(int32(model.queryOptions[2])).RepoIds(strconv.Itoa(int(model.RepositoryInfo.Id))).Execute() if err != nil { return nil, err } @@ -321,3 +351,10 @@ func setupContributorsTable(contributors []client.DbPullRequestContributor) tabl contributorTable.SetStyles(s) return contributorTable } + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/cmd/show/show.go b/cmd/show/show.go index 2c2ab37..c7c59a2 100644 --- a/cmd/show/show.go +++ b/cmd/show/show.go @@ -7,6 +7,7 @@ import ( "errors" client "github.com/open-sauced/go-api/client" + "github.com/open-sauced/pizza-cli/pkg/api" "github.com/spf13/cobra" ) @@ -25,6 +26,7 @@ type Options struct { // Range is the number of days to take into account when retrieving statistics Range int + // APIClient is the api client to interface with open sauced api APIClient *client.APIClient ServerContext context.Context @@ -35,7 +37,7 @@ const showLongDesc string = `WARNING: Proof of concept feature. The show command accepts the name of a git repository in the format 'owner/name' and uses OpenSauced api to retrieve metrics of the repository to be displayed as a TUI.` -// NewBakeCommand returns a new cobra command for 'pizza bake' +// NewShowCommand returns a new cobra command for 'pizza show' func NewShowCommand() *cobra.Command { opts := &Options{} @@ -48,13 +50,23 @@ func NewShowCommand() *cobra.Command { return errors.New("must specify the URL of a git repository to analyze") } opts.RepoName = args[0] - config := client.NewConfiguration() - opts.APIClient = client.NewAPIClient(config) - opts.ServerContext = context.WithValue(context.Background(), client.ContextServerIndex, 1) return nil }, RunE: func(cmd *cobra.Command, args []string) error { + var endpoint string + customEndpoint, _ := cmd.Flags().GetString("endpoint") + if customEndpoint != "" { + endpoint = customEndpoint + } + + useBeta, _ := cmd.Flags().GetBool("beta") + if useBeta { + endpoint = api.BetaAPIEndpoint + } + + opts.APIClient = api.NewGoClient(endpoint) + opts.ServerContext = context.TODO() return run(opts) }, } @@ -68,6 +80,6 @@ func NewShowCommand() *cobra.Command { func run(opts *Options) error { // Load the pizza TUI - pizzaTUI(opts) - return nil + err := pizzaTUI(opts) + return err } diff --git a/cmd/show/styles.go b/cmd/show/styles.go index 47a4935..6262be2 100644 --- a/cmd/show/styles.go +++ b/cmd/show/styles.go @@ -1,51 +1,42 @@ package show -import "github.com/charmbracelet/lipgloss" - -// viewport: The viewport of the tui (my:2, mx:40) -func viewport() lipgloss.Style { - return lipgloss.NewStyle().Margin(2, 40) -} - -// container: container styling (width: 80, py: 0, px: 5) -func container() lipgloss.Style { - return lipgloss.NewStyle().Width(80).Padding(0, 5) -} - -// widgetContainer: container for tables, and graphs (py:2, px:2) -func widgetContainer() lipgloss.Style { - return lipgloss.NewStyle().Padding(2, 2) -} - -func squareBorder() lipgloss.Style { - return lipgloss.NewStyle().BorderStyle(lipgloss.RoundedBorder()).BorderForeground(color()) -} - -// textContainer: container for text -func textContainer() lipgloss.Style { - return lipgloss.NewStyle().Padding(1, 1) -} - -// tableTitle: The style for table titles (width:25, align-horizontal:center, bold:true) -func tableTitle() lipgloss.Style { - return lipgloss.NewStyle().Width(25).AlignHorizontal(lipgloss.Center).Bold(true) -} - -// color: the color palette (Light: #000000, Dark: #FF4500) -func color() lipgloss.AdaptiveColor { - return lipgloss.AdaptiveColor{Light: "#000000", Dark: "#FF4500"} -} - -// activeStyle: table when selected (border:normal, border-foreground:#FF4500) -func activeStyle() lipgloss.Style { - return lipgloss.NewStyle(). - BorderStyle(lipgloss.NormalBorder()). - BorderForeground(lipgloss.Color("#FF4500")) -} - -// inactiveStyle: table when unselected (border: normal, border-foreground:#FFFFFF) -func inactiveStyle() lipgloss.Style { - return lipgloss.NewStyle(). - BorderStyle(lipgloss.NormalBorder()). - BorderForeground(lipgloss.Color("#FFFFFF")) -} +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +// WindowSize stores the size of the terminal +var WindowSize tea.WindowSizeMsg + +// STYLES + +// Viewport: The viewport of the tui (my:2, mx:40) +var Viewport = lipgloss.NewStyle().Margin(1, 2) + +// Container: container styling (width: 80, py: 0, px: 5) +var Container = lipgloss.NewStyle().Width(80).Padding(0, 5) + +// WidgetContainer: container for tables, and graphs (py:2, px:2) +var WidgetContainer = lipgloss.NewStyle().Padding(2, 2) + +// SquareBorder: Style to draw a border around a section +var SquareBorder = lipgloss.NewStyle().BorderStyle(lipgloss.RoundedBorder()).BorderForeground(Color) + +// TextContainer: container for text +var TextContainer = lipgloss.NewStyle().Padding(1, 1) + +// TableTitle: The style for table titles (width:25, align-horizontal:center, bold:true) +var TableTitle = lipgloss.NewStyle().Width(25).AlignHorizontal(lipgloss.Center).Bold(true) + +// Color: the color palette (Light: #000000, Dark: #FF4500) +var Color = lipgloss.AdaptiveColor{Light: "#000000", Dark: "#FF4500"} + +// ActiveStyle: table when selected (border:normal, border-foreground:#FF4500) +var ActiveStyle = lipgloss.NewStyle(). + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("#FF4500")) + +// InactiveStyle: table when not selected (border:normal, border-foreground:#FFFFFF) +var InactiveStyle = lipgloss.NewStyle(). + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("#FFFFFF")) diff --git a/cmd/show/tui.go b/cmd/show/tui.go index a52045c..989d70b 100644 --- a/cmd/show/tui.go +++ b/cmd/show/tui.go @@ -2,29 +2,21 @@ package show import ( "fmt" - "os" tea "github.com/charmbracelet/bubbletea" ) -// ██████╗ ██╗███████╗███████╗ █████╗ -// ██╔══██╗██║╚══███╔╝╚══███╔╝██╔══██╗ -// ██████╔╝██║ ███╔╝ ███╔╝ ███████║ -// ██╔═══╝ ██║ ███╔╝ ███╔╝ ██╔══██║ -// ██║ ██║███████╗███████╗██║ ██║ -// ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ - const ( dashboardView = iota contributorView ) -// sessionState serves as the variable to reference when looking for which model the user is in type ( + // sessionState serves as the variable to reference when looking for which model the user is in sessionState int - SuccessMsg struct{} ) +// MainModel: the main model is the central state manager of the TUI, decides which model is focused based on certain commands type MainModel struct { state sessionState dashboard tea.Model @@ -64,24 +56,30 @@ func (m MainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmd = newCmd case contributorView: // TODO: process cmd error if contributor fails to fetch - m.contributor, _ = InitContributor(m.authorName) - _, newCmd := m.contributor.Update(msg) + var err error + m.contributor, err = InitContributor(m.authorName) + if err != nil { + m.state = dashboardView + return m, func() tea.Msg { return ContributorErrMsg{name: m.authorName, err: err} } + } + newContributor, newCmd := m.contributor.Update(msg) + m.contributor = newContributor cmd = newCmd } cmds = append(cmds, cmd) return m, tea.Batch(cmds...) } -func pizzaTUI(opts *Options) { - - // TODO: handle error to cobra - dashboardModel, _ := InitDashboard(opts) +func pizzaTUI(opts *Options) error { + dashboardModel, err := InitDashboard(opts) + if err != nil { + return err + } model := MainModel{dashboard: dashboardModel} - if _, err := tea.NewProgram(model, tea.WithAltScreen()).Run(); err != nil { - fmt.Println("Error running program:", err) - os.Exit(1) + return fmt.Errorf("Error running program: %s", err.Error()) } + return nil } diff --git a/pkg/api/client.go b/pkg/api/client.go index e519936..7cdaf47 100644 --- a/pkg/api/client.go +++ b/pkg/api/client.go @@ -1,6 +1,10 @@ package api -import "net/http" +import ( + "net/http" + + "github.com/open-sauced/go-api/client" +) type Client struct { // The configured http client for making API requests @@ -18,3 +22,11 @@ func NewClient(endpoint string) *Client { Endpoint: endpoint, } } + +func NewGoClient(endpoint string) *client.APIClient { + config := client.NewConfiguration() + config.Servers = client.ServerConfigurations{ + {URL: endpoint}, + } + return client.NewAPIClient(config) +} diff --git a/pkg/api/constants.go b/pkg/api/constants.go index 66a71e7..5f4a0d0 100644 --- a/pkg/api/constants.go +++ b/pkg/api/constants.go @@ -1,6 +1,6 @@ package api const ( - APIEndpoint = "https://api.opensauced.pizza/v1" - BetaAPIEndpoint = "https://beta.api.opensauced.pizza/v1" + APIEndpoint = "https://api.opensauced.pizza" + BetaAPIEndpoint = "https://beta.api.opensauced.pizza" )