Browse Source

lots of functionality

master
Brett Bender 2 years ago
parent
commit
a59702182f
15 changed files with 1902 additions and 15 deletions
  1. +72
    -0
      internal/api/api.go
  2. +9
    -4
      internal/app/app.go
  3. +4
    -0
      internal/app/init.go
  4. +6
    -0
      internal/types/buildings.go
  5. +5
    -4
      internal/types/shelves.go
  6. +2
    -2
      internal/ui/assets/astdialogs/edit.go
  7. +156
    -0
      internal/ui/shelves/command.go
  8. +48
    -0
      internal/ui/shelves/data.go
  9. +24
    -0
      internal/ui/shelves/draw.go
  10. +66
    -0
      internal/ui/shelves/key.go
  11. +72
    -0
      internal/ui/shelves/refresh.go
  12. +218
    -0
      internal/ui/shelves/shelves.go
  13. +605
    -0
      internal/ui/shelves/shlvdialogs/create.go
  14. +604
    -0
      internal/ui/shelves/shlvdialogs/edit.go
  15. +11
    -5
      internal/ui/utils/keys.go

+ 72
- 0
internal/api/api.go View File

@ -132,6 +132,66 @@ func (c *APIClient) RetrieveAllShelves() ([]*types.ShelfLocation, error) {
return resp.ShelfLocations, nil
}
func (c *APIClient) DeleteShelfByID(idNumber string) (*types.ShelfLocation, error) {
url := fmt.Sprintf("%s/shelves/%s", c.Host, idNumber)
req, _ := http.NewRequest("DELETE", url, bytes.NewBuffer([]byte{}))
resp, err := makeRequest[types.ShelfResponse](c, req)
if err != nil {
return nil, err
}
return resp.ShelfLocation, nil
}
func (c *APIClient) CreateShelf(request types.CreateShelfRequest) (*types.ShelfLocation, error) {
url := fmt.Sprintf("%s/shelves", c.Host)
body, err := json.Marshal(request)
if err != nil {
return nil, err
}
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body))
resp, err := makeRequest[types.ShelfResponse](c, req)
if err != nil {
return nil, err
}
return resp.ShelfLocation, nil
}
func (c *APIClient) UpdateShelf(id string, request types.CreateShelfRequest) (*types.ShelfLocation, error) {
url := fmt.Sprintf("%s/shelves/%s", c.Host, id)
body, err := json.Marshal(request)
if err != nil {
return nil, err
}
req, _ := http.NewRequest("PUT", url, bytes.NewBuffer(body))
resp, err := makeRequest[types.ShelfResponse](c, req)
if err != nil {
return nil, err
}
return resp.ShelfLocation, nil
}
func (c *APIClient) RetrieveShelfByID(idNumber string) (*types.ShelfLocation, error) {
url := fmt.Sprintf("%s/shelves/%s", c.Host, idNumber)
req, _ := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
resp, err := makeRequest[types.ShelfResponse](c, req)
if err != nil {
return nil, err
}
return resp.ShelfLocation, nil
}
func (c *APIClient) RetrieveAllCategories() ([]*types.Category, error) {
url := fmt.Sprintf("%s/categories", c.Host)
req, _ := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
@ -143,3 +203,15 @@ func (c *APIClient) RetrieveAllCategories() ([]*types.Category, error) {
return resp.Categories, nil
}
func (c *APIClient) RetrieveAllBuildings() ([]*types.Building, error) {
url := fmt.Sprintf("%s/buildings", c.Host)
req, _ := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
resp, err := makeRequest[types.MultipleBuildingResponse](c, req)
if err != nil {
return nil, err
}
return resp.Buildings, nil
}

+ 9
- 4
internal/app/app.go View File

@ -6,6 +6,7 @@ import (
"git.brettb.xyz/goinv/client/internal/api"
"git.brettb.xyz/goinv/client/internal/ui/assets"
"git.brettb.xyz/goinv/client/internal/ui/help"
"git.brettb.xyz/goinv/client/internal/ui/shelves"
"git.brettb.xyz/goinv/client/internal/ui/utils"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
@ -18,9 +19,9 @@ type App struct {
pages *tview.Pages
logger *zap.Logger
help *help.Help
assets *assets.Assets
// shelves *shelves.Shelves
help *help.Help
assets *assets.Assets
shelves *shelves.Shelves
// categories *categories.Categories
// buildings *buildings.Buildings
menu *tview.TextView
@ -48,7 +49,7 @@ func NewApp(name, version, host string, logger *zap.Logger) *App {
menuItems := [][]string{
{utils.HelpScreenKey.Label(), app.help.GetTitle()},
{utils.AssetsScreenKey.Label(), app.assets.GetTitle()},
//{utils.ShelvesScreenKey.Label(), app.shelves.GetTitle()},
{utils.ShelvesScreenKey.Label(), app.shelves.GetTitle()},
//{utils.CategoriesScreenKey.Label(), app.categories.GetTitle()},
//{utils.BuildingsScreenKey.Label(), app.buildings.GetTitle()},
}
@ -57,6 +58,7 @@ func NewApp(name, version, host string, logger *zap.Logger) *App {
app.pages.AddPage(app.help.GetTitle(), app.help, true, false)
app.pages.AddPage(app.assets.GetTitle(), app.assets, true, false)
app.pages.AddPage(app.shelves.GetTitle(), app.shelves, true, false)
return &app
}
@ -98,6 +100,9 @@ func (app *App) Run() error {
case utils.AssetsScreenKey.EventKey():
app.switchToScreen(app.assets.GetTitle())
return nil
case utils.ShelvesScreenKey.EventKey():
app.switchToScreen(app.shelves.GetTitle())
return nil
}
}


+ 4
- 0
internal/app/init.go View File

@ -5,4 +5,8 @@ func (app *App) initUI() {
// Assets page
app.assets.UpdateShelfData()
app.assets.UpdateAssetData()
// Shelves page
app.shelves.UpdateShelfData()
app.shelves.UpdateBuildingData()
}

+ 6
- 0
internal/types/buildings.go View File

@ -9,3 +9,9 @@ type Building struct {
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
}
type MultipleBuildingResponse struct {
*Response
Buildings []*Building `json:"buildings"`
Total int `json:"total"`
}

+ 5
- 4
internal/types/shelves.go View File

@ -19,14 +19,15 @@ type ShelfLocation struct {
RoomNumber string `json:"room_number,omitempty"`
Description string `json:"description,omitempty"`
BuildingID *uint64 `json:"building_id"`
Building *Building `json:"-"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
}
type CreateShelfRequest struct {
Name string `json:"name"`
RoomNumber string `json:"room_number,omitempty"`
Description string `json:"description,omitempty"`
BuildingID uint64 `json:"building_id"`
Name string `json:"name"`
RoomNumber string `json:"room_number,omitempty"`
Description string `json:"description,omitempty"`
BuildingID *uint64 `json:"building_id"`
}

+ 2
- 2
internal/ui/assets/astdialogs/edit.go View File

@ -394,7 +394,7 @@ func (d *AssetEditDialog) initData() {
}
// Comments category
d.assetCommentsArea.SetText("", true)
d.assetCommentsArea.SetText(d.asset.Comments, true)
}
}
@ -773,7 +773,7 @@ func (d *AssetEditDialog) EditAssetOptions() types.CreateAssetRequest {
Manufacturer: d.assetManufacturerField.GetText(),
ModelName: d.assetModelField.GetText(),
Price: price,
Comments: d.assetCommentsArea.GetText(),
Comments: strings.TrimSpace(d.assetCommentsArea.GetText()),
ShelfLocationID: shelfID,
CategoryID: categoryID,
}


+ 156
- 0
internal/ui/shelves/command.go View File

@ -0,0 +1,156 @@
package shelves
import (
"fmt"
"git.brettb.xyz/goinv/client/internal/ui/dialogs"
"git.brettb.xyz/goinv/client/internal/ui/style"
)
func (a *Shelves) runCommand(cmd string) {
switch cmd {
case "create shelf":
a.createDialog.Display()
case "edit shelf":
a.cedit()
case "delete shelf":
a.cdelete()
case "refresh":
a.crefresh()
}
}
func (a *Shelves) cNotImplemented() {
a.displayError("not implemented", fmt.Errorf("this command has not been implemented"))
}
// Confirm deletion
func (a *Shelves) cdelete() {
selectedItem := a.getSelectedItem()
// Empty table
if selectedItem == nil {
a.displayError("DELETE SHELF ERROR", fmt.Errorf("no shelfs to delete"))
return
}
title := "delete shelf"
a.confirmDialog.SetTitle(title)
a.confirmData = status_CONFIRM_DELETE_SHELF
bgColor := style.GetColorHex(style.DialogSubBoxBorderColor)
fgColor := style.GetColorHex(style.DialogFgColor)
shelfName := fmt.Sprintf("[%s:%s:b]SHELF NAME:[:-:-] %s", fgColor, bgColor, selectedItem.name)
confirmMsg := fmt.Sprintf("%s\n%s\nAre you sure you want to delete the selected shelf ?", shelfName)
a.confirmDialog.SetText(confirmMsg)
a.confirmDialog.Display()
}
func (a *Shelves) cedit() {
selectedItem := a.getSelectedItem()
if selectedItem == nil {
a.displayError("DELETE SHELF ERROR", fmt.Errorf("no shelves to edit"))
return
}
shelf, err := a.client.RetrieveShelfByID(selectedItem.id)
if err != nil {
a.displayError("DELETE SHELF ERROR", fmt.Errorf("unable to retrieve shelf from server"))
return
}
a.editDialog.SetShelf(shelf)
a.editDialog.Display()
}
func (a *Shelves) edit() {
selectedItem := a.getSelectedItem()
createReq := a.editDialog.EditShelfOptions()
if createReq.Name == "" {
a.displayError("SHELF EDIT ERROR", fmt.Errorf("shelf name cannot be empty"))
return
}
_, err := a.client.UpdateShelf(selectedItem.id, createReq)
if err != nil {
a.displayError("SHELF EDIT ERROR", err)
return
}
a.crefresh()
}
func (a *Shelves) delete() {
selectedItem := a.getSelectedItem()
a.progressDialog.SetTitle(fmt.Sprintf("deleting shelf %s", selectedItem.id))
a.progressDialog.Display()
del := func() {
_, err := a.client.DeleteShelfByID(selectedItem.id)
a.progressDialog.Hide()
if err != nil {
a.displayError("DELETE SHELF ERROR", err)
return
}
// display success message
a.messageDialog.SetTitle(fmt.Sprintf("deleting shelf %s", selectedItem.id))
a.messageDialog.SetText(dialogs.MessageGeneric, "Success!", fmt.Sprintf("Shelf %s successfully deleted.", selectedItem.id))
a.messageDialog.Display()
a.UpdateShelfData()
}
del()
}
func (a *Shelves) crefresh() {
a.progressDialog.SetTitle("refreshing shelfs")
a.progressDialog.Display()
ref := func() {
a.UpdateShelfData()
a.UpdateShelfData()
a.progressDialog.Hide()
if !a.errorDialog.IsDisplay() {
a.messageDialog.SetTitle("shelf refresh")
a.messageDialog.SetText(dialogs.MessageGeneric, "Refreshed!", "Successfully refreshed page.")
a.messageDialog.Display()
}
}
ref()
}
func (a *Shelves) create() {
createReq := a.createDialog.CreateShelfOptions()
if createReq.Name == "" {
a.displayError("SHELF CREATE ERROR", fmt.Errorf("shelf name cannot be empty"))
return
}
_, err := a.client.CreateShelf(createReq)
if err != nil {
a.displayError("SHELF CREATE ERROR", err)
return
}
a.crefresh()
a.shelfTable.ScrollToEnd()
a.shelfTable.Select(a.shelfTable.GetRowCount(), 0)
}
func (a *Shelves) displayError(title string, err error) {
a.errorDialog.SetTitle(title)
a.errorDialog.SetText(fmt.Sprintf("%v", err))
a.errorDialog.Display()
}

+ 48
- 0
internal/ui/shelves/data.go View File

@ -0,0 +1,48 @@
package shelves
import "git.brettb.xyz/goinv/client/internal/types"
func (a *Shelves) UpdateData() {
a.UpdateShelfData()
a.UpdateBuildingData()
}
func (a *Shelves) UpdateShelfData() {
shelves, err := a.shelfListFunc()
a.shelfList.mu.Lock()
defer a.shelfList.mu.Unlock()
if err != nil {
a.displayError("could not retrieve shelves", err)
a.shelfList.dirty = true
return
}
a.shelfList.dirty = false
a.shelfList.report = shelves
}
func (a *Shelves) UpdateBuildingData() {
buildings, err := a.buildingListFunc()
if err != nil {
a.displayError("could not retrieve buildings", err)
return
}
a.buildingCache.mu.Lock()
a.buildingCache.report = buildings
a.buildingCache.mu.Unlock()
}
func (a *Shelves) getShelfData() []types.ShelfLocation {
a.shelfList.mu.Lock()
shelfReport := a.shelfList.report
defer a.shelfList.mu.Unlock()
return shelfReport
}
func (a *Shelves) getBuildingData() map[uint64]types.Building {
a.buildingCache.mu.Lock()
buildingReport := a.buildingCache.report
defer a.buildingCache.mu.Unlock()
return buildingReport
}

+ 24
- 0
internal/ui/shelves/draw.go View File

@ -0,0 +1,24 @@
package shelves
import "github.com/gdamore/tcell/v2"
func (a *Shelves) Draw(screen tcell.Screen) {
a.refresh()
a.Box.DrawForSubclass(screen, a)
x, y, width, height := a.GetInnerRect()
a.shelfTable.SetRect(x, y, width, height)
a.shelfTable.Draw(screen)
x, y, width, height = a.shelfTable.GetInnerRect()
for _, diag := range a.allDialogs {
if diag.IsDisplay() {
diag.SetRect(x, y, width, height)
diag.Draw(screen)
return
}
}
}

+ 66
- 0
internal/ui/shelves/key.go View File

@ -0,0 +1,66 @@
package shelves
import (
"git.brettb.xyz/goinv/client/internal/ui/utils"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
func (a *Shelves) InputHandler() func(*tcell.EventKey, func(tview.Primitive)) {
return a.WrapInputHandler(func(event *tcell.EventKey, setFocus func(tview.Primitive)) {
a.logger.Sugar().Debugf("assets event %v received", event)
if a.progressDialog.IsDisplay() {
setFocus(a.progressDialog)
return
}
if a.errorDialog.HasFocus() {
if errorHandler := a.errorDialog.InputHandler(); errorHandler != nil {
errorHandler(event, setFocus)
}
}
if a.messageDialog.HasFocus() {
if messageHandler := a.messageDialog.InputHandler(); messageHandler != nil {
messageHandler(event, setFocus)
}
}
if a.confirmDialog.HasFocus() {
if confirmHandler := a.confirmDialog.InputHandler(); confirmHandler != nil {
confirmHandler(event, setFocus)
}
}
if a.createDialog.HasFocus() {
if createHandler := a.createDialog.InputHandler(); createHandler != nil {
createHandler(event, setFocus)
}
}
if a.editDialog.HasFocus() {
if editHandler := a.editDialog.InputHandler(); editHandler != nil {
editHandler(event, setFocus)
}
}
if a.cmdDialog.HasFocus() {
if cmdHandler := a.cmdDialog.InputHandler(); cmdHandler != nil {
cmdHandler(event, setFocus)
}
}
if a.shelfTable.HasFocus() {
if event.Rune() == utils.CommandMenuKey.Rune() {
a.cmdDialog.Display()
} else {
if tableHandler := a.shelfTable.InputHandler(); tableHandler != nil {
tableHandler(event, setFocus)
}
}
}
setFocus(a)
})
}

+ 72
- 0
internal/ui/shelves/refresh.go View File

@ -0,0 +1,72 @@
package shelves
import (
"fmt"
"strings"
"git.brettb.xyz/goinv/client/internal/ui/style"
"github.com/rivo/tview"
)
const tableHeaderOffset = 1
func (a *Shelves) refresh() {
shelfs := a.getShelfData()
a.shelfTable.Clear()
a.updateShelfTableTitle(len(shelfs))
a.writeTableHeaders()
for i, shelf := range shelfs {
row := i + tableHeaderOffset
// ID, Name, Building, Room
a.shelfTable.SetCell(row, 0,
tview.NewTableCell(fmt.Sprintf("%d", shelf.ID)).
SetExpansion(a.shelfTableExpansions[0]).
SetAlign(tview.AlignLeft))
a.shelfTable.SetCell(row, 1,
tview.NewTableCell(shelf.Name).
SetExpansion(a.shelfTableExpansions[1]).
SetAlign(tview.AlignLeft))
building := ""
if shelf.Building != nil {
building = shelf.Building.Name
}
a.shelfTable.SetCell(row, 2,
tview.NewTableCell(building).
SetExpansion(a.shelfTableExpansions[2]).
SetAlign(tview.AlignLeft))
a.shelfTable.SetCell(row, 2,
tview.NewTableCell(shelf.RoomNumber).
SetExpansion(a.shelfTableExpansions[3]).
SetAlign(tview.AlignLeft))
}
}
func (a *Shelves) updateShelfTableTitle(count int) {
dirtyFlag := ""
if a.shelfList.dirty {
dirtyFlag = "*"
}
title := fmt.Sprintf("[::b]SHELVES [%s%d]", dirtyFlag, count)
a.shelfTable.SetTitle(title)
}
func (a *Shelves) writeTableHeaders() {
for i, headerText := range a.shelfTableHeaders {
header := fmt.Sprintf("[::b]%s", strings.ToUpper(headerText))
a.shelfTable.SetCell(0, i,
tview.NewTableCell(header).
SetExpansion(a.shelfTableExpansions[i]).
SetBackgroundColor(style.TableHeaderBgColor).
SetTextColor(style.TableHeaderFgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
}
}

+ 218
- 0
internal/ui/shelves/shelves.go View File

@ -0,0 +1,218 @@
package shelves
import (
"git.brettb.xyz/goinv/client/internal/ui/shelves/shlvdialogs"
"sync"
"git.brettb.xyz/goinv/client/internal/api"
"git.brettb.xyz/goinv/client/internal/types"
"git.brettb.xyz/goinv/client/internal/ui/dialogs"
"git.brettb.xyz/goinv/client/internal/ui/style"
"git.brettb.xyz/goinv/client/internal/ui/utils"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"go.uber.org/zap"
)
const (
status_CONFIRM_DELETE_SHELF = "delete_shelf"
)
type Shelves struct {
*tview.Box
client *api.APIClient
title string
logger *zap.Logger
shelfTable *tview.Table
shelfList shelfListReport
buildingCache buildingListReport
shelfTableHeaders []string
shelfTableExpansions []int
cmdDialog *dialogs.CommandDialog
confirmDialog *dialogs.ConfirmDialog
errorDialog *dialogs.ErrorDialog
progressDialog *dialogs.ProgressDialog
messageDialog *dialogs.MessageDialog
createDialog *shlvdialogs.ShelfCreateDialog
editDialog *shlvdialogs.ShelfEditDialog
allDialogs []dialogs.Dialog
confirmData string
shelfListFunc func() ([]types.ShelfLocation, error)
buildingListFunc func() (map[uint64]types.Building, error)
}
type shelfSelectedItem struct {
id string
name string
building string
roomNumber string
}
type shelfListReport struct {
mu sync.Mutex
report []types.ShelfLocation
dirty bool
}
type buildingListReport struct {
mu sync.Mutex
report map[uint64]types.Building
dirty bool
}
func NewShelves(logger *zap.Logger, client *api.APIClient) *Shelves {
shelves := &Shelves{
Box: tview.NewBox(),
client: client,
title: "shelves",
logger: logger,
shelfTable: tview.NewTable(),
shelfTableHeaders: []string{"id", "name", "building", "room number"},
shelfTableExpansions: []int{1, 4, 2, 2},
confirmDialog: dialogs.NewConfirmDialog(logger),
errorDialog: dialogs.NewErrorDialog(logger),
progressDialog: dialogs.NewProgressDialog(logger),
messageDialog: dialogs.NewMessageDialog(logger, ""),
createDialog: shlvdialogs.NewShelfCreateDialog(logger, client),
editDialog: shlvdialogs.NewShelfEditDialog(logger, client),
}
shelves.shelfTable.SetBackgroundColor(style.BgColor)
shelves.shelfTable.SetBorder(true)
shelves.updateShelfTableTitle(0)
shelves.shelfTable.SetTitleColor(style.FgColor)
shelves.shelfTable.SetBorderColor(style.BorderColor)
shelves.shelfTable.SetFixed(1, 1)
shelves.shelfTable.SetSelectable(true, false)
shelves.writeTableHeaders()
shelves.shelfTable.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if shelves.shelfTable.GetRowCount() <= 1 {
return nil
}
return event
})
shelves.cmdDialog = dialogs.NewCommandDialog(logger, [][]string{
{"create shelf", "create a new shelf"},
{"edit shelf", "edit the selected shelf"},
{"delete shelf", "delete the selected shelf"},
{"refresh", "refresh the page"},
})
shelves.cmdDialog.SetSelectedFunc(func() {
shelves.cmdDialog.Hide()
shelves.runCommand(shelves.cmdDialog.GetSelectedItem())
}).SetCancelFunc(func() {
shelves.cmdDialog.Hide()
})
shelves.confirmDialog.SetSelectedFunc(func() {
shelves.confirmDialog.Hide()
switch shelves.confirmData {
case status_CONFIRM_DELETE_SHELF:
shelves.delete()
}
}).SetCancelFunc(func() {
shelves.confirmDialog.Hide()
})
shelves.messageDialog.SetCancelFunc(func() {
shelves.messageDialog.Hide()
})
shelves.createDialog.SetCancelFunc(func() {
shelves.createDialog.Hide()
}).SetCreateFunc(func() {
shelves.createDialog.Hide()
shelves.create()
})
shelves.editDialog.SetCancelFunc(func() {
shelves.editDialog.Hide()
}).SetEditFunc(func() {
shelves.editDialog.Hide()
shelves.edit()
})
shelves.SetBuildingListFunc(func() (map[uint64]types.Building, error) {
if resp, err := shelves.client.RetrieveAllBuildings(); err != nil {
return nil, err
} else {
buildings := map[uint64]types.Building{}
for _, a := range resp {
buildings[a.ID] = *a
}
return buildings, nil
}
})
shelves.allDialogs = []dialogs.Dialog{
shelves.errorDialog,
shelves.messageDialog,
shelves.progressDialog,
shelves.confirmDialog,
shelves.createDialog,
shelves.editDialog,
shelves.cmdDialog,
}
return shelves
}
func (a *Shelves) GetTitle() string {
return a.title
}
func (a *Shelves) HasFocus() bool {
return dialogs.CheckDialogFocus(a.allDialogs...) || utils.CheckFocus(a.shelfTable, a.Box)
}
func (a *Shelves) SubDialogHasFocus() bool {
return dialogs.CheckDialogFocus(a.allDialogs...)
}
func (a *Shelves) Focus(delegate func(tview.Primitive)) {
for _, dialog := range a.allDialogs {
if dialog.IsDisplay() {
delegate(dialog)
return
}
}
delegate(a.shelfTable)
}
func (a *Shelves) SetBuildingListFunc(list func() (map[uint64]types.Building, error)) {
a.buildingListFunc = list
}
func (a *Shelves) SetShelfListFunc(list func() ([]types.ShelfLocation, error)) {
a.shelfListFunc = list
}
func (a *Shelves) hideAllDialogs() {
for _, dialog := range a.allDialogs {
dialog.Hide()
}
}
func (a *Shelves) getSelectedItem() *shelfSelectedItem {
selectedItem := shelfSelectedItem{}
if a.shelfTable.GetRowCount() <= 1 {
return nil
}
row, _ := a.shelfTable.GetSelection()
selectedItem.id = a.shelfTable.GetCell(row, 0).Text
selectedItem.name = a.shelfTable.GetCell(row, 1).Text
selectedItem.building = a.shelfTable.GetCell(row, 2).Text
selectedItem.roomNumber = a.shelfTable.GetCell(row, 3).Text
return &selectedItem
}

+ 605
- 0
internal/ui/shelves/shlvdialogs/create.go View File

@ -0,0 +1,605 @@
package shlvdialogs
import (
"fmt"
"strings"
"git.brettb.xyz/goinv/client/internal/api"
"git.brettb.xyz/goinv/client/internal/types"
"git.brettb.xyz/goinv/client/internal/ui/dialogs"
"git.brettb.xyz/goinv/client/internal/ui/style"
"git.brettb.xyz/goinv/client/internal/ui/utils"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"go.uber.org/zap"
)
const (
shelfCreateDialogMaxWidth = 100
shelfCreateDialogHeight = 17
)
const (
createShelfFormFocus = 0 + iota
createCategoriesFocus
createCategoryPagesFocus
createShelfNameFieldFocus
createShelfRoomFieldFocus
createShelfBuildingFieldFocus
createShelfDescriptionFieldFocus
)
const (
generalPageIndex = 0 + iota
locationPageIndex
commentPageIndex
)
type ShelfCreateDialog struct {
*tview.Box
layout *tview.Flex
createCategoryLabels []string
categories *tview.TextView
categoryPages *tview.Pages
generalInfoPage *tview.Flex
locationPage *tview.Flex
commentsPage *tview.Flex
form *tview.Form
display bool
activePageIndex int
focusElement int
logger *zap.Logger
client *api.APIClient
shelf *types.ShelfLocation
buildingList []*types.Building
shelfNameField *tview.InputField
shelfRoomField *tview.InputField
shelfDescriptionArea *tview.TextArea
shelfBuildingField *tview.DropDown
focusMap map[int]tview.Primitive
cancelHandler func()
createHandler func()
}
func NewShelfCreateDialog(logger *zap.Logger, client *api.APIClient) *ShelfCreateDialog {
createDialog := ShelfCreateDialog{
Box: tview.NewBox(),
layout: tview.NewFlex(),
createCategoryLabels: []string{
"General",
"Location",
"Comments",
},
categories: tview.NewTextView(),
categoryPages: tview.NewPages(),
generalInfoPage: tview.NewFlex(),
locationPage: tview.NewFlex(),
commentsPage: tview.NewFlex(),
form: tview.NewForm(),
display: false,
activePageIndex: 0,
logger: logger,
client: client,
shelfNameField: tview.NewInputField(),
shelfRoomField: tview.NewInputField(),
shelfBuildingField: tview.NewDropDown(),
shelfDescriptionArea: tview.NewTextArea(),
}
createDialog.focusMap = map[int]tview.Primitive{
createShelfNameFieldFocus: createDialog.shelfNameField,
createShelfRoomFieldFocus: createDialog.shelfRoomField,
createShelfBuildingFieldFocus: createDialog.shelfBuildingField,
createShelfDescriptionFieldFocus: createDialog.shelfDescriptionArea,
}
createDialog.setupLayout()
createDialog.setActiveCategory(0)
createDialog.initCustomInputHandlers()
return &createDialog
}
func (d *ShelfCreateDialog) Display() {
d.display = true
d.initData()
d.focusElement = createCategoryPagesFocus
}
func (d *ShelfCreateDialog) IsDisplay() bool {
return d.display
}
func (d *ShelfCreateDialog) Hide() {
d.display = false
}
func (d *ShelfCreateDialog) HasFocus() bool {
return utils.CheckFocus(d.categories, d.categoryPages, d.Box, d.form)
}
func (d *ShelfCreateDialog) Focus(delegate func(tview.Primitive)) {
switch d.focusElement {
case createShelfFormFocus:
button := d.form.GetButton(d.form.GetButtonCount() - 1)
button.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == utils.SwitchFocusKey.EventKey() {
d.focusElement = createCategoriesFocus
d.Focus(delegate)
d.form.SetFocus(0)
return nil
}
if event.Key() == tcell.KeyEnter {
return nil
}
return event
})
delegate(d.form)
case createCategoriesFocus:
d.categories.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == utils.SwitchFocusKey.EventKey() {
d.focusElement = createCategoryPagesFocus
d.Focus(delegate)
return nil
}
if event.Key() == tcell.KeyBacktab {
d.focusElement = createShelfFormFocus
d.Focus(delegate)
return nil
}
event = utils.ParseKeyEventKey(d.logger, event)
if event.Key() == tcell.KeyDown {
d.nextCategory()
}
if event.Key() == tcell.KeyUp {
d.previousCategory()
}
return event
})
delegate(d.categories)
case createCategoryPagesFocus:
delegate(d.categoryPages)
default:
delegate(d.focusMap[d.focusElement])
}
}
func (d *ShelfCreateDialog) InputHandler() func(*tcell.EventKey, func(tview.Primitive)) {
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(tview.Primitive)) {
d.logger.Sugar().Debugf("shelf create dialog event %v received", event)
if event.Key() == utils.CloseDialogKey.EventKey() && !d.dropdownHasFocus() {
d.cancelHandler()
return
}
if d.generalInfoPage.HasFocus() {
if handler := d.generalInfoPage.InputHandler(); handler != nil {
if event.Key() == utils.SwitchFocusKey.EventKey() {
d.setGeneralInfoNextFocus()
}
if event.Key() == tcell.KeyBacktab {
d.setGeneralInfoPrevFocus()
}
handler(event, setFocus)
return
}
}
if d.locationPage.HasFocus() {
if handler := d.locationPage.InputHandler(); handler != nil {
if event.Key() == utils.SwitchFocusKey.EventKey() {
d.setLocationNextFocus()
}
if event.Key() == tcell.KeyBacktab {
d.setLocationPrevFocus()
}
handler(event, setFocus)
return
}
}
if d.commentsPage.HasFocus() {
if handler := d.commentsPage.InputHandler(); handler != nil {
if event.Key() == utils.SwitchFocusKey.EventKey() {
d.setCommentsNextFocus()
}
if event.Key() == tcell.KeyBacktab {
d.setCommentsPrevFocus()
}
handler(event, setFocus)
return
}
}
if d.categories.HasFocus() {
if categoryHandler := d.categories.InputHandler(); categoryHandler != nil {
categoryHandler(event, setFocus)
return
}
}
if d.form.HasFocus() {
if formHandler := d.form.InputHandler(); formHandler != nil {
if event.Key() == tcell.KeyEnter {
enterButton := d.form.GetButton(d.form.GetButtonCount() - 1)
if enterButton.HasFocus() {
d.createHandler()
}
}
formHandler(event, setFocus)
return
}
}
})
}
func (d *ShelfCreateDialog) SetRect(x, y, width, height int) {
if width > shelfCreateDialogMaxWidth {
emptySpace := (width - shelfCreateDialogMaxWidth) / 2
x += emptySpace
width = shelfCreateDialogMaxWidth
}
if height > shelfCreateDialogHeight {
emptySpace := (height - shelfCreateDialogHeight) / 2
y += emptySpace
height = shelfCreateDialogHeight
}
d.Box.SetRect(x, y, width, height)
}
func (d *ShelfCreateDialog) Draw(screen tcell.Screen) {
if !d.display {
return
}
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
func (d *ShelfCreateDialog) SetCreateFunc(f func()) *ShelfCreateDialog {
d.createHandler = f
enterButton := d.form.GetButton(d.form.GetButtonCount() - 1)
enterButton.SetSelectedFunc(f)
return d
}
func (d *ShelfCreateDialog) SetCancelFunc(f func()) *ShelfCreateDialog {
d.cancelHandler = f
cancelButton := d.form.GetButton(d.form.GetButtonCount() - 2)
cancelButton.SetSelectedFunc(f)
return d
}
func (d *ShelfCreateDialog) dropdownHasFocus() bool {
return utils.CheckFocus(d.shelfBuildingField)
}
func (d *ShelfCreateDialog) SetShelf(a *types.ShelfLocation) {
d.shelf = a
}
func (d *ShelfCreateDialog) initData() {
// Get available buildings
buildingList, _ := d.client.RetrieveAllBuildings()
d.buildingList = buildingList
buildingOptions := []string{""}
for _, building := range d.buildingList {
buildingOptions = append(buildingOptions, building.Name) // In case we want to do custom formatting later
}
d.setActiveCategory(0)
// General category
if d.shelf == nil {
d.shelfNameField.SetText("")
// Location category
d.shelfBuildingField.SetOptions(buildingOptions, nil)
d.shelfRoomField.SetText("")
// Comments category
d.shelfDescriptionArea.SetText("", true)
} else {
d.shelfNameField.SetText(d.shelf.Name)
// Location category
d.shelfBuildingField.SetOptions(buildingOptions, nil)
if d.shelf.Building != nil {
d.shelfBuildingField.SetCurrentOption(findOption(buildingOptions, d.shelf.Building.Name))
}
d.shelfRoomField.SetText("")
// Comments category
d.shelfDescriptionArea.SetText("", true)
}
}
func (d *ShelfCreateDialog) setupLayout() {
bgColor := style.DialogBgColor
fgColor := style.DialogFgColor
d.categories.SetDynamicColors(true).
SetWrap(true).
SetTextAlign(tview.AlignLeft)
d.categories.SetBackgroundColor(fgColor)
d.categories.SetTextColor(bgColor)
d.categories.SetBorder(true)
d.categories.SetBorderColor(style.DialogSubBoxBorderColor)
d.categoryPages.SetBackgroundColor(bgColor)
d.categoryPages.SetBorder(true)
d.categoryPages.SetBorderColor(style.DialogSubBoxBorderColor)
d.setupGeneralInfoPageUI()
d.setupLocationPageUI()
d.setupCommentsPageUI()
activatedStyle := tcell.StyleDefault.
Background(style.ButtonSelectedBgColor).
Foreground(style.ButtonSelectedFgColor)
d.form.SetBackgroundColor(bgColor)
d.form.AddButton("Cancel", nil)
d.form.AddButton("Create", nil)
d.form.SetButtonsAlign(tview.AlignRight)
d.form.SetButtonBackgroundColor(style.ButtonBgColor)
d.form.SetButtonTextColor(style.ButtonFgColor)
d.form.SetButtonActivatedStyle(activatedStyle)
d.categoryPages.AddPage(d.createCategoryLabels[generalPageIndex], d.generalInfoPage, true, true)
d.categoryPages.AddPage(d.createCategoryLabels[locationPageIndex], d.locationPage, true, true)
d.categoryPages.AddPage(d.createCategoryLabels[commentPageIndex], d.commentsPage, true, true)
d.layout.SetBackgroundColor(bgColor)
d.layout.SetBorder(true)
d.layout.SetBorderColor(style.DialogBorderColor)
d.layout.SetTitle("SHELF CREATE")
d.layout.SetTitleColor(style.DialogFgColor)
_, layoutWidth := utils.AlignStringListWidth(d.createCategoryLabels)
layout := tview.NewFlex().SetDirection(tview.FlexColumn)
layout.AddItem(d.categories, layoutWidth+6, 0, true)
layout.AddItem(d.categoryPages, 0, 1, true)
layout.SetBackgroundColor(bgColor)
d.layout.SetDirection(tview.FlexRow)
d.layout.AddItem(layout, 0, 1, true)
d.layout.AddItem(d.form, dialogs.DialogFormHeight, 0, true)
}
func (d *ShelfCreateDialog) setupGeneralInfoPageUI() {
bgColor := style.DialogBgColor
inputFieldBgColor := style.InputFieldBgColor
pageLabelWidth := 14
d.shelfNameField.SetLabel("name:")
d.shelfNameField.SetLabelWidth(pageLabelWidth)
d.shelfNameField.SetLabelColor(bgColor)
d.shelfNameField.SetBackgroundColor(style.DialogFgColor)
d.shelfNameField.SetFieldBackgroundColor(inputFieldBgColor)
d.generalInfoPage.SetDirection(tview.FlexRow)
d.generalInfoPage.AddItem(d.shelfNameField, 1, 0, true)
d.generalInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
}
func (d *ShelfCreateDialog) setupLocationPageUI() {
bgColor := style.DialogBgColor
inputFieldBgColor := style.InputFieldBgColor
pageLabelWidth := 12
ddUnselectedStyle := style.DropdownUnselected
ddSelectedStyle := style.DropdownSelected
d.shelfRoomField.SetLabel("room:")
d.shelfRoomField.SetLabelWidth(pageLabelWidth)
d.shelfRoomField.SetLabelColor(bgColor)
d.shelfRoomField.SetBackgroundColor(style.DialogFgColor)
d.shelfRoomField.SetFieldBackgroundColor(inputFieldBgColor)
d.shelfBuildingField.SetLabel("building:")
d.shelfBuildingField.SetLabelWidth(pageLabelWidth)
d.shelfBuildingField.SetBackgroundColor(bgColor)
d.shelfBuildingField.SetLabelColor(style.DialogFgColor)
d.shelfBuildingField.SetListStyles(ddUnselectedStyle, ddSelectedStyle)
d.shelfBuildingField.SetFieldBackgroundColor(inputFieldBgColor)
d.locationPage.SetDirection(tview.FlexRow)
d.locationPage.AddItem(d.shelfRoomField, 1, 0, true)
d.locationPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.locationPage.AddItem(d.shelfBuildingField, 1, 0, true)
d.locationPage.SetBackgroundColor(bgColor)
}
func (d *ShelfCreateDialog) setupCommentsPageUI() {
bgColor := style.DialogBgColor
inputFieldBgColor := style.InputFieldBgColor
pageLabelWidth := 12
d.shelfDescriptionArea.SetLabel("comments:")
d.shelfDescriptionArea.SetLabelWidth(pageLabelWidth)
d.shelfDescriptionArea.SetBackgroundColor(inputFieldBgColor)
d.shelfDescriptionArea.SetLabelStyle(tcell.StyleDefault.Foreground(style.DialogFgColor).Background(bgColor))
d.shelfDescriptionArea.SetWrap(true)
d.commentsPage.AddItem(d.shelfDescriptionArea, 0, 1, true)
d.commentsPage.SetBackgroundColor(bgColor)
}
func (d *ShelfCreateDialog) setActiveCategory(idx int) {
fgColor := style.ButtonSelectedFgColor
bgBolor := style.ButtonSelectedBgColor
ctgTextColor := style.GetColorHex(fgColor)
ctgBgColor := style.GetColorHex(bgBolor)
d.activePageIndex = idx
d.categories.Clear()
alignedList, _ := utils.AlignStringListWidth(d.createCategoryLabels)
var ctgList []string
for i, lbl := range alignedList {
if i == idx {
ctgList = append(ctgList, fmt.Sprintf("[%s:%s:b]-> %s ", ctgTextColor, ctgBgColor, lbl))
continue
}
ctgList = append(ctgList, fmt.Sprintf("[-:-:-] %s ", lbl))
}
d.categories.SetText(strings.Join(ctgList, "\n"))
d.categoryPages.SwitchToPage(d.createCategoryLabels[idx])
}
func (d *ShelfCreateDialog) nextCategory() {
activePage := d.activePageIndex
if d.activePageIndex < len(d.createCategoryLabels)-1 {
activePage++
d.setActiveCategory(activePage)
return
}
d.setActiveCategory(0)
}
func (d *ShelfCreateDialog) previousCategory() {
activePage := d.activePageIndex
if d.activePageIndex > 0 {
activePage--
d.setActiveCategory(activePage)
return
}
d.setActiveCategory(len(d.createCategoryLabels) - 1)
}
func (d *ShelfCreateDialog) initCustomInputHandlers() {
d.shelfBuildingField.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
event = utils.ParseKeyEventKey(d.logger, event)
return event
})
}
func (d *ShelfCreateDialog) setGeneralInfoNextFocus() {
// Name -> Quantity -> Length -> Manufacturer -> Model -> Price
if d.shelfNameField.HasFocus() {
d.focusElement = createShelfFormFocus
return
}
d.focusElement = createShelfFormFocus
}
func (d *ShelfCreateDialog) setGeneralInfoPrevFocus() {
if d.shelfNameField.HasFocus() {
d.focusElement = createCategoriesFocus
return
}
d.focusElement = createShelfNameFieldFocus
}
func (d *ShelfCreateDialog) setLocationNextFocus() {
// Category -> Shelf
if d.shelfRoomField.HasFocus() {
d.focusElement = createShelfBuildingFieldFocus
return
}
d.focusElement = createShelfFormFocus
}
func (d *ShelfCreateDialog) setLocationPrevFocus() {
if d.shelfRoomField.HasFocus() {
d.focusElement = createCategoriesFocus
return
}
if d.shelfBuildingField.HasFocus() {
d.focusElement = createShelfRoomFieldFocus
return
}
d.focusElement = createShelfBuildingFieldFocus
}
func (d *ShelfCreateDialog) setCommentsNextFocus() {
d.focusElement = createShelfFormFocus
}
func (d *ShelfCreateDialog) setCommentsPrevFocus() {
d.focusElement = createCategoriesFocus
}
func (d *ShelfCreateDialog) CreateShelfOptions() types.CreateShelfRequest {
var (
buildingID *uint64
)
selectedBuildingIndex, _ := d.shelfBuildingField.GetCurrentOption()
if len(d.buildingList) > 0 && selectedBuildingIndex > 0 {
buildingID = &d.buildingList[selectedBuildingIndex-1].ID
}
req := types.CreateShelfRequest{
Name: d.shelfNameField.GetText(),
RoomNumber: d.shelfRoomField.GetText(),
Description: d.shelfDescriptionArea.GetText(),
BuildingID: buildingID,
}
return req
}

+ 604
- 0
internal/ui/shelves/shlvdialogs/edit.go View File

@ -0,0 +1,604 @@
package shlvdialogs
import (
"fmt"
"slices"
"strings"
"git.brettb.xyz/goinv/client/internal/api"
"git.brettb.xyz/goinv/client/internal/types"
"git.brettb.xyz/goinv/client/internal/ui/dialogs"
"git.brettb.xyz/goinv/client/internal/ui/style"
"git.brettb.xyz/goinv/client/internal/ui/utils"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"go.uber.org/zap"
)
const (
shelfEditDialogMaxWidth = 100
shelfEditDialogHeight = 17
)
const (
editShelfFormFocus = 0 + iota
editCategoriesFocus
editCategoryPagesFocus
editShelfNameFieldFocus
editShelfRoomFieldFocus
editShelfBuildingFieldFocus
editShelfDescriptionFieldFocus
)
type ShelfEditDialog struct {
*tview.Box
layout *tview.Flex
editCategoryLabels []string
categories *tview.TextView
categoryPages *tview.Pages
generalInfoPage *tview.Flex
locationPage *tview.Flex
commentsPage *tview.Flex
form *tview.Form
display bool
activePageIndex int
focusElement int
logger *zap.Logger
client *api.APIClient
shelf *types.ShelfLocation
buildingList []*types.Building
shelfNameField *tview.InputField
shelfRoomField *tview.InputField
shelfDescriptionArea *tview.TextArea
shelfBuildingField *tview.DropDown
focusMap map[int]tview.Primitive
cancelHandler func()
editHandler func()
}
func NewShelfEditDialog(logger *zap.Logger, client *api.APIClient) *ShelfEditDialog {
editDialog := ShelfEditDialog{
Box: tview.NewBox(),
layout: tview.NewFlex(),
editCategoryLabels: []string{
"General",
"Location",
"Comments",
},
categories: tview.NewTextView(),
categoryPages: tview.NewPages(),
generalInfoPage: tview.NewFlex(),
locationPage: tview.NewFlex(),
commentsPage: tview.NewFlex(),
form: tview.NewForm(),
display: false,
activePageIndex: 0,
logger: logger,
client: client,
shelfNameField: tview.NewInputField(),
shelfRoomField: tview.NewInputField(),
shelfBuildingField: tview.NewDropDown(),
shelfDescriptionArea: tview.NewTextArea(),
}
editDialog.focusMap = map[int]tview.Primitive{
editShelfNameFieldFocus: editDialog.shelfNameField,
editShelfRoomFieldFocus: editDialog.shelfRoomField,
editShelfBuildingFieldFocus: editDialog.shelfBuildingField,
editShelfDescriptionFieldFocus: editDialog.shelfDescriptionArea,
}
editDialog.setupLayout()
editDialog.setActiveCategory(0)
editDialog.initCustomInputHandlers()
return &editDialog
}
func (d *ShelfEditDialog) Display() {
d.display = true
d.initData()
d.focusElement = editCategoryPagesFocus
}
func (d *ShelfEditDialog) IsDisplay() bool {
return d.display
}
func (d *ShelfEditDialog) Hide() {
d.display = false
}
func (d *ShelfEditDialog) HasFocus() bool {
return utils.CheckFocus(d.categories, d.categoryPages, d.Box, d.form)
}
func (d *ShelfEditDialog) Focus(delegate func(tview.Primitive)) {
switch d.focusElement {
case editShelfFormFocus:
button := d.form.GetButton(d.form.GetButtonCount() - 1)
button.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == utils.SwitchFocusKey.EventKey() {
d.focusElement = editCategoriesFocus
d.Focus(delegate)
d.form.SetFocus(0)
return nil
}
if event.Key() == tcell.KeyEnter {
return nil
}
return event
})
delegate(d.form)
case editCategoriesFocus:
d.categories.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == utils.SwitchFocusKey.EventKey() {
d.focusElement = editCategoryPagesFocus
d.Focus(delegate)
return nil
}
if event.Key() == tcell.KeyBacktab {
d.focusElement = editShelfFormFocus
d.Focus(delegate)
return nil
}
event = utils.ParseKeyEventKey(d.logger, event)
if event.Key() == tcell.KeyDown {
d.nextCategory()
}
if event.Key() == tcell.KeyUp {
d.previousCategory()
}
return event
})
delegate(d.categories)
case editCategoryPagesFocus:
delegate(d.categoryPages)
default:
delegate(d.focusMap[d.focusElement])
}
}
func (d *ShelfEditDialog) InputHandler() func(*tcell.EventKey, func(tview.Primitive)) {
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(tview.Primitive)) {
d.logger.Sugar().Debugf("shelf edit dialog event %v received", event)
if event.Key() == utils.CloseDialogKey.EventKey() && !d.dropdownHasFocus() {
d.cancelHandler()
return
}
if d.generalInfoPage.HasFocus() {
if handler := d.generalInfoPage.InputHandler(); handler != nil {
if event.Key() == utils.SwitchFocusKey.EventKey() {
d.setGeneralInfoNextFocus()
}
if event.Key() == tcell.KeyBacktab {
d.setGeneralInfoPrevFocus()
}
handler(event, setFocus)
return
}
}
if d.locationPage.HasFocus() {
if handler := d.locationPage.InputHandler(); handler != nil {
if event.Key() == utils.SwitchFocusKey.EventKey() {
d.setLocationNextFocus()
}
if event.Key() == tcell.KeyBacktab {
d.setLocationPrevFocus()
}
handler(event, setFocus)
return
}
}
if d.commentsPage.HasFocus() {
if handler := d.commentsPage.InputHandler(); handler != nil {
if event.Key() == utils.SwitchFocusKey.EventKey() {
d.setCommentsNextFocus()
}
if event.Key() == tcell.KeyBacktab {
d.setCommentsPrevFocus()
}
handler(event, setFocus)
return
}
}
if d.categories.HasFocus() {
if categoryHandler := d.categories.InputHandler(); categoryHandler != nil {
categoryHandler(event, setFocus)
return
}
}
if d.form.HasFocus() {
if formHandler := d.form.InputHandler(); formHandler != nil {
if event.Key() == tcell.KeyEnter {
enterButton := d.form.GetButton(d.form.GetButtonCount() - 1)
if enterButton.HasFocus() {
d.editHandler()
}
}
formHandler(event, setFocus)
return
}
}
})
}
func (d *ShelfEditDialog) SetRect(x, y, width, height int) {
if width > shelfEditDialogMaxWidth {
emptySpace := (width - shelfEditDialogMaxWidth) / 2
x += emptySpace
width = shelfEditDialogMaxWidth
}
if height > shelfEditDialogHeight {
emptySpace := (height - shelfEditDialogHeight) / 2
y += emptySpace
height = shelfEditDialogHeight
}
d.Box.SetRect(x, y, width, height)
}
func (d *ShelfEditDialog) Draw(screen tcell.Screen) {
if !d.display {
return
}
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
func (d *ShelfEditDialog) SetEditFunc(f func()) *ShelfEditDialog {
d.editHandler = f
enterButton := d.form.GetButton(d.form.GetButtonCount() - 1)
enterButton.SetSelectedFunc(f)
return d
}
func (d *ShelfEditDialog) SetCancelFunc(f func()) *ShelfEditDialog {
d.cancelHandler = f
cancelButton := d.form.GetButton(d.form.GetButtonCount() - 2)
cancelButton.SetSelectedFunc(f)
return d
}
func (d *ShelfEditDialog) dropdownHasFocus() bool {
return utils.CheckFocus(d.shelfBuildingField)
}
func (d *ShelfEditDialog) SetShelf(a *types.ShelfLocation) {
d.shelf = a
}
func (d *ShelfEditDialog) initData() {
// Get available buildings
buildingList, _ := d.client.RetrieveAllBuildings()
d.buildingList = buildingList
buildingOptions := []string{""}
for _, building := range d.buildingList {
buildingOptions = append(buildingOptions, building.Name) // In case we want to do custom formatting later
}
d.setActiveCategory(0)
// General category
if d.shelf == nil {
d.shelfNameField.SetText("")
// Location category
d.shelfBuildingField.SetOptions(buildingOptions, nil)
d.shelfRoomField.SetText("")
// Comments category
d.shelfDescriptionArea.SetText("", true)
} else {
d.shelfNameField.SetText(d.shelf.Name)
// Location category
d.shelfBuildingField.SetOptions(buildingOptions, nil)
if d.shelf.Building != nil {
d.shelfBuildingField.SetCurrentOption(findOption(buildingOptions, d.shelf.Building.Name))
}
d.shelfRoomField.SetText("")
// Comments category
d.shelfDescriptionArea.SetText("", true)
}
}
func findOption(options []string, search string) int {
return slices.Index(options, search)
}
func (d *ShelfEditDialog) setupLayout() {
bgColor := style.DialogBgColor
fgColor := style.DialogFgColor
d.categories.SetDynamicColors(true).
SetWrap(true).
SetTextAlign(tview.AlignLeft)
d.categories.SetBackgroundColor(fgColor)
d.categories.SetTextColor(bgColor)
d.categories.SetBorder(true)
d.categories.SetBorderColor(style.DialogSubBoxBorderColor)
d.categoryPages.SetBackgroundColor(bgColor)
d.categoryPages.SetBorder(true)
d.categoryPages.SetBorderColor(style.DialogSubBoxBorderColor)
d.setupGeneralInfoPageUI()
d.setupLocationPageUI()
d.setupCommentsPageUI()
activatedStyle := tcell.StyleDefault.
Background(style.ButtonSelectedBgColor).
Foreground(style.ButtonSelectedFgColor)
d.form.SetBackgroundColor(bgColor)
d.form.AddButton("Cancel", nil)
d.form.AddButton("Edit", nil)
d.form.SetButtonsAlign(tview.AlignRight)
d.form.SetButtonBackgroundColor(style.ButtonBgColor)
d.form.SetButtonTextColor(style.ButtonFgColor)
d.form.SetButtonActivatedStyle(activatedStyle)
d.categoryPages.AddPage(d.editCategoryLabels[generalPageIndex], d.generalInfoPage, true, true)
d.categoryPages.AddPage(d.editCategoryLabels[locationPageIndex], d.locationPage, true, true)
d.categoryPages.AddPage(d.editCategoryLabels[commentPageIndex], d.commentsPage, true, true)
d.layout.SetBackgroundColor(bgColor)
d.layout.SetBorder(true)
d.layout.SetBorderColor(style.DialogBorderColor)
d.layout.SetTitle("SHELF EDIT")
d.layout.SetTitleColor(style.DialogFgColor)
_, layoutWidth := utils.AlignStringListWidth(d.editCategoryLabels)
layout := tview.NewFlex().SetDirection(tview.FlexColumn)
layout.AddItem(d.categories, layoutWidth+6, 0, true)
layout.AddItem(d.categoryPages, 0, 1, true)
layout.SetBackgroundColor(bgColor)
d.layout.SetDirection(tview.FlexRow)
d.layout.AddItem(layout, 0, 1, true)
d.layout.AddItem(d.form, dialogs.DialogFormHeight, 0, true)
}
func (d *ShelfEditDialog) setupGeneralInfoPageUI() {
bgColor := style.DialogBgColor
inputFieldBgColor := style.InputFieldBgColor
pageLabelWidth := 14
d.shelfNameField.SetLabel("name:")
d.shelfNameField.SetLabelWidth(pageLabelWidth)
d.shelfNameField.SetLabelColor(bgColor)
d.shelfNameField.SetBackgroundColor(style.DialogFgColor)
d.shelfNameField.SetFieldBackgroundColor(inputFieldBgColor)
d.generalInfoPage.SetDirection(tview.FlexRow)
d.generalInfoPage.AddItem(d.shelfNameField, 1, 0, true)
d.generalInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
}
func (d *ShelfEditDialog) setupLocationPageUI() {
bgColor := style.DialogBgColor
inputFieldBgColor := style.InputFieldBgColor
pageLabelWidth := 12
ddUnselectedStyle := style.DropdownUnselected
ddSelectedStyle := style.DropdownSelected
d.shelfRoomField.SetLabel("room:")
d.shelfRoomField.SetLabelWidth(pageLabelWidth)
d.shelfRoomField.SetLabelColor(bgColor)
d.shelfRoomField.SetBackgroundColor(style.DialogFgColor)
d.shelfRoomField.SetFieldBackgroundColor(inputFieldBgColor)
d.shelfBuildingField.SetLabel("building:")
d.shelfBuildingField.SetLabelWidth(pageLabelWidth)
d.shelfBuildingField.SetBackgroundColor(bgColor)
d.shelfBuildingField.SetLabelColor(style.DialogFgColor)
d.shelfBuildingField.SetListStyles(ddUnselectedStyle, ddSelectedStyle)
d.shelfBuildingField.SetFieldBackgroundColor(inputFieldBgColor)
d.locationPage.SetDirection(tview.FlexRow)
d.locationPage.AddItem(d.shelfRoomField, 1, 0, true)
d.locationPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.locationPage.AddItem(d.shelfBuildingField, 1, 0, true)
d.locationPage.SetBackgroundColor(bgColor)
}
func (d *ShelfEditDialog) setupCommentsPageUI() {
bgColor := style.DialogBgColor
inputFieldBgColor := style.InputFieldBgColor
pageLabelWidth := 12
d.shelfDescriptionArea.SetLabel("comments:")
d.shelfDescriptionArea.SetLabelWidth(pageLabelWidth)
d.shelfDescriptionArea.SetBackgroundColor(inputFieldBgColor)
d.shelfDescriptionArea.SetLabelStyle(tcell.StyleDefault.Foreground(style.DialogFgColor).Background(bgColor))
d.shelfDescriptionArea.SetWrap(true)
d.commentsPage.AddItem(d.shelfDescriptionArea, 0, 1, true)
d.commentsPage.SetBackgroundColor(bgColor)
}
func (d *ShelfEditDialog) setActiveCategory(idx int) {
fgColor := style.ButtonSelectedFgColor
bgBolor := style.ButtonSelectedBgColor
ctgTextColor := style.GetColorHex(fgColor)
ctgBgColor := style.GetColorHex(bgBolor)
d.activePageIndex = idx
d.categories.Clear()
alignedList, _ := utils.AlignStringListWidth(d.editCategoryLabels)
var ctgList []string
for i, lbl := range alignedList {
if i == idx {
ctgList = append(ctgList, fmt.Sprintf("[%s:%s:b]-> %s ", ctgTextColor, ctgBgColor, lbl))
continue
}
ctgList = append(ctgList, fmt.Sprintf("[-:-:-] %s ", lbl))
}
d.categories.SetText(strings.Join(ctgList, "\n"))
d.categoryPages.SwitchToPage(d.editCategoryLabels[idx])
}
func (d *ShelfEditDialog) nextCategory() {
activePage := d.activePageIndex
if d.activePageIndex < len(d.editCategoryLabels)-1 {
activePage++
d.setActiveCategory(activePage)
return
}
d.setActiveCategory(0)
}
func (d *ShelfEditDialog) previousCategory() {
activePage := d.activePageIndex
if d.activePageIndex > 0 {
activePage--
d.setActiveCategory(activePage)
return
}
d.setActiveCategory(len(d.editCategoryLabels) - 1)
}
func (d *ShelfEditDialog) initCustomInputHandlers() {
d.shelfBuildingField.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
event = utils.ParseKeyEventKey(d.logger, event)
return event
})
}
func (d *ShelfEditDialog) setGeneralInfoNextFocus() {
// Name -> Quantity -> Length -> Manufacturer -> Model -> Price
if d.shelfNameField.HasFocus() {
d.focusElement = editShelfFormFocus
return
}
d.focusElement = editShelfFormFocus
}
func (d *ShelfEditDialog) setGeneralInfoPrevFocus() {
if d.shelfNameField.HasFocus() {
d.focusElement = createCategoriesFocus
return
}
d.focusElement = editShelfNameFieldFocus
}
func (d *ShelfEditDialog) setLocationNextFocus() {
// Category -> Shelf
if d.shelfRoomField.HasFocus() {
d.focusElement = editShelfBuildingFieldFocus
return
}
d.focusElement = editShelfFormFocus
}
func (d *ShelfEditDialog) setLocationPrevFocus() {
if d.shelfRoomField.HasFocus() {
d.focusElement = createCategoriesFocus
return
}
if d.shelfBuildingField.HasFocus() {
d.focusElement = editShelfRoomFieldFocus
return
}
d.focusElement = editShelfBuildingFieldFocus
}
func (d *ShelfEditDialog) setCommentsNextFocus() {
d.focusElement = editShelfFormFocus
}
func (d *ShelfEditDialog) setCommentsPrevFocus() {
d.focusElement = createCategoriesFocus
}
func (d *ShelfEditDialog) EditShelfOptions() types.CreateShelfRequest {
var (
buildingID *uint64
)
selectedBuildingIndex, _ := d.shelfBuildingField.GetCurrentOption()
if len(d.buildingList) > 0 && selectedBuildingIndex > 0 {
buildingID = &d.buildingList[selectedBuildingIndex-1].ID
}
req := types.CreateShelfRequest{
Name: d.shelfNameField.GetText(),
RoomNumber: d.shelfRoomField.GetText(),
Description: d.shelfDescriptionArea.GetText(),
BuildingID: buildingID,
}
return req
}

+ 11
- 5
internal/ui/utils/keys.go View File

@ -38,12 +38,12 @@ var (
}
CloseDialogKey = uiKeyInfo{
Key: tcell.KeyEsc,
KeyLabel: "Esc",
KeyLabel: "esc",
KeyDesc: "close the active dialog",
}
SwitchFocusKey = uiKeyInfo{
Key: tcell.KeyTab,
KeyLabel: "Tab",
KeyLabel: "tab",
KeyDesc: "switch between widgets",
}
ArrowUpKey = uiKeyInfo{
@ -58,17 +58,17 @@ var (
}
ArrowLeftKey = uiKeyInfo{
Key: tcell.KeyLeft,
KeyLabel: "Arrow Left",
KeyLabel: "arrow Left",
KeyDesc: "previous screen",
}
ArrowRightKey = uiKeyInfo{
Key: tcell.KeyRight,
KeyLabel: "Arrow Right",
KeyLabel: "arrow Right",
KeyDesc: "next screen",
}
AppExitKey = uiKeyInfo{
Key: tcell.KeyCtrlC,
KeyLabel: "Ctrl+c",
KeyLabel: "ctrl+c",
KeyDesc: "exit application",
}
HelpScreenKey = uiKeyInfo{
@ -96,6 +96,11 @@ var (
KeyLabel: "F5",
KeyDesc: "display buildings screen",
}
GroupsScreenKey = uiKeyInfo{
Key: tcell.KeyF6,
KeyLabel: "F6",
KeyDesc: "display groups screen",
}
)
var UIKeyBindings = []uiKeyInfo{
@ -116,6 +121,7 @@ var UIKeyBindings = []uiKeyInfo{
ShelvesScreenKey,
CategoriesScreenKey,
BuildingsScreenKey,
GroupsScreenKey,
}
type uiKeyInfo struct {


Loading…
Cancel
Save