feat: assign and delete inbox artifacts

This commit is contained in:
2026-06-05 02:15:27 +08:00
parent a96a316883
commit 6eaa4cda49
12 changed files with 324 additions and 8 deletions
+43
View File
@@ -1,5 +1,7 @@
package main
import "fmt"
type InboxNodeDTO struct {
NodeDTO
CaptureKind string `json:"captureKind"`
@@ -32,6 +34,42 @@ func (a *App) ListInboxNodes() ([]InboxNodeDTO, error) {
return dtos, nil
}
func (a *App) AssignInboxNode(nodeID, targetParentID string) (*NodeDTO, error) {
if err := a.requireVault(); err != nil {
return nil, err
}
if !a.isInboxCaptureNode(nodeID) {
return nil, fmt.Errorf("node is not an inbox artifact")
}
if targetParentID == "" {
return nil, fmt.Errorf("target parent is required")
}
if err := a.MoveNode(nodeID, targetParentID); err != nil {
return nil, err
}
if err := a.clearCaptureMeta(nodeID); err != nil {
return nil, err
}
dto, err := a.GetNodeDetail(nodeID)
if err != nil {
return nil, err
}
return dto, nil
}
func (a *App) DeleteInboxNode(nodeID string) error {
if err := a.requireVault(); err != nil {
return err
}
if !a.isInboxCaptureNode(nodeID) {
return fmt.Errorf("node is not an inbox artifact")
}
if err := a.DeleteNode(nodeID); err != nil {
return err
}
return a.clearCaptureMeta(nodeID)
}
func (a *App) filterInboxCaptureNodes(list []NodeDTO) []NodeDTO {
out := make([]NodeDTO, 0, len(list))
for _, item := range list {
@@ -46,3 +84,8 @@ func (a *App) isInboxCaptureNode(nodeID string) bool {
v, ok, err := a.nodes.MetaGet(nodeID, "capture.inbox")
return err == nil && ok && v == "true"
}
func (a *App) clearCaptureMeta(nodeID string) error {
_, err := a.db.Exec(`DELETE FROM node_meta WHERE node_id = ? AND key LIKE 'capture.%'`, nodeID)
return err
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -16,8 +16,8 @@
background: #13131f;
}
</style>
<script type="module" crossorigin src="/assets/main-D-CBbIRx.js"></script>
<link rel="stylesheet" crossorigin href="/assets/main-BgORPqqS.css">
<script type="module" crossorigin src="/assets/main-B7pwitR5.js"></script>
<link rel="stylesheet" crossorigin href="/assets/main-CnGHmBj4.css">
</head>
<body>
<div id="app"></div>
+75 -1
View File
@@ -1,6 +1,11 @@
package main
import "testing"
import (
"errors"
"testing"
"verstak/internal/core/nodes"
)
func TestListInboxNodesReturnsOnlyCapturedArtifacts(t *testing.T) {
app, _ := setupTestApp(t)
@@ -76,3 +81,72 @@ func TestListInboxNodesReturnsOnlyCapturedArtifacts(t *testing.T) {
}
}
}
func TestAssignInboxNodeMovesArtifactIntoCase(t *testing.T) {
app, _ := setupTestApp(t)
parent, err := app.CreateNodeFromTemplate("", "Target Case", "folder.default")
if err != nil {
t.Fatalf("create parent: %v", err)
}
captured, err := app.CaptureText("Captured task material")
if err != nil {
t.Fatalf("CaptureText: %v", err)
}
moved, err := app.AssignInboxNode(captured.ID, parent.ID)
if err != nil {
t.Fatalf("AssignInboxNode: %v", err)
}
if moved.ParentID == nil || *moved.ParentID != parent.ID {
t.Fatalf("ParentID = %v, want %q", moved.ParentID, parent.ID)
}
inbox, err := app.ListInboxNodes()
if err != nil {
t.Fatalf("ListInboxNodes: %v", err)
}
for _, item := range inbox {
if item.ID == captured.ID {
t.Fatal("assigned artifact should leave inbox")
}
}
notes, err := app.ListNotes(parent.ID)
if err != nil {
t.Fatalf("ListNotes: %v", err)
}
var found bool
for _, note := range notes {
if note.ID == captured.ID {
found = true
}
}
if !found {
t.Fatal("assigned text artifact missing from target case notes")
}
}
func TestDeleteInboxNodeRemovesArtifactFromInbox(t *testing.T) {
app, _ := setupTestApp(t)
captured, err := app.CaptureText("Delete this captured material")
if err != nil {
t.Fatalf("CaptureText: %v", err)
}
if err := app.DeleteInboxNode(captured.ID); err != nil {
t.Fatalf("DeleteInboxNode: %v", err)
}
inbox, err := app.ListInboxNodes()
if err != nil {
t.Fatalf("ListInboxNodes: %v", err)
}
for _, item := range inbox {
if item.ID == captured.ID {
t.Fatal("deleted artifact should leave inbox")
}
}
if _, err := app.nodes.GetActive(captured.ID); !errors.Is(err, nodes.ErrNotFound) {
t.Fatalf("GetActive err = %v, want ErrNotFound", err)
}
}