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) {
+
+}
+
+// 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
+
+ for _, etape := range etapes {
+ -
+ { etape.Name }
+ { strconv.Itoa(etape.TaskCount) + " tâche(s)" }
+
+ }
+
+
+}
+
+// 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
+ }
+ 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
+ }
+ for _, etape := range etapes {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "- ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var34 string
+ templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(etape.Name)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablo_detail.templ`, Line: 160, Col: 48}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var35 string
+ templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(etape.TaskCount) + " tâche(s)")
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablo_detail.templ`, Line: 161, Col: 83}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "
")
+ 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