Add Show Widget for library actions with Overview charts from last run output.
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LoadActionWidgets reads and parses node-action-widgets.json from dir.
|
||||
func LoadActionWidgets(dir string) (ActionWidgetStore, error) {
|
||||
path := filepath.Join(dir, NodeActionWidgetsFileName)
|
||||
payload, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return ActionWidgetStore{}, err
|
||||
}
|
||||
|
||||
var store ActionWidgetStore
|
||||
if err := json.Unmarshal(payload, &store); err != nil {
|
||||
return ActionWidgetStore{}, fmt.Errorf("parse action widgets: %w", err)
|
||||
}
|
||||
if store.Widgets == nil {
|
||||
store.Widgets = []ActionWidgetSnapshot{}
|
||||
}
|
||||
return store, nil
|
||||
}
|
||||
|
||||
// LoadActionWidgetsOrEmpty returns an empty store when the file is missing.
|
||||
func LoadActionWidgetsOrEmpty(dir string) (ActionWidgetStore, error) {
|
||||
store, err := LoadActionWidgets(dir)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return ActionWidgetStore{Widgets: []ActionWidgetSnapshot{}}, nil
|
||||
}
|
||||
return ActionWidgetStore{}, err
|
||||
}
|
||||
return store, nil
|
||||
}
|
||||
|
||||
// SaveActionWidgets writes node-action-widgets.json to dir with mode 0640.
|
||||
func SaveActionWidgets(dir string, store ActionWidgetStore) error {
|
||||
if err := ensureConfigDir(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
if store.Widgets == nil {
|
||||
store.Widgets = []ActionWidgetSnapshot{}
|
||||
}
|
||||
|
||||
payload, err := json.MarshalIndent(store, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode action widgets: %w", err)
|
||||
}
|
||||
payload = append(payload, '\n')
|
||||
|
||||
path := filepath.Join(dir, NodeActionWidgetsFileName)
|
||||
return os.WriteFile(path, payload, 0o640)
|
||||
}
|
||||
|
||||
// WidgetsForNode returns snapshots belonging to nodeID.
|
||||
func WidgetsForNode(store ActionWidgetStore, nodeID string) []ActionWidgetSnapshot {
|
||||
out := make([]ActionWidgetSnapshot, 0)
|
||||
for _, widget := range store.Widgets {
|
||||
if widget.NodeID == nodeID {
|
||||
out = append(out, widget)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// UpsertActionWidgetSnapshot replaces or appends a snapshot keyed by node_id + item_id.
|
||||
func UpsertActionWidgetSnapshot(dir string, snapshot ActionWidgetSnapshot) error {
|
||||
if strings.TrimSpace(snapshot.NodeID) == "" {
|
||||
return fmt.Errorf("node id is required")
|
||||
}
|
||||
if strings.TrimSpace(snapshot.ItemID) == "" {
|
||||
return fmt.Errorf("item id is required")
|
||||
}
|
||||
if snapshot.UpdatedAt.IsZero() {
|
||||
snapshot.UpdatedAt = time.Now().UTC()
|
||||
}
|
||||
|
||||
store, err := LoadActionWidgetsOrEmpty(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found := false
|
||||
for index := range store.Widgets {
|
||||
if store.Widgets[index].NodeID == snapshot.NodeID &&
|
||||
store.Widgets[index].ItemID == snapshot.ItemID {
|
||||
store.Widgets[index] = snapshot
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
store.Widgets = append(store.Widgets, snapshot)
|
||||
}
|
||||
return SaveActionWidgets(dir, store)
|
||||
}
|
||||
|
||||
// DeleteActionWidgetsForItem removes the snapshot for a node action item, if any.
|
||||
func DeleteActionWidgetsForItem(dir string, nodeID string, itemID string) error {
|
||||
store, err := LoadActionWidgetsOrEmpty(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filtered := make([]ActionWidgetSnapshot, 0, len(store.Widgets))
|
||||
changed := false
|
||||
for _, widget := range store.Widgets {
|
||||
if widget.NodeID == nodeID && widget.ItemID == itemID {
|
||||
changed = true
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, widget)
|
||||
}
|
||||
if !changed {
|
||||
return nil
|
||||
}
|
||||
store.Widgets = filtered
|
||||
return SaveActionWidgets(dir, store)
|
||||
}
|
||||
Reference in New Issue
Block a user