xtablo-source/go-backend/internal/web/handlers/tablo_detail_tab.go
Arthur Belleville 4ae19faac9
feat(20-02): TabloDetailPage templ component + GetTabloDetailTab handler
- Create tablo_detail.templ with 8 templ components: TabloDetailPage, TabloDetailHeader, TabloDetailTabBar, TabloDetailKanbanBoard, TabloDetailKanbanColumn, TabloDetailTaskCard, TabloDetailEtapesSection, TabloDetailSortableScript
- Tab links use hx-get + hx-target="#tab-content" + hx-push-url="true" per UI-SPEC interaction contract
- Each kanban column has hidden reorder form id="reorder-form-{status}" for Sortable.js onEnd
- Create tablo_detail_tab.go with GetTabloDetailTab handler for HTMX tab content swaps
- Tasks tab returns kanban board fragment; other tabs return "coming soon" placeholder
2026-05-18 15:51:17 +02:00

76 lines
2.2 KiB
Go

package handlers
import (
"net/http"
"github.com/google/uuid"
"xtablo-backend/internal/web/views"
)
// GetTabloDetailTab handles GET /tablos/{tabloID}/{tab} for HTMX tab content swaps.
func (h *AuthHandler) GetTabloDetailTab() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, ok := h.authenticatedUser(r.Context(), r)
if !ok {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
tabloID, err := uuid.Parse(r.PathValue("tabloID"))
if err != nil {
http.Error(w, "invalid tablo id", http.StatusBadRequest)
return
}
tab := r.PathValue("tab")
w.Header().Set("Content-Type", "text/html; charset=utf-8")
switch tab {
case "tasks":
// Fetch tablo + tasks, build full view model, return kanban board fragment
tablos, err := h.repo.ListTablos(r.Context(), ListTablosInput{OwnerID: user.ID})
if err != nil {
http.Error(w, "failed to load tablos", http.StatusInternalServerError)
return
}
tablo, ok := findTabloByID(tablos, tabloID)
if !ok {
http.Error(w, "tablo not found", http.StatusNotFound)
return
}
taskRepo, ok := h.repo.(tabloDetailRepository)
if !ok {
http.Error(w, "tasks repository not configured", http.StatusInternalServerError)
return
}
tasks, err := taskRepo.ListTasksByTablo(r.Context(), ListTasksByTabloInput{
OwnerID: user.ID,
TabloID: tabloID,
})
if err != nil {
http.Error(w, "failed to load tasks", http.StatusInternalServerError)
return
}
ownerName := ""
if owner, err := h.repo.GetPublicUserByID(r.Context(), user.ID); err == nil {
ownerName = owner.DisplayName
}
vm := views.NewTabloDetailViewModel(tablo, tasks, ownerName)
if renderErr := views.TabloDetailKanbanBoard(vm.Columns, tabloID.String()).Render(r.Context(), w); renderErr != nil {
http.Error(w, "failed to render tab", http.StatusInternalServerError)
}
default:
// Other tabs: overview, files, discussion, events — coming soon fragment
if _, writeErr := w.Write([]byte(`<div class="tab-coming-soon">Cette section arrive bientôt.</div>`)); writeErr != nil {
http.Error(w, "failed to render tab", http.StatusInternalServerError)
}
}
}
}