make web submit when action done by user

This commit is contained in:
Robin Olsen
2026-03-19 21:12:28 +01:00
parent ef649c9ac4
commit 35114a3e0f
9 changed files with 52 additions and 17 deletions

12
Task Details.txt Normal file
View File

@@ -0,0 +1,12 @@
You are tasked with backlog refinement.
The product backlog has become cluttered with old and unclear issues. You need to groom the backlog:
1. Go to the backlog.
2. Find two issues that got the same name or describe the same problem.
3. Open one of these issues.
4. Select "Close issue"
5. Choose "Duplicate issue" as closing reason.
6. Save/close issue.
Focus on making the backlog a reliable source of upcoming work.

2
go.mod
View File

@@ -6,6 +6,7 @@ require (
github.com/go-git/go-git/v6 v6.0.0-20260317113930-fb0d09929504 github.com/go-git/go-git/v6 v6.0.0-20260317113930-fb0d09929504
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/muesli/reflow v0.3.0 github.com/muesli/reflow v0.3.0
github.com/spf13/pflag v1.0.10
github.com/steveyegge/beads v0.49.6 github.com/steveyegge/beads v0.49.6
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
) )
@@ -97,7 +98,6 @@ require (
github.com/sergi/go-diff v1.4.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect
github.com/spf13/afero v1.15.0 // indirect github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.21.0 // indirect github.com/spf13/viper v1.21.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/tetratelabs/wazero v1.11.0 // indirect github.com/tetratelabs/wazero v1.11.0 // indirect

View File

@@ -46,7 +46,13 @@ templ Modal(props ModalProps) {
} }
templ ModalContainer() { templ ModalContainer() {
<div id="modal-container"></div> <div
id="modal-container"
hx-get="/status/modal"
hx-trigger="task-status-success from:body"
hx-target="#modal-container"
hx-swap="innerHTML"
></div>
} }
type ConfirmModalProps struct { type ConfirmModalProps struct {

View File

@@ -143,7 +143,7 @@ func ModalContainer() templ.Component {
templ_7745c5c3_Var7 = templ.NopComponent templ_7745c5c3_Var7 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<div id=\"modal-container\"></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<div id=\"modal-container\" hx-get=\"/status/modal\" hx-trigger=\"task-status-success from:body\" hx-target=\"#modal-container\" hx-swap=\"innerHTML\"></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@@ -189,7 +189,7 @@ func ConfirmModal(props ConfirmModalProps) templ.Component {
var templ_7745c5c3_Var9 string var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(props.Title) templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(props.Title)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/modal.templ`, Line: 76, Col: 73} return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/modal.templ`, Line: 82, Col: 73}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@@ -202,7 +202,7 @@ func ConfirmModal(props ConfirmModalProps) templ.Component {
var templ_7745c5c3_Var10 string var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(props.Message) templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(props.Message)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/modal.templ`, Line: 80, Col: 52} return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/modal.templ`, Line: 86, Col: 52}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@@ -216,7 +216,7 @@ func ConfirmModal(props ConfirmModalProps) templ.Component {
var templ_7745c5c3_Var11 string var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(props.CancelText) templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(props.CancelText)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/modal.templ`, Line: 89, Col: 25} return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/modal.templ`, Line: 95, Col: 25}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@@ -257,7 +257,7 @@ func ConfirmModal(props ConfirmModalProps) templ.Component {
var templ_7745c5c3_Var14 string var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(props.ConfirmAction) templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(props.ConfirmAction)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/modal.templ`, Line: 97, Col: 37} return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/modal.templ`, Line: 103, Col: 37}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@@ -271,7 +271,7 @@ func ConfirmModal(props ConfirmModalProps) templ.Component {
var templ_7745c5c3_Var15 string var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(props.ConfirmText) templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(props.ConfirmText)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/modal.templ`, Line: 102, Col: 26} return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/modal.templ`, Line: 108, Col: 26}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {

View File

@@ -1,7 +1,7 @@
package components package components
templ Status() { templ Status(msg string) {
<div id="status" hx-get="/status" hx-trigger="click" hx-target="#status" hx-swap="outerHTML"> <div id="status" hx-get="/status" hx-trigger="load, click" hx-target="#status" hx-swap="outerHTML">
<button type="button" class="btn btn-outline btn-sm" hx-get="/status/modal" hx-target="#modal-container" hx-swap="innerHTML">Show Task Status</button> <button type="button" class="btn btn-outline btn-sm" hx-get="/status/modal" hx-target="#modal-container" hx-swap="innerHTML">{ msg }</button>
</div> </div>
} }

View File

@@ -8,7 +8,7 @@ package components
import "github.com/a-h/templ" import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime" import templruntime "github.com/a-h/templ/runtime"
func Status() templ.Component { func Status(msg string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -29,7 +29,20 @@ func Status() templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent templ_7745c5c3_Var1 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"status\" hx-get=\"/status\" hx-trigger=\"click\" hx-target=\"#status\" hx-swap=\"outerHTML\"><button type=\"button\" class=\"btn btn-outline btn-sm\" hx-get=\"/status/modal\" hx-target=\"#modal-container\" hx-swap=\"innerHTML\">Show Task Status</button></div>") templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"status\" hx-get=\"/status\" hx-trigger=\"load, click\" hx-target=\"#status\" hx-swap=\"outerHTML\"><button type=\"button\" class=\"btn btn-outline btn-sm\" hx-get=\"/status/modal\" hx-target=\"#modal-container\" hx-swap=\"innerHTML\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(msg)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/status.templ`, Line: 5, Col: 132}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</button></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

View File

@@ -30,11 +30,15 @@ func HandleTaskStatus(w http.ResponseWriter, r *http.Request) {
hx := HTMX(r) hx := HTMX(r)
if hx.IsHxRequest() { if hx.IsHxRequest() {
if taskFeedback.Success {
w.Header().Set("HX-Trigger", "task-status-success")
w.WriteHeader(http.StatusOK)
} else {
hx.WriteString(` hx.WriteString(`
<div id="status" type="button" hx-get="/status" hx-target="#status" hx-swap="outerHTML"> <div id="status" type="button" hx-get="/status" hx-target="#status" hx-swap="outerHTML">
<button class="btn btn-error btn-sm" hx-get="/status/modal" hx-target="#modal-container" hx-swap="innerHTML">` + taskFeedback.Message + `</button> <button class="btn btn-error btn-sm" hx-get="/status/modal" hx-target="#modal-container" hx-swap="innerHTML">` + taskFeedback.Message + `</button>
</div>`) </div>`)
}
return return
} }

View File

@@ -30,7 +30,7 @@ var baseLayout = components.LayoutProps{
}, },
Header: components.HeaderProps{ Header: components.HeaderProps{
Title: "LazyPM", Title: "LazyPM",
NavbarEnd: components.Status(), NavbarEnd: components.Status(""),
}, },
Modal: components.ModalContainer(), Modal: components.ModalContainer(),
} }

View File

@@ -38,7 +38,7 @@ var baseLayout = components.LayoutProps{
}, },
Header: components.HeaderProps{ Header: components.HeaderProps{
Title: "LazyPM", Title: "LazyPM",
NavbarEnd: components.Status(), NavbarEnd: components.Status(""),
}, },
Modal: components.ModalContainer(), Modal: components.ModalContainer(),
} }