Files
LazyPM/pkg/web/routes/issue_detail_templ.go
Robin Olsen d900a5fcd3 Merge pull request #75 from LazyBachelor/LPM-140
LPM-140 The Sprinting Update
Adds sprint support across the web UI (board + issue detail), TUI kanban, CLI/REPL, and the Beads-backed storage layer.

Changes:

Introduces sprint entities in storage and exposes sprint operations via IssueService.
Updates web board to support backlog vs. sprint columns, sprint selection, and sprint creation routes.
Updates TUI kanban to show backlog + sprint columns and adds sprint selection/creation actions.
2026-03-23 19:26:04 +01:00

183 lines
13 KiB
Go

// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.1001
package routes
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"fmt"
"github.com/LazyBachelor/LazyPM/internal/models"
"github.com/LazyBachelor/LazyPM/pkg/web/components"
)
func IssueDetailPage(issue *models.Issue, comments []*models.Comment) 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_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
issueJSON, _ := templ.JSONString(issue)
xData := fmt.Sprintf(`{
issue: %s,
editing: null,
newComment: '',
get statusClass() {
const classes = {
'open': 'badge-info',
'in_progress': 'badge-warning',
'blocked': 'badge-error',
'closed': 'badge-success'
}
return classes[this.issue.status] || 'badge-ghost'
},
get priorityLabel() {
const labels = ['Irrelevant', 'Low', 'Normal', 'High', 'Critical']
return labels[this.issue.priority] || 'P' + this.issue.priority
},
async saveField(field, value) {
const formData = new URLSearchParams()
formData.append(field, value || '')
const response = await fetch('/issues/' + this.issue.id, {
method: 'PATCH',
body: formData,
headers: { 'HX-Request': 'true' }
})
this.editing = null
// Check if server wants to redirect back to list
const redirectUrl = response.headers.get('HX-Redirect')
if (redirectUrl) {
window.location.href = redirectUrl
}
},
async addComment() {
if (!this.newComment.trim()) return
const author = this.issue.created_by || 'Anonymous'
const formData = new URLSearchParams()
formData.append('text', this.newComment)
formData.append('author', author)
await fetch('/issues/' + this.issue.id + '/comments', {
method: 'POST',
body: formData,
headers: { 'HX-Request': 'true' }
})
this.newComment = ''
window.location.reload()
}
}`, issueJSON)
templ_7745c5c3_Var2 := 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_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_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"max-w-4xl mx-auto p-4\" x-data=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(xData)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/routes/issue_detail.templ`, Line: 61, Col: 51}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><div class=\"flex justify-between items-center mb-6\"><a href=\"/\" class=\"btn btn-ghost btn-sm\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = components.IconBack().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "Back to Issues</a><div class=\"flex gap-2\"><button class=\"btn btn-error btn-sm\" @click=\"if(confirm('Delete this issue?')) { fetch('/issues/' + issue.id, { method: 'DELETE' }).then(() => window.location.href = '/') }\">Delete</button></div></div><div class=\"card bg-base-100 shadow-xl\"><div class=\"card-body\"><div class=\"mb-4\"><template x-if=\"editing !== 'title'\"><h1 class=\"card-title text-3xl cursor-pointer hover:text-primary\" @click=\"editing = 'title'\" x-text=\"issue.title\"></h1></template><template x-if=\"editing === 'title'\"><div class=\"flex gap-2\"><input type=\"text\" name=\"title\" x-model=\"issue.title\" class=\"input input-bordered text-xl flex-1\" @keydown.enter=\"saveField('title', issue.title)\" @keydown.escape=\"editing = null\" x-init=\"$el.focus()\"> <button class=\"btn btn-primary\" @click=\"saveField('title', issue.title)\">Save</button> <button class=\"btn\" @click=\"editing = null\">Cancel</button></div></template><div class=\"text-sm opacity-60 mt-1\" x-text=\"issue.id\"></div></div><div class=\"flex flex-wrap gap-2 mb-4\"><template x-if=\"editing !== 'status'\"><span :class=\"'badge ' + statusClass + ' cursor-pointer hover:opacity-80 whitespace-nowrap'\" @click=\"editing = 'status'\" x-text=\"issue.status\"></span></template><template x-if=\"editing === 'status'\"><select name=\"status\" x-model=\"issue.status\" class=\"select select-sm select-bordered\" @change=\"saveField('status', issue.status)\"><option value=\"open\">Open</option> <option value=\"in_progress\">In Progress</option> <option value=\"blocked\">Blocked</option> <option value=\"closed\">Closed</option></select></template><template x-if=\"editing !== 'type'\"><span class=\"badge badge-outline cursor-pointer hover:opacity-80 whitespace-nowrap\" @click=\"editing = 'type'\" x-text=\"issue.issue_type\"></span></template><template x-if=\"editing === 'type'\"><select name=\"issue_type\" x-model=\"issue.issue_type\" class=\"select select-sm select-bordered\" @change=\"saveField('issue_type', issue.issue_type)\"><option value=\"task\">Task</option> <option value=\"bug\">Bug</option> <option value=\"feature\">Feature</option> <option value=\"chore\">Chore</option></select></template><template x-if=\"editing !== 'priority'\"><span class=\"badge badge-ghost cursor-pointer hover:opacity-80 whitespace-nowrap\" @click=\"editing = 'priority'\" x-text=\"'P' + issue.priority + ' - ' + priorityLabel\"></span></template><template x-if=\"editing === 'priority'\"><select name=\"priority\" x-model=\"issue.priority\" class=\"select select-sm select-bordered\" @change=\"saveField('priority', issue.priority)\"><option value=\"0\">P0 - Irrelevant</option> <option value=\"1\">P1 - Low</option> <option value=\"2\">P2 - Normal</option> <option value=\"3\">P3 - High</option> <option value=\"4\">P4 - Critical</option></select></template></div><div class=\"mb-6\"><h3 class=\"text-sm font-semibold opacity-70 mb-2\">Description</h3><template x-if=\"editing !== 'description'\"><div class=\"min-h-25 p-4 bg-base-200 rounded-lg cursor-pointer hover:bg-base-300\" @click=\"editing = 'description'\"><p class=\"whitespace-pre-wrap\" x-show=\"issue.description\" x-text=\"issue.description\"></p><p class=\"text-base-content/50 italic\" x-show=\"!issue.description\">Click to add description...</p></div></template><template x-if=\"editing === 'description'\"><div class=\"flex flex-col gap-2\"><textarea name=\"description\" x-model=\"issue.description\" class=\"textarea textarea-bordered min-h-37.5\" @keydown.escape=\"editing = null\" x-init=\"$el.focus()\"></textarea><div class=\"flex gap-2\"><button class=\"btn btn-primary\" @click=\"saveField('description', issue.description)\">Save</button> <button class=\"btn\" @click=\"editing = null\">Cancel</button></div></div></template></div><div class=\"grid grid-cols-2 md:grid-cols-4 gap-4 text-sm\"><div class=\"bg-base-200 rounded-lg p-3\"><span class=\"text-xs opacity-60 block\">Created</span><p x-text=\"new Date(issue.created_at).toLocaleDateString()\"></p><p class=\"text-xs opacity-50\" x-text=\"issue.created_by\"></p></div><div class=\"bg-base-200 rounded-lg p-3\"><span class=\"text-xs opacity-60 block\">Updated</span><p x-text=\"new Date(issue.updated_at).toLocaleDateString()\"></p></div><div class=\"bg-base-200 rounded-lg p-3 cursor-pointer hover:bg-base-300\" @click=\"editing = 'assignee'\"><template x-if=\"editing !== 'assignee'\"><div><span class=\"text-xs opacity-60 block\">Assignee</span><p x-text=\"issue.assignee || 'Unassigned'\"></p></div></template><template x-if=\"editing === 'assignee'\"><div><span class=\"text-xs opacity-60 block\">Assignee</span> <input type=\"text\" name=\"assignee\" x-model=\"issue.assignee\" class=\"input input-sm input-bordered w-full\" @keydown.enter=\"saveField('assignee', issue.assignee)\" @keydown.escape=\"editing = null\" x-init=\"$el.focus()\"><div class=\"flex gap-1 mt-1\"><button class=\"btn btn-xs btn-primary\" @click=\"saveField('assignee', issue.assignee)\">Save</button> <button class=\"btn btn-xs\" @click=\"editing = null\">Cancel</button></div></div></template></div></div></div></div><div class=\"mt-6\"><h3 class=\"text-lg font-semibold mb-4\">Comments</h3><div class=\"card bg-base-100 shadow mb-4\"><div class=\"card-body\"><textarea x-model=\"newComment\" class=\"textarea textarea-bordered w-full\" placeholder=\"Add a comment...\" @keydown.enter.prevent=\"addComment()\"></textarea><div class=\"card-actions justify-end mt-2\"><button class=\"btn btn-primary btn-sm\" @click=\"addComment()\" :disabled=\"!newComment.trim()\">Add Comment</button></div></div></div><div class=\"space-y-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, comment := range comments {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div class=\"card bg-base-200\"><div class=\"card-body py-3\"><div class=\"flex justify-between items-start mb-2\"><span class=\"font-semibold text-sm\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(comment.Author)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/routes/issue_detail.templ`, Line: 261, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</span> <span class=\"text-xs opacity-50\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(comment.CreatedAt.Format("Jan 2, 2006 15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/routes/issue_detail.templ`, Line: 262, Col: 89}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</span></div><p class=\"whitespace-pre-wrap text-sm\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(comment.Text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/web/routes/issue_detail.templ`, Line: 264, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</p></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = BaseLayout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate