Files
LazyPM/pkg/web/components/issue_detail.templ
Robin Olsen 2f0426b58f 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.
2026-04-01 14:37:31 +02:00

134 lines
5.7 KiB
Plaintext

package components
import (
"fmt"
"github.com/LazyBachelor/LazyPM/internal/models"
)
type IssueDetailProps struct {
Issue *models.Issue
From string // "board" when navigating from board view; passed via hx-vals to child requests
}
templ IssueDetail(props IssueDetailProps) {
<div
if props.From == "board" {
hx-vals='{"from": "board"}'
}
>
<h2 class="text-2xl font-bold m-2">
{ props.Issue.Title }
</h2>
<div class="flex gap-2 flex-wrap">
@StatusBadge(props.Issue.Status)
@TypeBadge(props.Issue.IssueType)
@PriorityBadge(props.Issue.Priority)
</div>
if props.Issue.Description != "" {
<div class="mt-4">
<h3 class="text-sm font-semibold opacity-70 mb-1">Description</h3>
<p class="whitespace-pre-wrap text-sm">{ props.Issue.Description }</p>
</div>
}
<div class="mt-4">
<h3 class="text-sm font-semibold opacity-70 mb-1">Dependencies</h3>
<div
id="issue-dependencies-detail"
hx-get={ "/issues/" + props.Issue.ID + "/dependencies?container_id=issue-dependencies-detail" }
hx-trigger="load"
></div>
</div>
<div class="divider my-2"></div>
<div class="grid grid-cols-2 gap-2 text-sm">
<div class="bg-base-200 rounded-lg p-2">
<span class="text-xs opacity-70 block">Created</span>
<p>{ props.Issue.CreatedAt.Format("Jan 2, 2006") }</p>
</div>
<div class="bg-base-200 rounded-lg p-2">
<span class="text-xs opacity-70 block">Updated</span>
<p>{ props.Issue.UpdatedAt.Format("Jan 2, 2006") }</p>
</div>
<div class="flex flex-row gap-1 bg-base-200 rounded-lg p-2">
<span class="text-xs opacity-70 block">Created by</span>
<p class="text-xs opacity-70">{ props.Issue.CreatedBy }</p>
</div>
<a
class="btn btn-primary btn-sm flex flex-row gap-1 items-center justify-center"
hx-get={ "/issues/" + props.Issue.ID + "/assignee" }
hx-target="#modal-container"
hx-swap="innerHTML"
>
if props.Issue.Assignee == "" {
<span>Unassigned (click to assign) </span>
} else {
<span>Assignee: { props.Issue.Assignee }</span>
}
</a>
</div>
</div>
}
templ StatusBadge(status models.Status) {
switch status {
case "open":
<span class="badge badge-info badge-sm whitespace-nowrap">Open</span>
case "in_progress":
<span class="badge badge-warning badge-sm whitespace-nowrap">In Progress</span>
case "closed":
<span class="badge badge-success badge-sm whitespace-nowrap">Closed</span>
case "blocked":
<span class="badge badge-error badge-sm whitespace-nowrap">Blocked</span>
case "deferred":
<span class="badge badge-ghost badge-sm whitespace-nowrap">Deferred</span>
default:
<span class="badge badge-ghost badge-sm whitespace-nowrap">{ string(status) }</span>
}
}
templ TypeBadge(issueType models.IssueType) {
switch issueType {
case "bug":
<span class="badge badge-outline badge-error badge-sm whitespace-nowrap">Bug</span>
case "feature":
<span class="badge badge-outline badge-success badge-sm whitespace-nowrap">Feature</span>
case "task":
<span class="badge badge-outline badge-info badge-sm whitespace-nowrap">Task</span>
case "chore":
<span class="badge badge-outline badge-warning badge-sm whitespace-nowrap">Chore</span>
case "epic":
<span class="badge badge-outline badge-secondary badge-sm whitespace-nowrap">Epic</span>
default:
<span class="badge badge-outline badge-sm whitespace-nowrap">{ string(issueType) }</span>
}
}
templ PriorityBadge(priority int) {
switch priority {
case 0:
<span class="badge badge-ghost badge-sm whitespace-nowrap">Irrelevant</span>
case 1:
<span class="badge badge-ghost badge-sm whitespace-nowrap">Low</span>
case 2:
<span class="badge badge-info badge-sm whitespace-nowrap">Normal</span>
case 3:
<span class="badge badge-warning badge-sm whitespace-nowrap">High</span>
case 4:
<span class="badge badge-error badge-sm whitespace-nowrap">Critical</span>
default:
<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>
}