From 4ae19faac9828029a96714e740a0be45b319846e Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Mon, 18 May 2026 15:51:17 +0200 Subject: [PATCH] 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 --- .../internal/web/handlers/tablo_detail_tab.go | 76 ++ .../internal/web/views/tablo_detail.templ | 189 +++++ .../internal/web/views/tablo_detail_templ.go | 741 ++++++++++++++++++ 3 files changed, 1006 insertions(+) create mode 100644 go-backend/internal/web/handlers/tablo_detail_tab.go create mode 100644 go-backend/internal/web/views/tablo_detail.templ create mode 100644 go-backend/internal/web/views/tablo_detail_templ.go diff --git a/go-backend/internal/web/handlers/tablo_detail_tab.go b/go-backend/internal/web/handlers/tablo_detail_tab.go new file mode 100644 index 0000000..7a424c8 --- /dev/null +++ b/go-backend/internal/web/handlers/tablo_detail_tab.go @@ -0,0 +1,76 @@ +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(`
Cette section arrive bientôt.
`)); writeErr != nil { + http.Error(w, "failed to render tab", http.StatusInternalServerError) + } + } + } +} diff --git a/go-backend/internal/web/views/tablo_detail.templ b/go-backend/internal/web/views/tablo_detail.templ new file mode 100644 index 0000000..a6a3c21 --- /dev/null +++ b/go-backend/internal/web/views/tablo_detail.templ @@ -0,0 +1,189 @@ +package views + +import ( + "strconv" + "xtablo-backend/internal/web/ui" +) + +// TabloDetailPage renders the full tablo detail page. +templ TabloDetailPage(vm TabloDetailViewModel) { +
+ @TabloDetailHeader(vm) + @TabloDetailTabBar(vm.TabloID) +
+ @TabloDetailKanbanBoard(vm.Columns, vm.TabloID) + if len(vm.Etapes) > 0 { + @TabloDetailEtapesSection(vm.Etapes) + } +
+ @TabloDetailSortableScript(vm.TabloID) +
+} + +// TabloDetailHeader renders the header with avatar, title, and metadata row. +templ TabloDetailHeader(vm TabloDetailViewModel) { +
+
+
{ vm.Initial }
+

{ vm.TabloName }

+
+ +
+} + +// TabloDetailTabBar renders the HTMX tab navigation. +templ TabloDetailTabBar(tabloID string) { + +} + +// TabloDetailKanbanBoard renders the full kanban board with 4 columns. +templ TabloDetailKanbanBoard(columns []TabloDetailColumnView, tabloID string) { +
+ for _, col := range columns { + @TabloDetailKanbanColumn(col, tabloID) + } +
+} + +// TabloDetailKanbanColumn renders a single kanban column with header, task list, reorder form, and create zone. +templ TabloDetailKanbanColumn(col TabloDetailColumnView, tabloID string) { +
+
+ { col.Label } + { strconv.Itoa(len(col.Tasks)) } + + Ajouter +
+
+ if len(col.Tasks) == 0 { +
Aucune tâche
+ } else { + for _, task := range col.Tasks { + @TabloDetailTaskCard(task, tabloID) + } + } +
+ +
+
+} + +// TabloDetailTaskCard renders a single task card with drag handle and delete button. +templ TabloDetailTaskCard(task TabloDetailTaskView, tabloID string) { +
+
+ + { task.Title } + @ui.IconButton(ui.IconButtonProps{ + Label: "Supprimer la tâche", + Icon: "trash", + Variant: ui.IconButtonVariantDanger, + Tone: ui.IconButtonToneGhost, + Type: "button", + Attrs: templ.Attributes{ + "class": "task-card-delete", + "hx-delete": task.DeleteHref, + "hx-target": "#app-main-content", + "hx-swap": "outerHTML", + "hx-confirm": "Supprimer cette tâche ?", + }, + }) +
+
+} + +// TabloDetailEtapesSection renders the etapes summary list below the kanban board. +templ TabloDetailEtapesSection(etapes []TabloDetailEtapeView) { +
+

Étapes

