package banking import ( "fmt" "strings" tea "github.com/charmbracelet/bubbletea" ) // --- Account Summary --- type accountSummaryModel struct { accounts []Account } func newAccountSummaryModel(accounts []Account) accountSummaryModel { return accountSummaryModel{accounts: accounts} } func (m accountSummaryModel) Update(_ tea.Msg) (accountSummaryModel, tea.Cmd) { // Any key returns to menu. return m, nil } func (m accountSummaryModel) View() string { var b strings.Builder b.WriteString("\n") b.WriteString(centerText("ACCOUNT SUMMARY")) b.WriteString("\n\n") b.WriteString(thinDivider()) b.WriteString("\n\n") // Header. b.WriteString(titleStyle.Render(fmt.Sprintf(" %-12s %-18s %18s", "ACCOUNT", "TYPE", "BALANCE"))) b.WriteString("\n") b.WriteString(dimStyle.Render(" " + strings.Repeat("-", 50))) b.WriteString("\n") total := int64(0) for _, acct := range m.accounts { total += acct.Balance b.WriteString(baseStyle.Render(fmt.Sprintf(" %-12s %-18s %18s", acct.Number, acct.Type, formatCurrency(acct.Balance)))) b.WriteString("\n") } b.WriteString(dimStyle.Render(" " + strings.Repeat("-", 50))) b.WriteString("\n") b.WriteString(titleStyle.Render(fmt.Sprintf(" %-12s %-18s %18s", "", "TOTAL", formatCurrency(total)))) b.WriteString("\n\n") b.WriteString(thinDivider()) b.WriteString("\n\n") b.WriteString(dimStyle.Render(" PRESS ANY KEY TO RETURN TO MAIN MENU")) b.WriteString("\n") return b.String() } // --- Account Detail (with transactions) --- type accountDetailModel struct { accounts []Account transactions map[string][]Transaction selected int page int pageSize int choosing bool choice string } func newAccountDetailModel(accounts []Account, transactions map[string][]Transaction) accountDetailModel { return accountDetailModel{ accounts: accounts, transactions: transactions, choosing: true, pageSize: 10, } } func (m accountDetailModel) Update(msg tea.Msg) (accountDetailModel, tea.Cmd) { keyMsg, ok := msg.(tea.KeyMsg) if !ok { return m, nil } if m.choosing { switch keyMsg.Type { case tea.KeyEnter: for i := range m.accounts { if m.choice == fmt.Sprintf("%d", i+1) { m.selected = i m.choosing = false m.page = 0 break } } if m.choice == "0" { m.choice = "back" } return m, nil case tea.KeyBackspace: if len(m.choice) > 0 { m.choice = m.choice[:len(m.choice)-1] } default: ch := keyMsg.String() if len(ch) == 1 && ch[0] >= '0' && ch[0] <= '9' { m.choice += ch } } } else { switch keyMsg.String() { case "n", "N": acctNum := m.accounts[m.selected].Number txns := m.transactions[acctNum] maxPage := (len(txns) - 1) / m.pageSize if m.page < maxPage { m.page++ } case "p", "P": if m.page > 0 { m.page-- } case "b", "B": m.choosing = true m.choice = "" default: m.choice = "back" } } return m, nil } func (m accountDetailModel) View() string { if m.choosing { return m.viewChooseAccount() } return m.viewDetail() } func (m accountDetailModel) viewChooseAccount() string { var b strings.Builder b.WriteString("\n") b.WriteString(centerText("ACCOUNT DETAIL")) b.WriteString("\n\n") b.WriteString(thinDivider()) b.WriteString("\n\n") b.WriteString(titleStyle.Render(" SELECT ACCOUNT:")) b.WriteString("\n\n") for i, acct := range m.accounts { b.WriteString(baseStyle.Render(fmt.Sprintf(" [%d] %s - %s %s", i+1, acct.Number, acct.Type, formatCurrency(acct.Balance)))) b.WriteString("\n") } b.WriteString("\n") b.WriteString(baseStyle.Render(" [0] RETURN TO MAIN MENU")) b.WriteString("\n\n") b.WriteString(thinDivider()) b.WriteString("\n\n") b.WriteString(titleStyle.Render(" ENTER SELECTION: ")) b.WriteString(inputStyle.Render(m.choice)) b.WriteString(inputStyle.Render("_")) b.WriteString("\n") return b.String() } func (m accountDetailModel) viewDetail() string { var b strings.Builder acct := m.accounts[m.selected] txns := m.transactions[acct.Number] b.WriteString("\n") b.WriteString(centerText(fmt.Sprintf("ACCOUNT DETAIL - %s", acct.Number))) b.WriteString("\n\n") b.WriteString(baseStyle.Render(fmt.Sprintf(" TYPE: %s", acct.Type))) b.WriteString("\n") b.WriteString(baseStyle.Render(fmt.Sprintf(" BALANCE: %s", formatCurrency(acct.Balance)))) b.WriteString("\n\n") b.WriteString(thinDivider()) b.WriteString("\n\n") // Header. b.WriteString(titleStyle.Render(fmt.Sprintf(" %-12s %-34s %12s %12s", "DATE", "DESCRIPTION", "AMOUNT", "BALANCE"))) b.WriteString("\n") b.WriteString(dimStyle.Render(" " + strings.Repeat("-", 72))) b.WriteString("\n") // Paginate. start := m.page * m.pageSize end := min(start+m.pageSize, len(txns)) for _, txn := range txns[start:end] { b.WriteString(baseStyle.Render(fmt.Sprintf(" %-12s %-34s %12s %12s", txn.Date, txn.Description, formatCurrency(txn.Amount), formatCurrency(txn.Balance)))) b.WriteString("\n") } b.WriteString("\n") totalPages := (len(txns) + m.pageSize - 1) / m.pageSize b.WriteString(dimStyle.Render(fmt.Sprintf(" PAGE %d OF %d", m.page+1, totalPages))) b.WriteString("\n\n") b.WriteString(dimStyle.Render(" [N]EXT PAGE [P]REV PAGE [B]ACK ANY OTHER KEY = MAIN MENU")) b.WriteString("\n") return b.String() }