From 64ba8abbb0f77e9248701ef78002e7fb690a1170 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sat, 16 May 2026 22:24:52 +0200 Subject: [PATCH] fix(15-03): apply tablo detail restyle with correct AppLayout signatures Merge conflict resolution had taken the old template signatures (pre-AppLayout). Restored HEAD~1 signatures then applied the header/tab-nav restyle: - Title row: md:text-3xl font-bold, Discussion + Invite action buttons - Metadata row: created date, status badge, 0% progress bar - Sticky purple-accent tab bar (Overview, Tasks, Files, Discussion, Events) - All HTMX wiring and AppLayout wrapping preserved --- backend/templates/tablos.templ | 213 ++++++++++++++++++++------------- 1 file changed, 128 insertions(+), 85 deletions(-) diff --git a/backend/templates/tablos.templ b/backend/templates/tablos.templ index 56aca3a..1b57182 100644 --- a/backend/templates/tablos.templ +++ b/backend/templates/tablos.templ @@ -6,61 +6,114 @@ import ( "backend/internal/web/ui" ) -// TablosDashboard renders the root authenticated dashboard: heading, "New tablo" -// button, create-form slot, and the list of tablo cards (or empty state). +// TablosDashboard renders the root authenticated dashboard with sidebar AppLayout. +// Shows a project-card grid (or empty state) for the user's tablos. // UI-SPEC §1 Interaction Contract — GET /. -templ TablosDashboard(user *auth.User, csrfToken string, tablos []TabloCardView) { - @Layout("Tablos — Xtablo", user, csrfToken) { -
-

Your Tablos

- @ui.Button(ui.ButtonProps{ - Label: "New tablo", - Variant: ui.ButtonVariantDefault, - Tone: ui.ButtonToneSolid, - Size: ui.SizeMD, - Type: "button", - Attrs: templ.Attributes{ - "hx-get": "/tablos/new", - "hx-target": "#create-form-slot", - "hx-swap": "innerHTML", - }, - }) -
-
-
- if len(tablos) == 0 { - @TablosEmptyState() - } else { - for _, tablo := range tablos { - @TabloCard(tablo, csrfToken) +templ TablosDashboard(user *auth.User, csrfToken string, activePath string, tablos []sqlc.Tablo, cards []TabloCardView) { + @AppLayout("Tablos — Xtablo", user, csrfToken, activePath, tablos) { +
+
+

Your Tablos

+ @ui.Button(ui.ButtonProps{ + Label: "New tablo", + Variant: ui.ButtonVariantDefault, + Tone: ui.ButtonToneSolid, + Size: ui.SizeMD, + Type: "button", + Attrs: templ.Attributes{ + "hx-get": "/tablos/new", + "hx-target": "#create-form-slot", + "hx-swap": "innerHTML", + }, + }) +
+
+
+ if len(cards) == 0 { + @TablosEmptyState() + } else { + for _, card := range cards { + @TabloProjectCard(card, csrfToken) + } } - } -
+
+ } } // TablosEmptyState renders the empty-state copy when a user has no tablos. // Copy strings are locked by UI-SPEC Copywriting Contract. +// Uses ui.EmptyState for consistent styling across the app (Phase 13). templ TablosEmptyState() { -
-

No tablos yet

-

Create your first tablo to get started.

-
- @ui.Button(ui.ButtonProps{ - Label: "New tablo", - Variant: ui.ButtonVariantDefault, - Tone: ui.ButtonToneSolid, - Size: ui.SizeMD, - Type: "button", - Attrs: templ.Attributes{ - "hx-get": "/tablos/new", - "hx-target": "#create-form-slot", - "hx-swap": "innerHTML", - "aria-label": "Create your first tablo", - }, - }) + @ui.EmptyState(ui.EmptyStateProps{ + Title: "No tablos yet", + Description: "Create your first tablo to get started.", + Action: ui.Button(ui.ButtonProps{ + Label: "New tablo", + Variant: ui.ButtonVariantDefault, + Tone: ui.ButtonToneSolid, + Size: ui.SizeMD, + Type: "button", + Attrs: templ.Attributes{ + "hx-get": "/tablos/new", + "hx-target": "#create-form-slot", + "hx-swap": "innerHTML", + }, + }), + }) +} + +// TabloProjectCard renders a single tablo as a project-card in the dashboard grid. +// Follows D-C02 design: colored avatar circle, title zone (with inline-edit support), +// creation date, and edit/delete icon buttons. +// Guards color rendering against null pgtype.Text values (Pitfall 6). +// Uses .Time accessor on pgtype.Timestamptz (Pitfall 6). +templ TabloProjectCard(card TabloCardView, csrfToken string) { +
+
+
+ @ui.IconButton(ui.IconButtonProps{ + Label: "Edit title", + Icon: "pencil", + Variant: ui.IconButtonVariantNeutral, + Tone: ui.IconButtonToneGhost, + Type: "button", + Attrs: templ.Attributes{ + "hx-get": "/tablos/" + card.Tablo.ID.String() + "/edit-title", + "hx-target": "closest .tablo-title-zone", + "hx-swap": "outerHTML", + }, + }) +
+ @ui.IconButton(ui.IconButtonProps{ + Label: "Delete tablo", + Icon: "trash", + Variant: ui.IconButtonVariantDanger, + Tone: ui.IconButtonToneGhost, + Type: "button", + Attrs: templ.Attributes{ + "hx-get": "/tablos/" + card.Tablo.ID.String() + "/delete-confirm", + "hx-target": "closest .tablo-delete-zone", + "hx-swap": "outerHTML", + }, + }) +
+
-
+
+ if card.Tablo.Color.Valid && card.Tablo.Color.String != "" { + + } else { + + } +
+