+ +
+} + +// TabloDetailSortableScript emits the Sortable.js initialization script. +templ TabloDetailSortableScript(tabloID string) { + @templ.Raw(``) +} diff --git a/go-backend/internal/web/views/tablo_detail_templ.go b/go-backend/internal/web/views/tablo_detail_templ.go new file mode 100644 index 0000000..979d0f7 --- /dev/null +++ b/go-backend/internal/web/views/tablo_detail_templ.go @@ -0,0 +1,741 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.1020 +package views + +//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 ( + "strconv" + "xtablo-backend/internal/web/ui" +) + +// TabloDetailPage renders the full tablo detail page. +func TabloDetailPage(vm TabloDetailViewModel) 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) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = TabloDetailHeader(vm).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = TabloDetailTabBar(vm.TabloID).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = TabloDetailKanbanBoard(vm.Columns, vm.TabloID).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(vm.Etapes) > 0 { + templ_7745c5c3_Err = TabloDetailEtapesSection(vm.Etapes).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = TabloDetailSortableScript(vm.TabloID).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +// TabloDetailHeader renders the header with avatar, title, and metadata row. +func TabloDetailHeader(vm TabloDetailViewModel) 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_Var2 := templ.GetChildren(ctx) + if templ_7745c5c3_Var2 == nil { + templ_7745c5c3_Var2 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var4 string + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(vm.Initial) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablo_detail.templ`, Line: 27, Col: 83} + } + _, 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, 7, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(vm.TabloName) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablo_detail.templ`, Line: 28, Col: 48} + } + _, 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, 8, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +// TabloDetailTabBar renders the HTMX tab navigation. +func TabloDetailTabBar(tabloID 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_Var12 := templ.GetChildren(ctx) + if templ_7745c5c3_Var12 == nil { + templ_7745c5c3_Var12 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +// TabloDetailKanbanBoard renders the full kanban board with 4 columns. +func TabloDetailKanbanBoard(columns []TabloDetailColumnView, tabloID 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, 21, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, col := range columns { + templ_7745c5c3_Err = TabloDetailKanbanColumn(col, tabloID).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +// TabloDetailKanbanColumn renders a single kanban column with header, task list, reorder form, and create zone. +func TabloDetailKanbanColumn(col TabloDetailColumnView, tabloID 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_Var19 := templ.GetChildren(ctx) + if templ_7745c5c3_Var19 == nil { + templ_7745c5c3_Var19 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var21 string + templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(col.Label) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablo_detail.templ`, Line: 105, Col: 54} + } + _, 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, 25, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var22 string + templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(len(col.Tasks))) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablo_detail.templ`, Line: 106, Col: 71} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, " + Ajouter
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(col.Tasks) == 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "
Aucune tâche
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + for _, task := range col.Tasks { + templ_7745c5c3_Err = TabloDetailTaskCard(task, tabloID).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +// TabloDetailTaskCard renders a single task card with drag handle and delete button. +func TabloDetailTaskCard(task TabloDetailTaskView, tabloID 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_Var30 := templ.GetChildren(ctx) + if templ_7745c5c3_Var30 == nil { + templ_7745c5c3_Var30 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var32 string + templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(task.Title) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablo_detail.templ`, Line: 134, Col: 45} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = ui.IconButton(ui.IconButtonProps{ + Label: "Supprimer la tâche", + Icon: "trash", + Variant: ui.IconButtonVariantDanger, + Tone: ui.IconButtonToneGhost, + Type: "button", + Attrs: templ.Attributes{ + "class": "task-card-delete", + "hx-delete": task.DeleteHref, + "hx-target": "#app-main-content", + "hx-swap": "outerHTML", + "hx-confirm": "Supprimer cette tâche ?", + }, + }).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +// TabloDetailEtapesSection renders the etapes summary list below the kanban board. +func TabloDetailEtapesSection(etapes []TabloDetailEtapeView) 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_Var33 := templ.GetChildren(ctx) + if templ_7745c5c3_Var33 == nil { + templ_7745c5c3_Var33 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "

Étapes

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +// TabloDetailSortableScript emits the Sortable.js initialization script. +func TabloDetailSortableScript(tabloID 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_Var36 := templ.GetChildren(ctx) + if templ_7745c5c3_Var36 == nil { + templ_7745c5c3_Var36 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templ.Raw(``).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +var _ = templruntime.GeneratedTemplate