From 354785edff62e0d26b2389433b7c0ff554f05ed8 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sun, 10 May 2026 14:56:46 +0200 Subject: [PATCH] Pass tablo list to dashboard views Update DashboardPage and DashboardContentSwap to receive the list of tablos so the sidebar can display real project data. Extract tablo icon palette logic into a new tabloicons package that maps hex colors to presentation attributes (icon, background, foreground colors). Update handlers to load tablos from the repository before rendering dashboard views. Refactor TabloCardView to use icon presentation instead of initials when available. --- go-backend/internal/web/handlers/auth.go | 36 +- go-backend/internal/web/handlers/auth_test.go | 54 ++ go-backend/internal/web/handlers/tablos.go | 97 +-- .../internal/web/tabloicons/presentation.go | 81 ++ go-backend/internal/web/ui/app.css | 5 + .../web/views/dashboard_components.templ | 64 +- .../web/views/dashboard_components_templ.go | 751 ++++++++++-------- .../web/views/dashboard_components_test.go | 42 +- go-backend/internal/web/views/home.go | 36 +- go-backend/internal/web/views/tablos.templ | 6 +- go-backend/internal/web/views/tablos_templ.go | 157 ++-- go-backend/static/styles.css | 5 + 12 files changed, 795 insertions(+), 539 deletions(-) create mode 100644 go-backend/internal/web/tabloicons/presentation.go diff --git a/go-backend/internal/web/handlers/auth.go b/go-backend/internal/web/handlers/auth.go index bd8dbab..7b7df0c 100644 --- a/go-backend/internal/web/handlers/auth.go +++ b/go-backend/internal/web/handlers/auth.go @@ -98,9 +98,9 @@ func (h *AuthHandler) GetHome() http.HandlerFunc { content := views.OverviewMainContent(user.DisplayName, user.Email, projects) var renderErr error if isHXRequest(r) { - renderErr = views.DashboardContentSwap("/", content).Render(r.Context(), w) + renderErr = views.DashboardContentSwap("/", tablos, content).Render(r.Context(), w) } else { - renderErr = views.DashboardPage("/", content).Render(r.Context(), w) + renderErr = views.DashboardPage("/", tablos, content).Render(r.Context(), w) } if renderErr != nil { http.Error(w, "failed to render app page", http.StatusInternalServerError) @@ -152,16 +152,24 @@ func (h *AuthHandler) GetNotFound() http.HandlerFunc { return } + tablos, err := h.repo.ListTablos(r.Context(), ListTablosInput{ + OwnerID: user.ID, + }) + if err != nil { + http.Error(w, "failed to load projects", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(http.StatusNotFound) content := views.NotFoundContent(user.DisplayName) - var err error + var renderErr error if isHXRequest(r) { - err = views.DashboardContentSwap("", content).Render(r.Context(), w) + renderErr = views.DashboardContentSwap("", tablos, content).Render(r.Context(), w) } else { - err = views.DashboardPage("", content).Render(r.Context(), w) + renderErr = views.DashboardPage("", tablos, content).Render(r.Context(), w) } - if err != nil { + if renderErr != nil { http.Error(w, "failed to render not found page", http.StatusInternalServerError) } } @@ -175,15 +183,23 @@ func (h *AuthHandler) renderAppPage(activePath string, content func(user PublicU return } + tablos, err := h.repo.ListTablos(r.Context(), ListTablosInput{ + OwnerID: user.ID, + }) + if err != nil { + http.Error(w, "failed to load projects", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") pageContent := content(user) - var err error + var renderErr error if isHXRequest(r) { - err = views.DashboardContentSwap(activePath, pageContent).Render(r.Context(), w) + renderErr = views.DashboardContentSwap(activePath, tablos, pageContent).Render(r.Context(), w) } else { - err = views.DashboardPage(activePath, pageContent).Render(r.Context(), w) + renderErr = views.DashboardPage(activePath, tablos, pageContent).Render(r.Context(), w) } - if err != nil { + if renderErr != nil { http.Error(w, "failed to render app page", http.StatusInternalServerError) } } diff --git a/go-backend/internal/web/handlers/auth_test.go b/go-backend/internal/web/handlers/auth_test.go index ad27bbd..43f5641 100644 --- a/go-backend/internal/web/handlers/auth_test.go +++ b/go-backend/internal/web/handlers/auth_test.go @@ -1,6 +1,7 @@ package handlers import ( + "context" "bytes" "net/http" "net/http/httptest" @@ -114,6 +115,59 @@ func TestLogoutLogsSessionDeletion(t *testing.T) { } } +func TestTasksPageSidebarShowsRealTablos(t *testing.T) { + repo := NewInMemoryAuthRepository() + handler := NewAuthHandler(repo) + sessionCookie := loginTestUser(t, handler, "demo@xtablo.com", "xtablo-demo") + + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.AddCookie(sessionCookie) + userID, ok := handler.currentUserID(req.Context(), req) + if !ok { + t.Fatal("expected user session") + } + + for _, tablo := range []CreateTabloInput{ + {OwnerID: userID, Name: "Blue", Color: "#3B82F6", Status: TabloStatusTodo}, + {OwnerID: userID, Name: "Green", Color: "#22C55E", Status: TabloStatusTodo}, + {OwnerID: userID, Name: "Purple", Color: "#A855F7", Status: TabloStatusTodo}, + {OwnerID: userID, Name: "Red", Color: "#EF4444", Status: TabloStatusTodo}, + {OwnerID: userID, Name: "Hidden Fifth", Color: "#EAB308", Status: TabloStatusTodo}, + } { + if _, err := repo.CreateTablo(context.Background(), tablo); err != nil { + t.Fatalf("create tablo %q: %v", tablo.Name, err) + } + } + + pageReq := httptest.NewRequest(http.MethodGet, "/tasks", nil) + pageReq.AddCookie(sessionCookie) + rec := httptest.NewRecorder() + + handler.GetTasksPage().ServeHTTP(rec, pageReq) + + if rec.Code != http.StatusOK { + t.Fatalf("expected status 200, got %d", rec.Code) + } + + body := rec.Body.String() + for _, want := range []string{ + "Green", + "Purple", + "Red", + "Hidden Fifth", + } { + if !strings.Contains(body, want) { + t.Fatalf("expected sidebar to contain %q, got %q", want, body) + } + } + if strings.Contains(body, "Blue") { + t.Fatalf("expected sidebar to limit to the four most recent tablos, got %q", body) + } + if !strings.Contains(body, `href="/tablos/`) { + t.Fatalf("expected sidebar project links, got %q", body) + } +} + func newTestAuthHandler(t *testing.T) *AuthHandler { t.Helper() return NewAuthHandler(NewInMemoryAuthRepository()) diff --git a/go-backend/internal/web/handlers/tablos.go b/go-backend/internal/web/handlers/tablos.go index 3d1811c..e724e4b 100644 --- a/go-backend/internal/web/handlers/tablos.go +++ b/go-backend/internal/web/handlers/tablos.go @@ -7,13 +7,13 @@ import ( "net/http" "net/url" "regexp" - "strconv" "strings" "time" "github.com/google/uuid" tablomodel "xtablo-backend/internal/tablos" "xtablo-backend/internal/web/dates" + "xtablo-backend/internal/web/tabloicons" "xtablo-backend/internal/web/views" ) @@ -24,29 +24,6 @@ var tabloColorPattern = regexp.MustCompile(`^#[0-9A-Fa-f]{6}$`) const defaultTabloColor = "#3B82F6" const tabloColorValidationMessage = "La couleur du projet doit être un code hexadécimal au format #RRGGBB" -type tabloPaletteEntry struct { - icon string - bg string - fg string - accent string - r int - g int - b int -} - -var tabloIconPalette = []tabloPaletteEntry{ - {icon: "bolt", bg: "bg-blue-500", fg: "text-white", accent: "blue", r: 59, g: 130, b: 246}, - {icon: "leaf", bg: "bg-green-500", fg: "text-white", accent: "green", r: 34, g: 197, b: 94}, - {icon: "gem", bg: "bg-purple-500", fg: "text-white", accent: "purple", r: 168, g: 85, b: 247}, - {icon: "flame", bg: "bg-red-500", fg: "text-white", accent: "red", r: 239, g: 68, b: 68}, - {icon: "star", bg: "bg-yellow-500", fg: "text-gray-700", accent: "yellow", r: 234, g: 179, b: 8}, - {icon: "compass", bg: "bg-indigo-500", fg: "text-white", accent: "indigo", r: 99, g: 102, b: 241}, - {icon: "heart", bg: "bg-pink-500", fg: "text-white", accent: "pink", r: 236, g: 72, b: 153}, - {icon: "waves", bg: "bg-teal-500", fg: "text-white", accent: "teal", r: 20, g: 184, b: 166}, - {icon: "sun", bg: "bg-orange-500", fg: "text-white", accent: "orange", r: 249, g: 115, b: 22}, - {icon: "sparkles", bg: "bg-cyan-500", fg: "text-gray-700", accent: "cyan", r: 6, g: 182, b: 212}, -} - type TabloStatus = tablomodel.Status const ( @@ -152,7 +129,7 @@ func (h *AuthHandler) PostTablos() http.HandlerFunc { http.Error(w, "failed to list tablos", http.StatusInternalServerError) return } - renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), "Le nom du projet est requis"), http.StatusUnprocessableEntity) + renderTablosResponse(w, r, "/tablos", tablos, tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), "Le nom du projet est requis"), http.StatusUnprocessableEntity) return } @@ -163,7 +140,7 @@ func (h *AuthHandler) PostTablos() http.HandlerFunc { http.Error(w, "failed to list tablos", http.StatusInternalServerError) return } - renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), tabloColorValidationMessage), http.StatusUnprocessableEntity) + renderTablosResponse(w, r, "/tablos", tablos, tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), tabloColorValidationMessage), http.StatusUnprocessableEntity) return } @@ -184,7 +161,7 @@ func (h *AuthHandler) PostTablos() http.HandlerFunc { return } - renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK) + renderTablosResponse(w, r, "/tablos", tablos, tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK) } } @@ -218,7 +195,7 @@ func (h *AuthHandler) GetEditTabloModal() http.HandlerFunc { return } - renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, tablo.Name, tablo.Color, ""), http.StatusOK) + renderTablosResponse(w, r, "/tablos", tablos, tablosPageViewModel(user, state, tablos, tablo.Name, tablo.Color, ""), http.StatusOK) } } @@ -252,7 +229,7 @@ func (h *AuthHandler) PostTabloUpdate() http.HandlerFunc { http.Error(w, "failed to list tablos", http.StatusInternalServerError) return } - renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), "Le nom du projet est requis"), http.StatusUnprocessableEntity) + renderTablosResponse(w, r, "/tablos", tablos, tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), "Le nom du projet est requis"), http.StatusUnprocessableEntity) return } @@ -263,7 +240,7 @@ func (h *AuthHandler) PostTabloUpdate() http.HandlerFunc { http.Error(w, "failed to list tablos", http.StatusInternalServerError) return } - renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), tabloColorValidationMessage), http.StatusUnprocessableEntity) + renderTablosResponse(w, r, "/tablos", tablos, tablosPageViewModel(user, state, tablos, name, r.FormValue("color"), tabloColorValidationMessage), http.StatusUnprocessableEntity) return } @@ -289,7 +266,7 @@ func (h *AuthHandler) PostTabloUpdate() http.HandlerFunc { return } - renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK) + renderTablosResponse(w, r, "/tablos", tablos, tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK) } } @@ -323,7 +300,7 @@ func (h *AuthHandler) DeleteTablo() http.HandlerFunc { return } - renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK) + renderTablosResponse(w, r, "/tablos", tablos, tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK) } } @@ -341,7 +318,7 @@ func (h *AuthHandler) renderTablosPage(w http.ResponseWriter, r *http.Request) { return } - renderTablosResponse(w, r, "/tablos", tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK) + renderTablosResponse(w, r, "/tablos", tablos, tablosPageViewModel(user, state, tablos, "", "", ""), http.StatusOK) } func tablosPageViewModel(user PublicUser, state TablosPageState, tablos []TabloRecord, formName string, formColor string, errorMessage string) views.TablosPageViewModel { @@ -374,16 +351,16 @@ func findTabloByID(tablos []TabloRecord, targetID uuid.UUID) (TabloRecord, bool) return TabloRecord{}, false } -func renderTablosResponse(w http.ResponseWriter, r *http.Request, activePath string, vm views.TablosPageViewModel, statusCode int) { +func renderTablosResponse(w http.ResponseWriter, r *http.Request, activePath string, tablos []TabloRecord, vm views.TablosPageViewModel, statusCode int) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(statusCode) var err error content := views.TablosPageContent(vm) if isHXRequest(r) { - err = views.DashboardContentSwapWithMainClass(activePath, "flex-1 overflow-auto", content).Render(r.Context(), w) + err = views.DashboardContentSwapWithMainClass(activePath, tablos, "flex-1 overflow-auto", content).Render(r.Context(), w) } else { - err = views.DashboardPageWithMainClass(activePath, "flex-1 overflow-auto", content).Render(r.Context(), w) + err = views.DashboardPageWithMainClass(activePath, tablos, "flex-1 overflow-auto", content).Render(r.Context(), w) } if err != nil { http.Error(w, "failed to render tablos page", http.StatusInternalServerError) @@ -538,52 +515,8 @@ func tabloStatusPresentation(status TabloStatus) (string, string, int, string) { } func tabloIconPresentation(color string) (string, string, string, string) { - r, g, b, ok := parseHexColor(color) - if !ok { - fallback := tabloIconPalette[0] - return fallback.icon, fallback.bg, fallback.fg, fallback.accent - } - - best := tabloIconPalette[0] - bestDistance := colorDistanceSquared(r, g, b, best.r, best.g, best.b) - for _, entry := range tabloIconPalette[1:] { - distance := colorDistanceSquared(r, g, b, entry.r, entry.g, entry.b) - if distance < bestDistance { - best = entry - bestDistance = distance - } - } - - return best.icon, best.bg, best.fg, best.accent -} - -func parseHexColor(color string) (int, int, int, bool) { - trimmed := strings.TrimSpace(color) - if len(trimmed) != 7 || trimmed[0] != '#' { - return 0, 0, 0, false - } - - r, err := strconv.ParseInt(trimmed[1:3], 16, 0) - if err != nil { - return 0, 0, 0, false - } - g, err := strconv.ParseInt(trimmed[3:5], 16, 0) - if err != nil { - return 0, 0, 0, false - } - b, err := strconv.ParseInt(trimmed[5:7], 16, 0) - if err != nil { - return 0, 0, 0, false - } - - return int(r), int(g), int(b), true -} - -func colorDistanceSquared(r1 int, g1 int, b1 int, r2 int, g2 int, b2 int) int { - dr := r1 - r2 - dg := g1 - g2 - db := b1 - b2 - return dr*dr + dg*dg + db*db + presentation := tabloicons.ForColor(color) + return presentation.Icon, presentation.Bg, presentation.Fg, presentation.Accent } func formatFrenchDate(value time.Time) string { diff --git a/go-backend/internal/web/tabloicons/presentation.go b/go-backend/internal/web/tabloicons/presentation.go new file mode 100644 index 0000000..90871f8 --- /dev/null +++ b/go-backend/internal/web/tabloicons/presentation.go @@ -0,0 +1,81 @@ +package tabloicons + +import ( + "strconv" + "strings" +) + +type Presentation struct { + Icon string + Bg string + Fg string + Accent string +} + +type paletteEntry struct { + Presentation + r int + g int + b int +} + +var palette = []paletteEntry{ + {Presentation: Presentation{Icon: "bolt", Bg: "bg-blue-500", Fg: "text-white", Accent: "blue"}, r: 59, g: 130, b: 246}, + {Presentation: Presentation{Icon: "leaf", Bg: "bg-green-500", Fg: "text-white", Accent: "green"}, r: 34, g: 197, b: 94}, + {Presentation: Presentation{Icon: "gem", Bg: "bg-purple-500", Fg: "text-white", Accent: "purple"}, r: 168, g: 85, b: 247}, + {Presentation: Presentation{Icon: "flame", Bg: "bg-red-500", Fg: "text-white", Accent: "red"}, r: 239, g: 68, b: 68}, + {Presentation: Presentation{Icon: "star", Bg: "bg-yellow-500", Fg: "text-gray-700", Accent: "yellow"}, r: 234, g: 179, b: 8}, + {Presentation: Presentation{Icon: "compass", Bg: "bg-indigo-500", Fg: "text-white", Accent: "indigo"}, r: 99, g: 102, b: 241}, + {Presentation: Presentation{Icon: "heart", Bg: "bg-pink-500", Fg: "text-white", Accent: "pink"}, r: 236, g: 72, b: 153}, + {Presentation: Presentation{Icon: "waves", Bg: "bg-teal-500", Fg: "text-white", Accent: "teal"}, r: 20, g: 184, b: 166}, + {Presentation: Presentation{Icon: "sun", Bg: "bg-orange-500", Fg: "text-white", Accent: "orange"}, r: 249, g: 115, b: 22}, + {Presentation: Presentation{Icon: "sparkles", Bg: "bg-cyan-500", Fg: "text-gray-700", Accent: "cyan"}, r: 6, g: 182, b: 212}, +} + +func ForColor(color string) Presentation { + r, g, b, ok := parseHexColor(color) + if !ok { + return palette[0].Presentation + } + + best := palette[0] + bestDistance := colorDistanceSquared(r, g, b, best.r, best.g, best.b) + for _, entry := range palette[1:] { + distance := colorDistanceSquared(r, g, b, entry.r, entry.g, entry.b) + if distance < bestDistance { + best = entry + bestDistance = distance + } + } + + return best.Presentation +} + +func parseHexColor(color string) (int, int, int, bool) { + trimmed := strings.TrimSpace(color) + if len(trimmed) != 7 || trimmed[0] != '#' { + return 0, 0, 0, false + } + + r, err := strconv.ParseInt(trimmed[1:3], 16, 0) + if err != nil { + return 0, 0, 0, false + } + g, err := strconv.ParseInt(trimmed[3:5], 16, 0) + if err != nil { + return 0, 0, 0, false + } + b, err := strconv.ParseInt(trimmed[5:7], 16, 0) + if err != nil { + return 0, 0, 0, false + } + + return int(r), int(g), int(b), true +} + +func colorDistanceSquared(r1 int, g1 int, b1 int, r2 int, g2 int, b2 int) int { + dr := r1 - r2 + dg := g1 - g2 + db := b1 - b2 + return dr*dr + dg*dg + db*db +} diff --git a/go-backend/internal/web/ui/app.css b/go-backend/internal/web/ui/app.css index 026863b..9056499 100644 --- a/go-backend/internal/web/ui/app.css +++ b/go-backend/internal/web/ui/app.css @@ -1004,6 +1004,11 @@ td.text-right .borderless-icon-button.ui-icon-button-ghost.ui-icon-button-danger width: 3rem; } +.project-avatar > svg { + height: 1.25rem; + width: 1.25rem; +} + .project-list-icon { background: var(--project-color, var(--color-project-fallback)); color: var(--color-text-inverse); diff --git a/go-backend/internal/web/views/dashboard_components.templ b/go-backend/internal/web/views/dashboard_components.templ index 2734eae..b93466c 100644 --- a/go-backend/internal/web/views/dashboard_components.templ +++ b/go-backend/internal/web/views/dashboard_components.templ @@ -1,13 +1,14 @@ package views import "strconv" +import tablomodel "xtablo-backend/internal/tablos" import "xtablo-backend/internal/web/ui" -templ DashboardPage(activePath string, content templ.Component) { - @DashboardPageWithMainClass(activePath, "dashboard-main flex-1 overflow-auto", content) +templ DashboardPage(activePath string, tablos []tablomodel.Record, content templ.Component) { + @DashboardPageWithMainClass(activePath, tablos, "dashboard-main flex-1 overflow-auto", content) } -templ DashboardPageWithMainClass(activePath string, mainClass string, content templ.Component) { +templ DashboardPageWithMainClass(activePath string, tablos []tablomodel.Record, mainClass string, content templ.Component) { @@ -20,7 +21,7 @@ templ DashboardPageWithMainClass(activePath string, mainClass string, content te
- @DashboardSidebar(activePath) + @DashboardSidebar(activePath, tablos) @DashboardMainContentWithClass(mainClass, content)
@@ -28,7 +29,7 @@ templ DashboardPageWithMainClass(activePath string, mainClass string, content te } templ DashboardNotFoundPage(displayName string, email string) { - @DashboardPage("", NotFoundContent(displayName)) + @DashboardPage("", nil, NotFoundContent(displayName)) } templ DashboardMainContent(content templ.Component) { @@ -41,16 +42,16 @@ templ DashboardMainContentWithClass(mainClass string, content templ.Component) { } -templ DashboardContentSwap(activePath string, content templ.Component) { - @DashboardContentSwapWithMainClass(activePath, "dashboard-main flex-1 overflow-auto", content) +templ DashboardContentSwap(activePath string, tablos []tablomodel.Record, content templ.Component) { + @DashboardContentSwapWithMainClass(activePath, tablos, "dashboard-main flex-1 overflow-auto", content) } -templ DashboardContentSwapWithMainClass(activePath string, mainClass string, content templ.Component) { +templ DashboardContentSwapWithMainClass(activePath string, tablos []tablomodel.Record, mainClass string, content templ.Component) { @DashboardMainContentWithClass(mainClass, content) - @DashboardNavOOB(activePath) + @DashboardNavOOB(activePath, tablos) } -templ DashboardSidebar(activePath string) { +templ DashboardSidebar(activePath string, tablos []tablomodel.Record) { } -templ DashboardNavOOB(activePath string) { +templ DashboardNavOOB(activePath string, tablos []tablomodel.Record) { for _, item := range sidebarPrimaryNavItems(activePath) { @SidebarNavItemOOB(item) } + @SidebarProjectsSectionOOB(tablos) for _, item := range sidebarFooterNavItems(activePath) { @SidebarNavItemOOB(item) } } +templ SidebarProjectsSection(tablos []tablomodel.Record) { + +} + +templ SidebarProjectsSectionOOB(tablos []tablomodel.Record) { + +} + templ SidebarOrganization() { ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -345,7 +336,7 @@ func DashboardSidebar(activePath string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -353,7 +344,7 @@ func DashboardSidebar(activePath string) templ.Component { }) } -func DashboardNavOOB(activePath string) templ.Component { +func DashboardNavOOB(activePath string, tablos []tablomodel.Record) 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 { @@ -380,6 +371,10 @@ func DashboardNavOOB(activePath string) templ.Component { return templ_7745c5c3_Err } } + templ_7745c5c3_Err = SidebarProjectsSectionOOB(tablos).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } for _, item := range sidebarFooterNavItems(activePath) { templ_7745c5c3_Err = SidebarNavItemOOB(item).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { @@ -390,6 +385,100 @@ func DashboardNavOOB(activePath string) templ.Component { }) } +func SidebarProjectsSection(tablos []tablomodel.Record) 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, 16, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func SidebarProjectsSectionOOB(tablos []tablomodel.Record) 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_Var13 := templ.GetChildren(ctx) + if templ_7745c5c3_Var13 == nil { + templ_7745c5c3_Var13 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + func SidebarOrganization() 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 @@ -406,12 +495,12 @@ func SidebarOrganization() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var12 := templ.GetChildren(ctx) - if templ_7745c5c3_Var12 == nil { - templ_7745c5c3_Var12 = templ.NopComponent + templ_7745c5c3_Var14 := templ.GetChildren(ctx) + if templ_7745c5c3_Var14 == nil { + templ_7745c5c3_Var14 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -435,12 +524,12 @@ func OverviewMainContent(displayName string, email string, tablos []TabloCardVie }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var13 := templ.GetChildren(ctx) - if templ_7745c5c3_Var13 == nil { - templ_7745c5c3_Var13 = templ.NopComponent + templ_7745c5c3_Var15 := templ.GetChildren(ctx) + if templ_7745c5c3_Var15 == nil { + templ_7745c5c3_Var15 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -460,7 +549,7 @@ func OverviewMainContent(displayName string, email string, tablos []TabloCardVie if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -484,9 +573,9 @@ func TasksMainContent() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var14 := templ.GetChildren(ctx) - if templ_7745c5c3_Var14 == nil { - templ_7745c5c3_Var14 = templ.NopComponent + templ_7745c5c3_Var16 := templ.GetChildren(ctx) + if templ_7745c5c3_Var16 == nil { + templ_7745c5c3_Var16 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = AppSectionMainContent("Tâches", "Suivez les tâches de votre équipe, les priorités en cours et ce qui reste à livrer.").Render(ctx, templ_7745c5c3_Buffer) @@ -513,9 +602,9 @@ func TablosMainContent() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var15 := templ.GetChildren(ctx) - if templ_7745c5c3_Var15 == nil { - templ_7745c5c3_Var15 = templ.NopComponent + templ_7745c5c3_Var17 := templ.GetChildren(ctx) + if templ_7745c5c3_Var17 == nil { + templ_7745c5c3_Var17 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = AppSectionMainContent("Projets", "Gardez une vue claire sur vos tablos, leur état d'avancement et les prochaines décisions à prendre.").Render(ctx, templ_7745c5c3_Buffer) @@ -542,9 +631,9 @@ func PlanningMainContent() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var16 := templ.GetChildren(ctx) - if templ_7745c5c3_Var16 == nil { - templ_7745c5c3_Var16 = templ.NopComponent + templ_7745c5c3_Var18 := templ.GetChildren(ctx) + if templ_7745c5c3_Var18 == nil { + templ_7745c5c3_Var18 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = AppSectionMainContent("Planning", "Visualisez le rythme de l'équipe, les jalons à venir et les arbitrages de charge.").Render(ctx, templ_7745c5c3_Buffer) @@ -571,9 +660,9 @@ func ChatMainContent() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var17 := templ.GetChildren(ctx) - if templ_7745c5c3_Var17 == nil { - templ_7745c5c3_Var17 = templ.NopComponent + templ_7745c5c3_Var19 := templ.GetChildren(ctx) + if templ_7745c5c3_Var19 == nil { + templ_7745c5c3_Var19 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = AppSectionMainContent("Discussions", "Retrouvez les conversations importantes, les décisions récentes et les échanges à relancer.").Render(ctx, templ_7745c5c3_Buffer) @@ -600,9 +689,9 @@ func FilesMainContent() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var18 := templ.GetChildren(ctx) - if templ_7745c5c3_Var18 == nil { - templ_7745c5c3_Var18 = templ.NopComponent + templ_7745c5c3_Var20 := templ.GetChildren(ctx) + if templ_7745c5c3_Var20 == nil { + templ_7745c5c3_Var20 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = AppSectionMainContent("Fichiers", "Centralisez les documents utiles, les pièces partagées et les ressources de travail.").Render(ctx, templ_7745c5c3_Buffer) @@ -629,9 +718,9 @@ func FeedbackMainContent() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var19 := templ.GetChildren(ctx) - if templ_7745c5c3_Var19 == nil { - templ_7745c5c3_Var19 = templ.NopComponent + templ_7745c5c3_Var21 := templ.GetChildren(ctx) + if templ_7745c5c3_Var21 == nil { + templ_7745c5c3_Var21 = templ.NopComponent } ctx = templ.ClearChildren(ctx) templ_7745c5c3_Err = AppSectionMainContent("Feedback", "Collectez les retours produit, priorisez les signaux et transformez-les en actions concrètes.").Render(ctx, templ_7745c5c3_Buffer) @@ -658,38 +747,38 @@ func AppSectionMainContent(title string, description string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var20 := templ.GetChildren(ctx) - if templ_7745c5c3_Var20 == nil { - templ_7745c5c3_Var20 = templ.NopComponent + templ_7745c5c3_Var22 := templ.GetChildren(ctx) + if templ_7745c5c3_Var22 == nil { + templ_7745c5c3_Var22 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "
Espace de travail

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "
Espace de travail

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var21 string - templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(title) + var templ_7745c5c3_Var23 string + templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 162, Col: 14} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 182, Col: 14} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var22 string - templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(description) + var templ_7745c5c3_Var24 string + templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(description) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 163, Col: 19} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 183, Col: 19} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -713,25 +802,25 @@ func NotFoundContent(displayName string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var23 := templ.GetChildren(ctx) - if templ_7745c5c3_Var23 == nil { - templ_7745c5c3_Var23 = templ.NopComponent + templ_7745c5c3_Var25 := templ.GetChildren(ctx) + if templ_7745c5c3_Var25 == nil { + templ_7745c5c3_Var25 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "
Erreur de navigation
404

Page introuvable

Cette page n'existe pas ou n'est plus disponible.

Retour à l'aperçu
Connecté en tant que ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "
Erreur de navigation
404

Page introuvable

Cette page n'existe pas ou n'est plus disponible.

Retour à l'aperçu
Connecté en tant que ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var24 string - templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(dashboardGreetingName(displayName)) + var templ_7745c5c3_Var26 string + templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(dashboardGreetingName(displayName)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 183, Col: 48} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 203, Col: 48} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -755,38 +844,38 @@ func OverviewHeader(displayName string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var25 := templ.GetChildren(ctx) - if templ_7745c5c3_Var25 == nil { - templ_7745c5c3_Var25 = templ.NopComponent + templ_7745c5c3_Var27 := templ.GetChildren(ctx) + if templ_7745c5c3_Var27 == nil { + templ_7745c5c3_Var27 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var26 string - templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(dashboardTodayLabel()) + var templ_7745c5c3_Var28 string + templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(dashboardTodayLabel()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 191, Col: 50} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 211, Col: 50} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "

Bonjour, ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "

Bonjour, ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var27 string - templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(dashboardGreetingName(displayName)) + var templ_7745c5c3_Var29 string + templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(dashboardGreetingName(displayName)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 193, Col: 84} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 213, Col: 84} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "!

Founder
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "!

Founder") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -800,7 +889,7 @@ func OverviewHeader(displayName string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -824,12 +913,12 @@ func OverviewActions(actions []quickAction) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var28 := templ.GetChildren(ctx) - if templ_7745c5c3_Var28 == nil { - templ_7745c5c3_Var28 = templ.NopComponent + 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, 30, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -839,7 +928,7 @@ func OverviewActions(actions []quickAction) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -863,12 +952,12 @@ func OverviewProjectsSection(projects []TabloCardView) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var29 := templ.GetChildren(ctx) - if templ_7745c5c3_Var29 == nil { - templ_7745c5c3_Var29 = templ.NopComponent + templ_7745c5c3_Var31 := templ.GetChildren(ctx) + if templ_7745c5c3_Var31 == nil { + templ_7745c5c3_Var31 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "

Mes Projets

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "

Mes Projets

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -887,7 +976,7 @@ func OverviewProjectsSection(projects []TabloCardView) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -895,7 +984,7 @@ func OverviewProjectsSection(projects []TabloCardView) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -923,39 +1012,39 @@ func SeeMoreProjects(hiddenCount int) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var30 := templ.GetChildren(ctx) - if templ_7745c5c3_Var30 == nil { - templ_7745c5c3_Var30 = templ.NopComponent + templ_7745c5c3_Var32 := templ.GetChildren(ctx) + if templ_7745c5c3_Var32 == nil { + templ_7745c5c3_Var32 = templ.NopComponent } ctx = templ.ClearChildren(ctx) if hiddenCount > 0 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, " de plus ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -980,12 +1069,12 @@ func OverviewProjectsScript() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var33 := templ.GetChildren(ctx) - if templ_7745c5c3_Var33 == nil { - templ_7745c5c3_Var33 = templ.NopComponent + templ_7745c5c3_Var35 := templ.GetChildren(ctx) + if templ_7745c5c3_Var35 == nil { + templ_7745c5c3_Var35 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1009,12 +1098,12 @@ func OverviewTasks(tasks []dashboardTask) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var34 := templ.GetChildren(ctx) - if templ_7745c5c3_Var34 == nil { - templ_7745c5c3_Var34 = templ.NopComponent + templ_7745c5c3_Var36 := templ.GetChildren(ctx) + if templ_7745c5c3_Var36 == nil { + templ_7745c5c3_Var36 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "

Mes Tâches

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "

Mes Tâches

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1024,7 +1113,7 @@ func OverviewTasks(tasks []dashboardTask) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1048,12 +1137,12 @@ func QuickActionCard(action quickAction) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var35 := templ.GetChildren(ctx) - if templ_7745c5c3_Var35 == nil { - templ_7745c5c3_Var35 = templ.NopComponent + templ_7745c5c3_Var37 := templ.GetChildren(ctx) + if templ_7745c5c3_Var37 == nil { + templ_7745c5c3_Var37 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1111,39 +1200,17 @@ func TaskRow(task dashboardTask) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var38 := templ.GetChildren(ctx) - if templ_7745c5c3_Var38 == nil { - templ_7745c5c3_Var38 = templ.NopComponent + templ_7745c5c3_Var40 := templ.GetChildren(ctx) + if templ_7745c5c3_Var40 == nil { + templ_7745c5c3_Var40 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - var templ_7745c5c3_Var39 = []any{taskRowClass(task.Completed)} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var39...) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var41 = []any{taskCheckClass(task.Completed)} + var templ_7745c5c3_Var41 = []any{taskRowClass(task.Completed)} templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var41...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var43 string - templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(task.Title) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 338, Col: 18} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var44 = []any{"task-project-badge " + projectAccentClass(task.ProjectHue)} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var44...) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var45 string - templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var44).String()) + templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(task.Title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 1, Col: 0} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 358, Col: 18} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var45) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\">") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var46 string - templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(task.ProjectKey) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 341, Col: 28} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) + var templ_7745c5c3_Var46 = []any{"task-project-badge " + projectAccentClass(task.ProjectHue)} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var46...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var48 string - templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(task.Date) + templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(task.ProjectKey) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 344, Col: 39} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 361, Col: 28} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var49 = []any{"task-status " + toneClass(task.StatusTone)} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var49...) + var templ_7745c5c3_Var49 string + templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(task.Project) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 363, Col: 50} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var50 string - templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var49).String()) + templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(task.Date) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 364, Col: 39} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var51 = []any{"task-status " + toneClass(task.StatusTone)} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var51...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var51 string - templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(task.Status) + var templ_7745c5c3_Var53 string + templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(task.Status) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 347, Col: 75} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 367, Col: 75} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1299,69 +1388,69 @@ func SidebarNavItem(item sidebarNavItem) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var52 := templ.GetChildren(ctx) - if templ_7745c5c3_Var52 == nil { - templ_7745c5c3_Var52 = templ.NopComponent + templ_7745c5c3_Var54 := templ.GetChildren(ctx) + if templ_7745c5c3_Var54 == nil { + templ_7745c5c3_Var54 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - var templ_7745c5c3_Var53 = []any{sidebarNavItemClass(item.Active)} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var53...) + var templ_7745c5c3_Var55 = []any{sidebarNavItemClass(item.Active)} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var55...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1406,69 +1495,69 @@ func SidebarNavItemOOB(item sidebarNavItem) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var59 := templ.GetChildren(ctx) - if templ_7745c5c3_Var59 == nil { - templ_7745c5c3_Var59 = templ.NopComponent + templ_7745c5c3_Var61 := templ.GetChildren(ctx) + if templ_7745c5c3_Var61 == nil { + templ_7745c5c3_Var61 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - var templ_7745c5c3_Var60 = []any{sidebarNavItemClass(item.Active)} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var60...) + var templ_7745c5c3_Var62 = []any{sidebarNavItemClass(item.Active)} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var62...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1513,25 +1602,25 @@ func SidebarProjectItem(item sidebarProjectItem) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var66 := templ.GetChildren(ctx) - if templ_7745c5c3_Var66 == nil { - templ_7745c5c3_Var66 = templ.NopComponent + templ_7745c5c3_Var68 := templ.GetChildren(ctx) + if templ_7745c5c3_Var68 == nil { + templ_7745c5c3_Var68 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1539,20 +1628,20 @@ func SidebarProjectItem(item sidebarProjectItem) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var68 string - templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(item.Label) + var templ_7745c5c3_Var70 string + templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinStringErrs(item.Label) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 382, Col: 50} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/dashboard_components.templ`, Line: 402, Col: 50} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/go-backend/internal/web/views/dashboard_components_test.go b/go-backend/internal/web/views/dashboard_components_test.go index 7d67bd5..4a9ce94 100644 --- a/go-backend/internal/web/views/dashboard_components_test.go +++ b/go-backend/internal/web/views/dashboard_components_test.go @@ -16,7 +16,7 @@ func TestOverviewProjectsFromTablosCarriesColorAndEditURL(t *testing.T) { record := tablomodel.Record{ ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"), Name: "Palette", - Color: "#3B82F6", + Color: "#22C55E", Status: tablomodel.StatusTodo, CreatedAt: time.Date(2026, time.May, 10, 9, 0, 0, 0, time.UTC), } @@ -27,7 +27,7 @@ func TestOverviewProjectsFromTablosCarriesColorAndEditURL(t *testing.T) { } project := projects[0] - if project.Color != "#3B82F6" { + if project.Color != "#22C55E" { t.Fatalf("expected color to be preserved, got %q", project.Color) } if project.EditRequestURL != "/tablos/11111111-1111-1111-1111-111111111111/edit" { @@ -36,13 +36,16 @@ func TestOverviewProjectsFromTablosCarriesColorAndEditURL(t *testing.T) { if project.CardDateLabel != "10 mai 2026" { t.Fatalf("expected French card date label, got %q", project.CardDateLabel) } + if project.IconKind != "leaf" { + t.Fatalf("expected color presentation icon to be preserved, got %q", project.IconKind) + } } func TestOverviewProjectsSectionRendersColorAndEditAction(t *testing.T) { record := tablomodel.Record{ ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"), Name: "Palette", - Color: "#3B82F6", + Color: "#22C55E", Status: tablomodel.StatusTodo, CreatedAt: time.Date(2026, time.May, 10, 9, 0, 0, 0, time.UTC), } @@ -50,9 +53,10 @@ func TestOverviewProjectsSectionRendersColorAndEditAction(t *testing.T) { html := renderViewToString(t, OverviewProjectsSection(OverviewProjectsFromTablos([]tablomodel.Record{record}))) for _, want := range []string{ - `style="--project-color:#3B82F6;"`, + `style="--project-color:#22C55E;"`, `aria-label="Modifier le projet"`, `hx-get="/tablos/11111111-1111-1111-1111-111111111111/edit"`, + ``, } { if !strings.Contains(html, want) { t.Fatalf("expected %q in %q", want, html) @@ -83,6 +87,36 @@ func TestTabloListRowRendersLeafIconKind(t *testing.T) { } } +func TestSidebarProjectItemsUsesFirstFourRealTablos(t *testing.T) { + tablos := []tablomodel.Record{ + {ID: uuid.MustParse("11111111-1111-1111-1111-111111111111"), Name: "Alpha", Color: "#3B82F6"}, + {ID: uuid.MustParse("22222222-2222-2222-2222-222222222222"), Name: "Beta", Color: "#22C55E"}, + {ID: uuid.MustParse("33333333-3333-3333-3333-333333333333"), Name: "Gamma", Color: "#A855F7"}, + {ID: uuid.MustParse("44444444-4444-4444-4444-444444444444"), Name: "Delta", Color: "#EF4444"}, + {ID: uuid.MustParse("55555555-5555-5555-5555-555555555555"), Name: "Epsilon", Color: "#EAB308"}, + } + + items := sidebarProjectItems(tablos) + if len(items) != 4 { + t.Fatalf("expected 4 sidebar items, got %d", len(items)) + } + + for i, want := range []struct { + href string + label string + icon string + }{ + {href: "/tablos/11111111-1111-1111-1111-111111111111", label: "Alpha", icon: "bolt"}, + {href: "/tablos/22222222-2222-2222-2222-222222222222", label: "Beta", icon: "leaf"}, + {href: "/tablos/33333333-3333-3333-3333-333333333333", label: "Gamma", icon: "gem"}, + {href: "/tablos/44444444-4444-4444-4444-444444444444", label: "Delta", icon: "flame"}, + } { + if items[i].Href != want.href || items[i].Label != want.label || items[i].Icon != want.icon { + t.Fatalf("item %d = %#v, want href=%q label=%q icon=%q", i, items[i], want.href, want.label, want.icon) + } + } +} + func TestTabloListRowDoesNotRenderSpacerBetweenEditAndDelete(t *testing.T) { component := TabloListRow(TabloCardView{ ID: "11111111-1111-1111-1111-111111111111", diff --git a/go-backend/internal/web/views/home.go b/go-backend/internal/web/views/home.go index a7de2eb..e190716 100644 --- a/go-backend/internal/web/views/home.go +++ b/go-backend/internal/web/views/home.go @@ -7,6 +7,7 @@ import ( tablomodel "xtablo-backend/internal/tablos" "xtablo-backend/internal/web/dates" + "xtablo-backend/internal/web/tabloicons" "github.com/a-h/templ" ) @@ -112,6 +113,7 @@ func OverviewProjectsFromTablos(tablos []tablomodel.Record) []TabloCardView { projects := make([]TabloCardView, 0, len(tablos)) for _, tablo := range tablos { statusLabel, statusTone, progress := overviewProjectStatus(tablo.Status) + presentation := tabloicons.ForColor(tablo.Color) projects = append(projects, TabloCardView{ ID: tablo.ID.String(), Name: tablo.Name, @@ -119,8 +121,9 @@ func OverviewProjectsFromTablos(tablos []tablomodel.Record) []TabloCardView { Status: string(tablo.Status), StatusLabel: statusLabel, StatusTone: statusTone, + IconKind: presentation.Icon, Initial: projectInitial(tablo.Name), - Accent: overviewProjectAccent(tablo.Name), + Accent: presentation.Accent, CardDateLabel: dates.FormatFrenchDate(tablo.CreatedAt), Progress: progress, ProgressLabel: progressPercentLabel(progress), @@ -163,13 +166,21 @@ func sidebarPrimaryNavItems(activePath string) []sidebarNavItem { } } -func sidebarProjectItems() []sidebarProjectItem { - return []sidebarProjectItem{ - {Href: "/tablos/hello", Label: "Hello", Icon: "bolt"}, - {Href: "/tablos/atelier", Label: "Atelier Produit", Icon: "gem"}, - {Href: "/tablos/arthur", Label: "Arthur Belleville", Icon: "bolt"}, - {Href: "/tablos/equipe", Label: "Equipe Design", Icon: "bolt"}, +func sidebarProjectItems(tablos []tablomodel.Record) []sidebarProjectItem { + limit := len(tablos) + if limit > 4 { + limit = 4 } + + items := make([]sidebarProjectItem, 0, limit) + for _, tablo := range tablos[:limit] { + items = append(items, sidebarProjectItem{ + Href: fmt.Sprintf("/tablos/%s", tablo.ID), + Label: tablo.Name, + Icon: tabloicons.ForColor(tablo.Color).Icon, + }) + } + return items } func sidebarFooterNavItems(activePath string) []sidebarNavItem { @@ -233,17 +244,6 @@ func overviewProjectStatus(status tablomodel.Status) (string, string, int) { } } -func overviewProjectAccent(name string) string { - switch len(strings.TrimSpace(name)) % 3 { - case 1: - return "purple" - case 2: - return "red" - default: - return "blue" - } -} - func projectInitial(name string) string { name = strings.TrimSpace(name) if name == "" { diff --git a/go-backend/internal/web/views/tablos.templ b/go-backend/internal/web/views/tablos.templ index 589ea28..c943e63 100644 --- a/go-backend/internal/web/views/tablos.templ +++ b/go-backend/internal/web/views/tablos.templ @@ -189,7 +189,11 @@ templ TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) {
- { tablo.Initial } + if tablo.IconKind != "" { + @ActionIcon(tablo.IconKind) + } else { + { tablo.Initial } + }

{ tablo.Name }

diff --git a/go-backend/internal/web/views/tablos_templ.go b/go-backend/internal/web/views/tablos_templ.go index 7598de5..68af53a 100644 --- a/go-backend/internal/web/views/tablos_templ.go +++ b/go-backend/internal/web/views/tablos_templ.go @@ -569,33 +569,48 @@ func TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) templ.C if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var22 string - templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.Initial) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 192, Col: 25} + if tablo.IconKind != "" { + templ_7745c5c3_Err = ActionIcon(tablo.IconKind).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var22 string + templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.Initial) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 195, Col: 26} + } + _, 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, 35, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } } - _, 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, 34, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var23 string templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 194, Col: 19} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 198, Col: 19} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -603,46 +618,46 @@ func TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) templ.C if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var24 string templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.CardDateLabel) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 198, Col: 30} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 202, Col: 30} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "
Progression: ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "
Progression: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var25 string templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.ProgressLabel) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 203, Col: 33} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 207, Col: 33} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\">
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -671,33 +686,33 @@ func TabloListRow(tablo TabloCardView) templ.Component { templ_7745c5c3_Var27 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "
svg]:w-4 [&>svg]:h-4\">") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\">
svg]:w-4 [&>svg]:h-4\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -705,20 +720,20 @@ func TabloListRow(tablo TabloCardView) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var30 string templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 219, Col: 84} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 223, Col: 84} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -729,7 +744,7 @@ func TabloListRow(tablo TabloCardView) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "
svg]:w-4 [&>svg]:h-4 [&>svg]:shrink-0\">") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "
svg]:w-4 [&>svg]:h-4 [&>svg]:shrink-0\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -740,39 +755,39 @@ func TabloListRow(tablo TabloCardView) templ.Component { var templ_7745c5c3_Var31 string templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.CreatedAtLabel) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 231, Col: 26} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 235, Col: 26} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "\">
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var33 string templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(tablo.ProgressLabel) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 239, Col: 109} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 243, Col: 109} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -784,7 +799,7 @@ func TabloListRow(tablo TabloCardView) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -845,7 +860,7 @@ func InitProjectFilterScript() templ.Component { templ_7745c5c3_Var35 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -874,7 +889,7 @@ func TabloListHead() templ.Component { templ_7745c5c3_Var36 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "ProjetStatutCréé leProgression") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "ProjetStatutCréé leProgression") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -934,51 +949,51 @@ func CreateTabloModalBody(vm TablosPageViewModel) templ.Component { templ_7745c5c3_Var38 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\"> ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if vm.ErrorMessage != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var41 string templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(vm.ErrorMessage) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 335, Col: 112} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 339, Col: 112} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1015,33 +1030,33 @@ func CreateTabloModalBody(vm TablosPageViewModel) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "
Annuler") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" hx-push-url=\"true\" class=\"ui-button ui-button-solid ui-button-neutral ui-button-md\">Annuler") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1054,7 +1069,7 @@ func CreateTabloModalBody(vm TablosPageViewModel) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1115,7 +1130,7 @@ func EditTabloColorField(vm TablosPageViewModel) templ.Component { templ_7745c5c3_Var45 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1134,20 +1149,20 @@ func EditTabloColorField(vm TablosPageViewModel) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "\" class=\"ui-input tablo-color-picker\" oninput=\"document.getElementById('edit-tablo-color').value=this.value\">
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1176,64 +1191,64 @@ func EditTabloModalBody(vm TablosPageViewModel) templ.Component { templ_7745c5c3_Var47 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "\"> ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if vm.ErrorMessage != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var51 string templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(vm.ErrorMessage) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 425, Col: 112} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 429, Col: 112} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1261,33 +1276,33 @@ func EditTabloModalBody(vm TablosPageViewModel) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "
Annuler") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" hx-push-url=\"true\" class=\"ui-button ui-button-solid ui-button-neutral ui-button-md\">Annuler") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1300,7 +1315,7 @@ func EditTabloModalBody(vm TablosPageViewModel) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/go-backend/static/styles.css b/go-backend/static/styles.css index bc2a6a9..9b89ab0 100644 --- a/go-backend/static/styles.css +++ b/go-backend/static/styles.css @@ -1909,6 +1909,11 @@ td.text-right .borderless-icon-button.ui-icon-button-ghost.ui-icon-button-danger width: 3rem; } +.project-avatar > svg { + height: 1.25rem; + width: 1.25rem; +} + .project-list-icon { background: var(--project-color, var(--color-project-fallback)); color: var(--color-text-inverse);