Enhance status component and dashboard functionality

- Updated the status component template to include a new trigger for task status checks.
- Modified the status component's Go implementation to reflect the new trigger in the HTML output.
- Improved the UpdateIssue handler to conditionally set the assignee based on form input.
- Enhanced the dashboard template to include a new dependencies section for issues, allowing users to add dependencies dynamically.
- Refactored the dashboard's issue rows to utilize JavaScript functions for rendering status, type, and priority badges, improving maintainability and readability.
This commit is contained in:
Robin Olsen
2026-04-01 14:37:31 +02:00
parent f755e9f01e
commit 2f0426b58f
14 changed files with 335 additions and 53 deletions

View File

@@ -119,3 +119,15 @@ templ PriorityBadge(priority int) {
<span class="badge badge-ghost badge-sm whitespace-nowrap">{ fmt.Sprintf("P%d", priority) }</span>
}
}
templ StatusBadgeExpr(statusExpr string, fallbackStatus string) {
<span x-html={ fmt.Sprintf(`'<span class="badge ${(() => { const s = %s || %q; switch(s) { case "open": return "badge-info"; case "in_progress": return "badge-warning"; case "closed": return "badge-success"; case "blocked": return "badge-error"; default: return "badge-ghost"; } })()} badge-sm whitespace-nowrap">' + ((%s || %q).replace(/_/g, " ").replace(/\\b\\w/g, l => l.toUpperCase())) + '</span>'`, statusExpr, fallbackStatus, statusExpr, fallbackStatus) }></span>
}
templ TypeBadgeExpr(typeExpr string, fallbackType string) {
<span x-html={ fmt.Sprintf(`'<span class="badge badge-outline ${(() => { const t = %s || %q; switch(t) { case "bug": return "badge-error"; case "feature": return "badge-success"; case "task": return "badge-info"; case "chore": return "badge-warning"; case "epic": return "badge-secondary"; default: return ""; } })()} badge-sm whitespace-nowrap">' + ((%s || %q).charAt(0).toUpperCase() + (%s || %q).slice(1)) + '</span>'`, typeExpr, fallbackType, typeExpr, fallbackType, typeExpr, fallbackType) }></span>
}
templ PriorityBadgeExpr(priorityExpr string, fallbackPriority int) {
<span x-html={ fmt.Sprintf(`'<span class="badge ${(() => { const p = parseInt(%s || %d); switch(p) { case 0: case 1: return "badge-ghost"; case 2: return "badge-info"; case 3: return "badge-warning"; case 4: return "badge-error"; default: return "badge-ghost"; } })()} badge-sm whitespace-nowrap">' + (() => { const p = parseInt(%s || %d); switch(p) { case 0: return "Irrelevant"; case 1: return "Low"; case 2: return "Normal"; case 3: return "High"; case 4: return "Critical"; default: return "P" + p; } })() + '</span>'`, priorityExpr, fallbackPriority, priorityExpr, fallbackPriority) }></span>
}

View File

@@ -413,4 +413,130 @@ func PriorityBadge(priority int) templ.Component {
})
}
func StatusBadgeExpr(statusExpr string, fallbackStatus string) templ.Component {
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
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var16 := templ.GetChildren(ctx)
if templ_7745c5c3_Var16 == nil {
templ_7745c5c3_Var16 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<span x-html=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`'<span class="badge ${(() => { const s = %s || %q; switch(s) { case "open": return "badge-info"; case "in_progress": return "badge-warning"; case "closed": return "badge-success"; case "blocked": return "badge-error"; default: return "badge-ghost"; } })()} badge-sm whitespace-nowrap">' + ((%s || %q).replace(/_/g, " ").replace(/\\b\\w/g, l => l.toUpperCase())) + '</span>'`, statusExpr, fallbackStatus, statusExpr, fallbackStatus))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue_detail.templ`, Line: 124, Col: 460}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\"></span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func TypeBadgeExpr(typeExpr string, fallbackType string) templ.Component {
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
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var18 := templ.GetChildren(ctx)
if templ_7745c5c3_Var18 == nil {
templ_7745c5c3_Var18 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<span x-html=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`'<span class="badge badge-outline ${(() => { const t = %s || %q; switch(t) { case "bug": return "badge-error"; case "feature": return "badge-success"; case "task": return "badge-info"; case "chore": return "badge-warning"; case "epic": return "badge-secondary"; default: return ""; } })()} badge-sm whitespace-nowrap">' + ((%s || %q).charAt(0).toUpperCase() + (%s || %q).slice(1)) + '</span>'`, typeExpr, fallbackType, typeExpr, fallbackType, typeExpr, fallbackType))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue_detail.templ`, Line: 128, Col: 495}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\"></span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func PriorityBadgeExpr(priorityExpr string, fallbackPriority int) templ.Component {
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
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var20 := templ.GetChildren(ctx)
if templ_7745c5c3_Var20 == nil {
templ_7745c5c3_Var20 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<span x-html=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`'<span class="badge ${(() => { const p = parseInt(%s || %d); switch(p) { case 0: case 1: return "badge-ghost"; case 2: return "badge-info"; case 3: return "badge-warning"; case 4: return "badge-error"; default: return "badge-ghost"; } })()} badge-sm whitespace-nowrap">' + (() => { const p = parseInt(%s || %d); switch(p) { case 0: return "Irrelevant"; case 1: return "Low"; case 2: return "Normal"; case 3: return "High"; case 4: return "Critical"; default: return "P" + p; } })() + '</span>'`, priorityExpr, fallbackPriority, priorityExpr, fallbackPriority))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue_detail.templ`, Line: 132, Col: 588}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\"></span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@@ -1,7 +1,7 @@
package components
templ Status(msg string) {
<div id="status" hx-get="/status" hx-trigger="load, click" hx-target="#status" hx-swap="outerHTML">
<div id="status" hx-get="/status" hx-trigger="load, click, task-status-check from:body" 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">{ msg }</button>
</div>
}

View File

@@ -29,7 +29,7 @@ func Status(msg string) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
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\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"status\" hx-get=\"/status\" hx-trigger=\"load, click, task-status-check from:body\" 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
}