feat: create dialog
This commit is contained in:
@@ -14,9 +14,11 @@ import (
|
||||
var envPrefix = "GOINV_CLI_"
|
||||
|
||||
func main() {
|
||||
logger, _ := zap.NewProduction()
|
||||
logger, err := zap.NewProduction()
|
||||
defer logger.Sync()
|
||||
|
||||
logger.Info("loading configuration")
|
||||
|
||||
config.LoadConfig(file.Provider("config.json"), json.Parser())
|
||||
config.LoadConfig(env.Provider(envPrefix, ".", func(s string) string {
|
||||
return strings.Replace(strings.ToLower(
|
||||
@@ -26,7 +28,7 @@ func main() {
|
||||
|
||||
var cfg config.Config
|
||||
|
||||
err := config.Unmarshal(&cfg)
|
||||
err = config.Unmarshal(&cfg)
|
||||
if err != nil {
|
||||
logger.Panic("could not load config", zap.Error(err))
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func NewAPIClient(host string) *APIClient {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *APIClient) retrieveSingleAsset(r *http.Request) (*types.AssetResponse, error) {
|
||||
func makeRequest[T any](c *APIClient, r *http.Request) (*T, error) {
|
||||
r.Header.Set("User-Agent", userAgent)
|
||||
//r.Header.Set("Authorization", "Bearer "+c.Token)
|
||||
r.Header.Set("Accept", "application/json")
|
||||
@@ -41,57 +41,7 @@ func (c *APIClient) retrieveSingleAsset(r *http.Request) (*types.AssetResponse,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := unmarshal[types.AssetResponse](body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s", "An unexpected response was received")
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *APIClient) retrieveMultipleAssets(r *http.Request) (*types.MultipleAssetsResponse, error) {
|
||||
r.Header.Set("User-Agent", userAgent)
|
||||
//r.Header.Set("Authorization", "Bearer "+c.Token)
|
||||
r.Header.Set("Accept", "application/json")
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
|
||||
httpCli := http.Client{}
|
||||
httpResp, err := httpCli.Do(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer httpResp.Body.Close()
|
||||
body, err := io.ReadAll(httpResp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := unmarshal[types.MultipleAssetsResponse](body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s", "An unexpected response was received")
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *APIClient) retrieveMultipleShelves(r *http.Request) (*types.MultipleShelfResponse, error) {
|
||||
r.Header.Set("User-Agent", userAgent)
|
||||
//r.Header.Set("Authorization", "Bearer "+c.Token)
|
||||
r.Header.Set("Accept", "application/json")
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
|
||||
httpCli := http.Client{}
|
||||
httpResp, err := httpCli.Do(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer httpResp.Body.Close()
|
||||
body, err := io.ReadAll(httpResp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := unmarshal[types.MultipleShelfResponse](body)
|
||||
resp, err := unmarshal[T](body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s", "An unexpected response was received")
|
||||
}
|
||||
@@ -102,7 +52,7 @@ func (c *APIClient) RetrieveAssetByID(idNumber string) (*types.Asset, error) {
|
||||
url := fmt.Sprintf("%s/assets/%s", c.Host, idNumber)
|
||||
req, _ := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
|
||||
|
||||
resp, err := c.retrieveSingleAsset(req)
|
||||
resp, err := makeRequest[types.AssetResponse](c, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -120,7 +70,7 @@ func (c *APIClient) CreateAsset(request types.CreateAssetRequest) (*types.Asset,
|
||||
|
||||
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body))
|
||||
|
||||
resp, err := c.retrieveSingleAsset(req)
|
||||
resp, err := makeRequest[types.AssetResponse](c, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -132,7 +82,7 @@ func (c *APIClient) RetrieveAllAssets() ([]*types.Asset, error) {
|
||||
url := fmt.Sprintf("%s/assets", c.Host)
|
||||
req, _ := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
|
||||
|
||||
resp, err := c.retrieveMultipleAssets(req)
|
||||
resp, err := makeRequest[types.MultipleAssetsResponse](c, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -144,7 +94,7 @@ func (c *APIClient) DeleteAssetByID(idNumber string) (*types.Asset, error) {
|
||||
url := fmt.Sprintf("%s/assets/%s", c.Host, idNumber)
|
||||
req, _ := http.NewRequest("DELETE", url, bytes.NewBuffer([]byte{}))
|
||||
|
||||
resp, err := c.retrieveSingleAsset(req)
|
||||
resp, err := makeRequest[types.AssetResponse](c, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -156,10 +106,22 @@ func (c *APIClient) RetrieveAllShelves() ([]*types.ShelfLocation, error) {
|
||||
url := fmt.Sprintf("%s/shelves", c.Host)
|
||||
req, _ := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
|
||||
|
||||
resp, err := c.retrieveMultipleShelves(req)
|
||||
resp, err := makeRequest[types.MultipleShelfResponse](c, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.ShelfLocations, nil
|
||||
}
|
||||
|
||||
func (c *APIClient) RetrieveAllCategories() ([]*types.Category, error) {
|
||||
url := fmt.Sprintf("%s/categories", c.Host)
|
||||
req, _ := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
|
||||
|
||||
resp, err := makeRequest[types.MultipleCategoryResponse](c, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Categories, nil
|
||||
}
|
||||
|
||||
23
internal/types/api.go
Normal file
23
internal/types/api.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package types
|
||||
|
||||
type Response struct {
|
||||
HTTPStatusCode int `json:"status"`
|
||||
}
|
||||
|
||||
type APIVersion struct {
|
||||
Major int `json:"major"`
|
||||
Minor int `json:"minor"`
|
||||
Patch int `json:"patch"`
|
||||
}
|
||||
|
||||
type IndexResponse struct {
|
||||
*Response
|
||||
Version APIVersion `json:"version"`
|
||||
}
|
||||
|
||||
type APIError struct {
|
||||
*Response
|
||||
Err error `json:"-"`
|
||||
|
||||
Messages []string `json:"messages"`
|
||||
}
|
||||
@@ -5,10 +5,12 @@ import (
|
||||
)
|
||||
|
||||
type AssetResponse struct {
|
||||
*Response
|
||||
Asset *Asset `json:"asset"`
|
||||
}
|
||||
|
||||
type MultipleAssetsResponse struct {
|
||||
*Response
|
||||
Assets []*Asset `json:"assets"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
@@ -9,3 +9,14 @@ type Category struct {
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt *time.Time `json:"deleted_at,omitempty"`
|
||||
}
|
||||
|
||||
type CategoryResponse struct {
|
||||
*Response
|
||||
Category *Category `json:"category"`
|
||||
}
|
||||
|
||||
type MultipleCategoryResponse struct {
|
||||
*Response
|
||||
Categories []*Category `json:"categories"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ package types
|
||||
import "time"
|
||||
|
||||
type ShelfResponse struct {
|
||||
*Response
|
||||
ShelfLocation *ShelfLocation `json:"shelf"`
|
||||
}
|
||||
|
||||
type MultipleShelfResponse struct {
|
||||
*Response
|
||||
ShelfLocations []*ShelfLocation `json:"shelves"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"git.brettb.xyz/goinv/client/internal/ui/assets/astdialogs"
|
||||
"sync"
|
||||
|
||||
"git.brettb.xyz/goinv/client/internal/api"
|
||||
@@ -32,6 +33,7 @@ type Assets struct {
|
||||
errorDialog *dialogs.ErrorDialog
|
||||
progressDialog *dialogs.ProgressDialog
|
||||
messageDialog *dialogs.MessageDialog
|
||||
createDialog *astdialogs.AssetCreateDialog
|
||||
allDialogs []dialogs.Dialog
|
||||
confirmData string
|
||||
assetListFunc func() ([]types.Asset, error)
|
||||
@@ -119,6 +121,12 @@ func NewAssets(logger *zap.Logger, client *api.APIClient) *Assets {
|
||||
assets.messageDialog.Hide()
|
||||
})
|
||||
|
||||
assets.createDialog.SetCancelFunc(func() {
|
||||
assets.createDialog.Hide()
|
||||
}).SetCreateFunc(func() {
|
||||
assets.createDialog.Hide()
|
||||
})
|
||||
|
||||
assets.SetAssetListFunc(func() ([]types.Asset, error) {
|
||||
if asp, err := assets.client.RetrieveAllAssets(); err != nil {
|
||||
return nil, err
|
||||
|
||||
614
internal/ui/assets/astdialogs/create.go
Normal file
614
internal/ui/assets/astdialogs/create.go
Normal file
@@ -0,0 +1,614 @@
|
||||
package astdialogs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
assetCreateDialogMaxWidth = 100
|
||||
assetCreateDialogHeight = 10
|
||||
)
|
||||
|
||||
const (
|
||||
createAssetFormFocus = 0 + iota
|
||||
createCategoriesFocus
|
||||
createCategoryPagesFocus
|
||||
createAssetNameFieldFocus
|
||||
createAssetQuantityFieldFocus
|
||||
createAssetLengthFieldFocus
|
||||
createAssetManufacturerFieldFocus
|
||||
createAssetModelFieldFocus
|
||||
createAssetPriceFieldFocus
|
||||
createAssetCategoryFieldFocus
|
||||
createAssetShelfFieldFocus
|
||||
createAssetCommentFieldFocus
|
||||
)
|
||||
|
||||
const (
|
||||
generalPageIndex = 0 + iota
|
||||
locationPageIndex
|
||||
commentPageIndex
|
||||
)
|
||||
|
||||
type AssetCreateDialog 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
|
||||
|
||||
categoryList []*types.Category
|
||||
shelfList []*types.ShelfLocation
|
||||
|
||||
assetNameField *tview.InputField
|
||||
assetQuantityField *tview.InputField
|
||||
assetLengthField *tview.InputField
|
||||
assetManufacturerField *tview.InputField
|
||||
assetModelField *tview.InputField
|
||||
assetPriceField *tview.InputField
|
||||
assetCategoryField *tview.DropDown
|
||||
assetShelfField *tview.DropDown
|
||||
assetCommentsArea *tview.TextArea
|
||||
|
||||
focusMap map[int]tview.Primitive
|
||||
|
||||
cancelHandler func()
|
||||
createHandler func()
|
||||
}
|
||||
|
||||
func NewAssetCreateDialog(logger *zap.Logger, client *api.APIClient) *AssetCreateDialog {
|
||||
createDialog := AssetCreateDialog{
|
||||
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,
|
||||
assetNameField: tview.NewInputField(),
|
||||
assetQuantityField: tview.NewInputField(),
|
||||
assetLengthField: tview.NewInputField(),
|
||||
assetManufacturerField: tview.NewInputField(),
|
||||
assetModelField: tview.NewInputField(),
|
||||
assetPriceField: tview.NewInputField(),
|
||||
assetCategoryField: tview.NewDropDown(),
|
||||
assetShelfField: tview.NewDropDown(),
|
||||
assetCommentsArea: tview.NewTextArea(),
|
||||
}
|
||||
|
||||
createDialog.focusMap = map[int]tview.Primitive{
|
||||
createAssetNameFieldFocus: createDialog.assetNameField,
|
||||
createAssetQuantityFieldFocus: createDialog.assetQuantityField,
|
||||
createAssetLengthFieldFocus: createDialog.assetLengthField,
|
||||
createAssetManufacturerFieldFocus: createDialog.assetManufacturerField,
|
||||
createAssetModelFieldFocus: createDialog.assetModelField,
|
||||
createAssetPriceFieldFocus: createDialog.assetPriceField,
|
||||
createAssetCategoryFieldFocus: createDialog.assetCategoryField,
|
||||
createAssetShelfFieldFocus: createDialog.assetShelfField,
|
||||
createAssetCommentFieldFocus: createDialog.assetCommentsArea,
|
||||
}
|
||||
|
||||
createDialog.setupLayout()
|
||||
createDialog.setActiveCategory(0)
|
||||
createDialog.initCustomInputHandlers()
|
||||
|
||||
return &createDialog
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) Display() {
|
||||
d.display = true
|
||||
d.initData()
|
||||
d.focusElement = createCategoryPagesFocus
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) IsDisplay() bool {
|
||||
return d.display
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) Hide() {
|
||||
d.display = false
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) HasFocus() bool {
|
||||
return utils.CheckFocus(d.categories, d.categoryPages, d.Box, d.form)
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) Focus(delegate func(tview.Primitive)) {
|
||||
switch d.focusElement {
|
||||
case createAssetFormFocus:
|
||||
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
|
||||
}
|
||||
|
||||
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 *AssetCreateDialog) InputHandler() func(*tcell.EventKey, func(tview.Primitive)) {
|
||||
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(tview.Primitive)) {
|
||||
d.logger.Sugar().Debugf("asset 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()
|
||||
}
|
||||
|
||||
handler(event, setFocus)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if d.locationPage.HasFocus() {
|
||||
if handler := d.locationPage.InputHandler(); handler != nil {
|
||||
if event.Key() == utils.SwitchFocusKey.EventKey() {
|
||||
d.setLocationNextFocus()
|
||||
}
|
||||
|
||||
handler(event, setFocus)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if d.commentsPage.HasFocus() {
|
||||
if handler := d.commentsPage.InputHandler(); handler != nil {
|
||||
if event.Key() == utils.SwitchFocusKey.EventKey() {
|
||||
d.setCommentsNextFocus()
|
||||
}
|
||||
|
||||
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 *AssetCreateDialog) SetRect(x, y, width, height int) {
|
||||
if width > assetCreateDialogMaxWidth {
|
||||
emptySpace := (width - assetCreateDialogMaxWidth) / 2
|
||||
x += emptySpace
|
||||
width = assetCreateDialogMaxWidth
|
||||
}
|
||||
|
||||
if height > assetCreateDialogHeight {
|
||||
emptySpace := (height - assetCreateDialogHeight) / 2
|
||||
y += emptySpace
|
||||
height = assetCreateDialogHeight
|
||||
}
|
||||
|
||||
d.Box.SetRect(x, y, width, height)
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) 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 *AssetCreateDialog) SetCreateFunc(f func()) *AssetCreateDialog {
|
||||
d.createHandler = f
|
||||
enterButton := d.form.GetButton(d.form.GetButtonCount() - 1)
|
||||
|
||||
enterButton.SetSelectedFunc(f)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) SetCancelFunc(f func()) *AssetCreateDialog {
|
||||
d.cancelHandler = f
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) dropdownHasFocus() bool {
|
||||
return utils.CheckFocus(d.assetCategoryField, d.assetShelfField)
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) initData() {
|
||||
|
||||
// Get available shelves
|
||||
shelfList, _ := d.client.RetrieveAllShelves()
|
||||
d.shelfList = shelfList
|
||||
|
||||
shelfOptions := []string{""}
|
||||
for _, shelf := range d.shelfList {
|
||||
shelfOptions = append(shelfOptions, fmt.Sprintf("%s", shelf.Name)) // In case we want to do custom formatting later
|
||||
}
|
||||
|
||||
// Get available categories
|
||||
categoryList, _ := d.client.RetrieveAllCategories()
|
||||
d.categoryList = categoryList
|
||||
|
||||
categoryOptions := []string{""}
|
||||
for _, category := range d.categoryList {
|
||||
categoryOptions = append(shelfOptions, fmt.Sprintf("%s", category.Name))
|
||||
}
|
||||
|
||||
// General category
|
||||
d.assetNameField.SetText("")
|
||||
d.assetQuantityField.SetText("")
|
||||
d.assetLengthField.SetText("")
|
||||
d.assetManufacturerField.SetText("")
|
||||
d.assetModelField.SetText("")
|
||||
d.assetPriceField.SetText("")
|
||||
|
||||
// Location category
|
||||
d.assetShelfField.SetOptions(shelfOptions, nil)
|
||||
d.assetShelfField.SetCurrentOption(0)
|
||||
d.assetCategoryField.SetOptions(categoryOptions, nil)
|
||||
d.assetCategoryField.SetCurrentOption(0)
|
||||
|
||||
// Comments category
|
||||
d.assetCommentsArea.SetText("", true)
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) setupLayout() {
|
||||
bgColor := style.DialogBgColor
|
||||
|
||||
d.categories.SetDynamicColors(true).
|
||||
SetWrap(true).
|
||||
SetTextAlign(tview.AlignLeft)
|
||||
d.categories.SetBackgroundColor(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("ASSET CREATE")
|
||||
|
||||
_, 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.AddItem(layout, 0, 1, true)
|
||||
|
||||
d.layout.AddItem(d.form, dialogs.DialogFormHeight, 0, true)
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) setupGeneralInfoPageUI() {
|
||||
bgColor := style.DialogBgColor
|
||||
inputFieldBgColor := style.InputFieldBgColor
|
||||
pageLabelWidth := 12
|
||||
|
||||
d.assetNameField.SetLabel("name:")
|
||||
d.assetNameField.SetLabelWidth(pageLabelWidth)
|
||||
d.assetNameField.SetLabelColor(bgColor)
|
||||
d.assetNameField.SetBackgroundColor(style.DialogFgColor)
|
||||
d.assetNameField.SetFieldBackgroundColor(inputFieldBgColor)
|
||||
|
||||
d.assetQuantityField.SetLabel("quantity:")
|
||||
d.assetQuantityField.SetLabelWidth(pageLabelWidth)
|
||||
d.assetQuantityField.SetLabelColor(bgColor)
|
||||
d.assetQuantityField.SetBackgroundColor(style.DialogFgColor)
|
||||
d.assetQuantityField.SetFieldBackgroundColor(inputFieldBgColor)
|
||||
|
||||
d.assetLengthField.SetLabel("length:")
|
||||
d.assetLengthField.SetLabelWidth(pageLabelWidth)
|
||||
d.assetLengthField.SetLabelColor(bgColor)
|
||||
d.assetLengthField.SetBackgroundColor(style.DialogFgColor)
|
||||
d.assetLengthField.SetFieldBackgroundColor(inputFieldBgColor)
|
||||
|
||||
d.assetManufacturerField.SetLabel("manufacturer:")
|
||||
d.assetManufacturerField.SetLabelWidth(pageLabelWidth)
|
||||
d.assetManufacturerField.SetLabelColor(bgColor)
|
||||
d.assetManufacturerField.SetBackgroundColor(style.DialogFgColor)
|
||||
d.assetManufacturerField.SetFieldBackgroundColor(inputFieldBgColor)
|
||||
|
||||
d.assetModelField.SetLabel("model:")
|
||||
d.assetModelField.SetLabelWidth(pageLabelWidth)
|
||||
d.assetModelField.SetLabelColor(bgColor)
|
||||
d.assetModelField.SetBackgroundColor(style.DialogFgColor)
|
||||
d.assetModelField.SetFieldBackgroundColor(inputFieldBgColor)
|
||||
|
||||
d.assetPriceField.SetLabel("price:")
|
||||
d.assetPriceField.SetLabelWidth(pageLabelWidth)
|
||||
d.assetPriceField.SetLabelColor(bgColor)
|
||||
d.assetPriceField.SetBackgroundColor(style.DialogFgColor)
|
||||
d.assetPriceField.SetFieldBackgroundColor(inputFieldBgColor)
|
||||
|
||||
d.generalInfoPage.SetDirection(tview.FlexRow)
|
||||
d.generalInfoPage.AddItem(d.assetNameField, 1, 0, true)
|
||||
d.generalInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
|
||||
d.generalInfoPage.AddItem(d.assetQuantityField, 1, 0, true)
|
||||
d.generalInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
|
||||
d.generalInfoPage.AddItem(d.assetLengthField, 1, 0, true)
|
||||
d.generalInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
|
||||
d.generalInfoPage.AddItem(d.assetManufacturerField, 1, 0, true)
|
||||
d.generalInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
|
||||
d.generalInfoPage.AddItem(d.assetModelField, 1, 0, true)
|
||||
d.generalInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
|
||||
d.generalInfoPage.AddItem(d.assetPriceField, 1, 0, true)
|
||||
d.generalInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) setupLocationPageUI() {
|
||||
bgColor := style.DialogBgColor
|
||||
inputFieldBgColor := style.InputFieldBgColor
|
||||
pageLabelWidth := 12
|
||||
ddUnselectedStyle := style.DropdownUnselected
|
||||
ddSelectedStyle := style.DropdownSelected
|
||||
|
||||
d.assetCategoryField.SetLabel("category:")
|
||||
d.assetCategoryField.SetLabelWidth(pageLabelWidth)
|
||||
d.assetCategoryField.SetBackgroundColor(bgColor)
|
||||
d.assetCategoryField.SetLabelColor(style.DialogFgColor)
|
||||
d.assetCategoryField.SetListStyles(ddUnselectedStyle, ddSelectedStyle)
|
||||
d.assetCategoryField.SetFieldBackgroundColor(inputFieldBgColor)
|
||||
|
||||
d.assetShelfField.SetLabel("shelf:")
|
||||
d.assetShelfField.SetLabelWidth(pageLabelWidth)
|
||||
d.assetShelfField.SetBackgroundColor(bgColor)
|
||||
d.assetShelfField.SetLabelColor(style.DialogFgColor)
|
||||
d.assetShelfField.SetListStyles(ddUnselectedStyle, ddSelectedStyle)
|
||||
d.assetShelfField.SetFieldBackgroundColor(inputFieldBgColor)
|
||||
|
||||
d.locationPage.SetDirection(tview.FlexRow)
|
||||
d.locationPage.AddItem(d.assetCategoryField, 1, 0, true)
|
||||
d.locationPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
|
||||
d.locationPage.AddItem(d.assetShelfField, 1, 0, true)
|
||||
d.locationPage.SetBackgroundColor(bgColor)
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) setupCommentsPageUI() {
|
||||
bgColor := style.DialogBgColor
|
||||
inputFieldBgColor := style.InputFieldBgColor
|
||||
pageLabelWidth := 12
|
||||
|
||||
d.assetCommentsArea.SetLabel("comments:")
|
||||
d.assetCommentsArea.SetLabelWidth(pageLabelWidth)
|
||||
d.assetCommentsArea.SetBackgroundColor(inputFieldBgColor)
|
||||
d.assetCommentsArea.SetLabelStyle(tcell.StyleDefault.Foreground(style.DialogFgColor).Background(bgColor))
|
||||
d.assetCommentsArea.SetWrap(true)
|
||||
|
||||
d.commentsPage.AddItem(d.assetCommentsArea, 0, 1, true)
|
||||
d.commentsPage.SetBackgroundColor(bgColor)
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) setActiveCategory(idx int) {
|
||||
fgColor := style.DialogFgColor
|
||||
bgBolor := style.ButtonBgColor
|
||||
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 *AssetCreateDialog) nextCategory() {
|
||||
activePage := d.activePageIndex
|
||||
if d.activePageIndex < len(d.createCategoryLabels)-1 {
|
||||
activePage++
|
||||
|
||||
d.setActiveCategory(activePage)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
d.setActiveCategory(0)
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) previousCategory() {
|
||||
activePage := d.activePageIndex
|
||||
if d.activePageIndex > 0 {
|
||||
activePage--
|
||||
|
||||
d.setActiveCategory(activePage)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
d.setActiveCategory(len(d.createCategoryLabels) - 1)
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) initCustomInputHandlers() {
|
||||
d.assetShelfField.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
event = utils.ParseKeyEventKey(d.logger, event)
|
||||
|
||||
return event
|
||||
})
|
||||
|
||||
d.assetCategoryField.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
event = utils.ParseKeyEventKey(d.logger, event)
|
||||
|
||||
return event
|
||||
})
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) setGeneralInfoNextFocus() {
|
||||
// Name -> Quantity -> Length -> Manufacturer -> Model -> Price
|
||||
if d.assetNameField.HasFocus() {
|
||||
d.focusElement = createAssetQuantityFieldFocus
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if d.assetQuantityField.HasFocus() {
|
||||
d.focusElement = createAssetLengthFieldFocus
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if d.assetLengthField.HasFocus() {
|
||||
d.focusElement = createAssetManufacturerFieldFocus
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if d.assetManufacturerField.HasFocus() {
|
||||
d.focusElement = createAssetModelFieldFocus
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if d.assetModelField.HasFocus() {
|
||||
d.focusElement = createAssetPriceFieldFocus
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
d.focusElement = createAssetFormFocus
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) setLocationNextFocus() {
|
||||
// Category -> Shelf
|
||||
if d.assetCategoryField.HasFocus() {
|
||||
d.focusElement = createAssetShelfFieldFocus
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
d.focusElement = createAssetFormFocus
|
||||
}
|
||||
|
||||
func (d *AssetCreateDialog) setCommentsNextFocus() {
|
||||
d.focusElement = createAssetFormFocus
|
||||
}
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
|
||||
func (a *Assets) runCommand(cmd string) {
|
||||
switch cmd {
|
||||
case "create asset", "view asset":
|
||||
case "create asset":
|
||||
a.createDialog.Display()
|
||||
case "view asset":
|
||||
a.cNotImplemented()
|
||||
return
|
||||
case "delete asset":
|
||||
a.cdelete()
|
||||
return
|
||||
case "refresh":
|
||||
a.crefresh()
|
||||
}
|
||||
|
||||
@@ -41,4 +41,7 @@ var (
|
||||
ButtonFgColor = tcell.ColorLightBlue
|
||||
ButtonSelectedFgColor = tcell.ColorBlack
|
||||
ButtonSelectedBgColor = tcell.ColorWhite
|
||||
InputFieldBgColor = tcell.ColorGray
|
||||
DropdownUnselected = tcell.StyleDefault.Background(tcell.ColorWhiteSmoke).Foreground(tcell.ColorBlack)
|
||||
DropdownSelected = tcell.StyleDefault.Background(tcell.ColorLightSlateGray).Foreground(tcell.ColorWhite)
|
||||
)
|
||||
|
||||
@@ -27,3 +27,29 @@ func CheckFocus(prims ...tview.Primitive) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AlignStringListWidth(list []string) ([]string, int) {
|
||||
var (
|
||||
m = 0
|
||||
alignedList = make([]string, 0)
|
||||
)
|
||||
|
||||
for _, item := range list {
|
||||
if len(item) > m {
|
||||
m = len(item)
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
if len(item) < m {
|
||||
need := m - len(item)
|
||||
for i := 0; i < need; i++ {
|
||||
item += " "
|
||||
}
|
||||
}
|
||||
|
||||
alignedList = append(alignedList, item)
|
||||
}
|
||||
|
||||
return alignedList, m
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user