{ card.Tablo.Title }

+
+
+
+ { card.Tablo.CreatedAt.Time.Format("Jan 2, 2006") } +
+ } // TabloCard renders a single tablo as a ui.Card on the dashboard. @@ -179,23 +232,23 @@ templ TabloCardWithOOBFormClear(tablo sqlc.Tablo, csrfToken string) {
} -// TabloDetailPage renders the full detail page for a single tablo with a tab layout. -// Tabs: Overview / Tasks / Files / Events / Discussion. activeTab selects the initially rendered tab content. +// TabloDetailPage renders the full detail page for a single tablo with a 3-tab layout. +// Tabs: Overview / Tasks / Files. activeTab selects the initially rendered tab content. // files and tasks are pre-fetched slices for the active tab (may be nil for inactive tabs). +// activePath and sidebarTablos drive the AppLayout sidebar. // UI-SPEC §3 Interaction Contract — GET /tablos/{id}. // D-07: signature includes activeTab string param; D-08: tab bar links carry hx-push-url. -templ TabloDetailPage(user *auth.User, csrfToken string, tablo sqlc.Tablo, tasks []sqlc.Task, etapes []sqlc.Etape, counts EtapeTaskCounts, filter EtapeFilter, files []sqlc.TabloFile, events EventsCalendar, discussion DiscussionTabData, activeTab string) { - @Layout("Tablos — Xtablo", user, csrfToken) { - -
- +templ TabloDetailPage(user *auth.User, csrfToken string, activePath string, sidebarTablos []sqlc.Tablo, tablo sqlc.Tablo, tasks []sqlc.Task, etapes []sqlc.Etape, counts EtapeTaskCounts, filter EtapeFilter, files []sqlc.TabloFile, events EventsCalendar, discussion DiscussionTabData, activeTab string) { + @AppLayout("Tablos — Xtablo", user, csrfToken, activePath, sidebarTablos) { + +
- - @TabloTitleDisplay(tablo, csrfToken) +
+ @TabloTitleDisplay(tablo, csrfToken) +
- Discussion - - -
@TabloDeleteButtonFragment(tablo, csrfToken)
- +
- Créé le + Created { tablo.CreatedAt.Time.Format("Jan 2, 2006") }
- Statut - En cours + Status + In progress
- Progression -
+ Progress +
0%
- -
+ +
@TabloDescDisplay(tablo, csrfToken)
- +