From e796ac8b792966cb33c1acc64f0d0feb87321cb1 Mon Sep 17 00:00:00 2001 From: viljarb Date: Thu, 5 Mar 2026 17:51:21 +0100 Subject: [PATCH] add reason for closing issue for closed issues --- pkg/web/components/issue.templ | 16 ++- pkg/web/components/issue_templ.go | 200 ++++++++++++++++++------------ pkg/web/handler/issues.go | 4 + pkg/web/handler/modals.go | 1 + pkg/web/input.css | 6 + 5 files changed, 144 insertions(+), 83 deletions(-) diff --git a/pkg/web/components/issue.templ b/pkg/web/components/issue.templ index cf7fbda..55797d5 100644 --- a/pkg/web/components/issue.templ +++ b/pkg/web/components/issue.templ @@ -13,6 +13,7 @@ type IssueFormProps struct { Title string Description string Status string + CloseReason string // Shown when status is "closed" Priority int IssueType string Class string @@ -21,7 +22,7 @@ type IssueFormProps struct { } templ IssueForm(props IssueFormProps) { -
+
+ @base.Textarea(base.TextareaProps{ + Name: "close_reason", + Label: "Reason for closing issue", + Value: props.CloseReason, + Class: "w-full", + Placeholder: "Describe why this issue is being closed...", + Rows: 3, + }) +
+ } @base.Select(base.SelectProps{ Name: "issue_type", Label: "Issue Type", diff --git a/pkg/web/components/issue_templ.go b/pkg/web/components/issue_templ.go index 3b94b96..922d698 100644 --- a/pkg/web/components/issue_templ.go +++ b/pkg/web/components/issue_templ.go @@ -21,6 +21,7 @@ type IssueFormProps struct { Title string Description string Status string + CloseReason string // Shown when status is "closed" Priority int IssueType string Class string @@ -67,68 +68,81 @@ func IssueForm(props IssueFormProps) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, ">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -168,11 +182,33 @@ func IssueForm(props IssueFormProps) templ.Component { {Label: "In Progress", Value: "in_progress", Selected: props.Status == "in_progress"}, {Label: "Closed", Value: "closed", Selected: props.Status == "closed"}, }, - Size: "md", + Size: "md", + Attrs: templ.Attributes{"x-model": "status"}, }).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + if props.PatchAction != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = base.Textarea(base.TextareaProps{ + Name: "close_reason", + Label: "Reason for closing issue", + Value: props.CloseReason, + Class: "w-full", + Placeholder: "Describe why this issue is being closed...", + Rows: 3, + }).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } templ_7745c5c3_Err = base.Select(base.SelectProps{ Name: "issue_type", Label: "Issue Type", @@ -200,36 +236,36 @@ func IssueForm(props IssueFormProps) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if props.DeleteAction != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" hx-target=\"#modal-container\" hx-swap=\"innerHTML\">Delete Issue ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } if props.Target == "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -257,9 +293,9 @@ func IssueTable(props IssueTableProps) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var8 := templ.GetChildren(ctx) - if templ_7745c5c3_Var8 == nil { - templ_7745c5c3_Var8 = templ.NopComponent + templ_7745c5c3_Var9 := templ.GetChildren(ctx) + if templ_7745c5c3_Var9 == nil { + templ_7745c5c3_Var9 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = base.Table( @@ -299,117 +335,117 @@ func IssueRows(issues []*models.Issue) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var9 := templ.GetChildren(ctx) - if templ_7745c5c3_Var9 == nil { - templ_7745c5c3_Var9 = templ.NopComponent + templ_7745c5c3_Var10 := templ.GetChildren(ctx) + if templ_7745c5c3_Var10 == nil { + templ_7745c5c3_Var10 = templ.NopComponent } ctx = templ.ClearChildren(ctx) for _, issue := range issues { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var12 string - templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs("/issues/" + issue.ID) + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(issue.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue.templ`, Line: 141, Col: 35} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue.templ`, Line: 150, Col: 15} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" hx-target=\"main\" hx-swap=\"innerHTML\" hx-push-url=\"true\">") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" hx-target=\"main\" hx-swap=\"innerHTML\" hx-push-url=\"true\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var14 string - templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(string(issue.Status)) + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(issue.Title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue.templ`, Line: 147, Col: 54} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue.templ`, Line: 159, Col: 18} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var15 string - templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(string(issue.IssueType)) + templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(string(issue.Status)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue.templ`, Line: 148, Col: 57} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue.templ`, Line: 161, Col: 54} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var16 string - templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("P%d", issue.Priority)) + templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(string(issue.IssueType)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue.templ`, Line: 149, Col: 68} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/components/issue.templ`, Line: 162, Col: 57} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/pkg/web/handler/issues.go b/pkg/web/handler/issues.go index d6a0977..95c91f1 100644 --- a/pkg/web/handler/issues.go +++ b/pkg/web/handler/issues.go @@ -25,6 +25,7 @@ type UpdateIssueForm struct { Title *string `form:"title" validate:"omitempty,max=255"` Description *string `form:"description" validate:"omitempty,max=2000"` Status *models.Status `form:"status" validate:"omitempty,oneof=open in_progress closed"` + CloseReason *string `form:"close_reason" validate:"omitempty,max=2000"` IssueType *models.IssueType `form:"issue_type" validate:"omitempty,oneof=task bug feature chore"` Priority *int `form:"priority" validate:"omitempty,gte=0,lte=4"` } @@ -270,6 +271,9 @@ func (f *UpdateIssueForm) toChanges() map[string]any { if f.Status != nil { changes["status"] = *f.Status } + if f.CloseReason != nil { + changes["close_reason"] = *f.CloseReason + } if f.IssueType != nil { changes["issue_type"] = *f.IssueType } diff --git a/pkg/web/handler/modals.go b/pkg/web/handler/modals.go index 157ec35..5a07676 100644 --- a/pkg/web/handler/modals.go +++ b/pkg/web/handler/modals.go @@ -55,6 +55,7 @@ func EditIssueFormModal(w http.ResponseWriter, r *http.Request) { Title: issue.Title, Description: issue.Description, Status: string(issue.Status), + CloseReason: issue.CloseReason, IssueType: string(issue.IssueType), Priority: issue.Priority, }) diff --git a/pkg/web/input.css b/pkg/web/input.css index 73bfd82..1341d1f 100644 --- a/pkg/web/input.css +++ b/pkg/web/input.css @@ -1,4 +1,10 @@ @import 'tailwindcss'; + +/* Alpine.js x-cloak: hide until Alpine initializes */ +[x-cloak] { + display: none !important; +} + @source "./**/*.templ"; @source "./**/*.go"; @source "./components/**/*.templ";