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
|
|
}
|