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.
This commit is contained in:
parent
3232309388
commit
354785edff
12 changed files with 795 additions and 539 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
81
go-backend/internal/web/tabloicons/presentation.go
Normal file
81
go-backend/internal/web/tabloicons/presentation.go
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
|
|
@ -20,7 +21,7 @@ templ DashboardPageWithMainClass(activePath string, mainClass string, content te
|
|||
</head>
|
||||
<body>
|
||||
<div class="dashboard-shell">
|
||||
@DashboardSidebar(activePath)
|
||||
@DashboardSidebar(activePath, tablos)
|
||||
@DashboardMainContentWithClass(mainClass, content)
|
||||
</div>
|
||||
</body>
|
||||
|
|
@ -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) {
|
|||
</main>
|
||||
}
|
||||
|
||||
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) {
|
||||
<aside class="dashboard-sidebar">
|
||||
<nav aria-label="Main navigation" class="sidebar-nav-shell">
|
||||
<div class="sidebar-brand">
|
||||
|
|
@ -75,17 +76,7 @@ templ DashboardSidebar(activePath string) {
|
|||
}
|
||||
}
|
||||
</ul>
|
||||
<div class="sidebar-projects">
|
||||
<hr role="separator"/>
|
||||
<div class="sidebar-section-label">Projets</div>
|
||||
<ul class="sidebar-project-list">
|
||||
for _, item := range sidebarProjectItems() {
|
||||
<li>
|
||||
@SidebarProjectItem(item)
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
@SidebarProjectsSection(tablos)
|
||||
<ul class="sidebar-list sidebar-footer-links" role="list">
|
||||
for _, item := range sidebarFooterNavItems(activePath) {
|
||||
<li>
|
||||
|
|
@ -99,15 +90,44 @@ templ DashboardSidebar(activePath string) {
|
|||
</aside>
|
||||
}
|
||||
|
||||
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) {
|
||||
<div id="sidebar-projects-section" class="sidebar-projects">
|
||||
<hr role="separator"/>
|
||||
<div class="sidebar-section-label">Projets</div>
|
||||
<ul class="sidebar-project-list">
|
||||
for _, item := range sidebarProjectItems(tablos) {
|
||||
<li>
|
||||
@SidebarProjectItem(item)
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ SidebarProjectsSectionOOB(tablos []tablomodel.Record) {
|
||||
<div id="sidebar-projects-section" class="sidebar-projects" hx-swap-oob="outerHTML">
|
||||
<hr role="separator"/>
|
||||
<div class="sidebar-section-label">Projets</div>
|
||||
<ul class="sidebar-project-list">
|
||||
for _, item := range sidebarProjectItems(tablos) {
|
||||
<li>
|
||||
@SidebarProjectItem(item)
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ SidebarOrganization() {
|
||||
<div class="sidebar-organization">
|
||||
<button class="organization-button" aria-label="Organization menu" type="button">
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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"`,
|
||||
`<path d="M11 20A7 7 0 0 1 4 13V6a1 1 0 0 1 1-1h7a7 7 0 0 1 7 7v0a8 8 0 0 1-8 8Z"></path>`,
|
||||
} {
|
||||
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",
|
||||
|
|
|
|||
|
|
@ -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 == "" {
|
||||
|
|
|
|||
|
|
@ -189,7 +189,11 @@ templ TabloGridCardWithAttrs(tablo TabloCardView, attrs templ.Attributes) {
|
|||
</div>
|
||||
<div class="project-card-title-row">
|
||||
<div class="project-avatar">
|
||||
<span>{ tablo.Initial }</span>
|
||||
if tablo.IconKind != "" {
|
||||
@ActionIcon(tablo.IconKind)
|
||||
} else {
|
||||
<span>{ tablo.Initial }</span>
|
||||
}
|
||||
</div>
|
||||
<h4>{ tablo.Name }</h4>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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, "</div></div><div class=\"project-card-title-row\"><div class=\"project-avatar\"><span>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</div></div><div class=\"project-card-title-row\"><div class=\"project-avatar\">")
|
||||
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, "<span>")
|
||||
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, "</span>")
|
||||
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, "</span></div><h4>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</div><h4>")
|
||||
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, "</h4></div><div class=\"project-date-row\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</h4></div><div class=\"project-date-row\">")
|
||||
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, "<span>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<span>")
|
||||
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, "</span></div><div class=\"project-progress\"><div class=\"project-progress-label\"><span>Progression:</span> <strong>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</span></div><div class=\"project-progress\"><div class=\"project-progress-label\"><span>Progression:</span> <strong>")
|
||||
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, "</strong></div><div class=\"project-progress-track\"><div class=\"project-progress-bar\" style=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</strong></div><div class=\"project-progress-track\"><div class=\"project-progress-bar\" style=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(progressInlineStyle(tablo.Progress))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 206, Col: 81}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 210, Col: 81}
|
||||
}
|
||||
_, 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, 39, "\"></div></div></div></article>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\"></div></div></div></article>")
|
||||
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, "<tr class=\"border-t border-[#EAECF0] dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors cursor-pointer\" style=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<tr class=\"border-t border-[#EAECF0] dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors cursor-pointer\" style=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(projectColorVariableStyle(tablo.Color))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 213, Col: 179}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 217, Col: 179}
|
||||
}
|
||||
_, 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, 41, "\" data-project-filter-item data-project-name=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "\" data-project-filter-item data-project-name=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var29 string
|
||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(tablo.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 213, Col: 237}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 217, Col: 237}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\"><td class=\"px-6 py-4\"><div class=\"flex items-center gap-3\"><div class=\"project-list-icon w-8 h-8 rounded-lg flex items-center justify-center shrink-0 overflow-hidden [&>svg]:w-4 [&>svg]:h-4\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\"><td class=\"px-6 py-4\"><div class=\"flex items-center gap-3\"><div class=\"project-list-icon w-8 h-8 rounded-lg flex items-center justify-center shrink-0 overflow-hidden [&>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, "</div><span class=\"font-medium text-gray-900 dark:text-gray-100 truncate\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "</div><span class=\"font-medium text-gray-900 dark:text-gray-100 truncate\">")
|
||||
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, "</span></div></td><td class=\"px-6 py-4 whitespace-nowrap\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</span></div></td><td class=\"px-6 py-4 whitespace-nowrap\">")
|
||||
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, "</td><td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400\"><div class=\"flex items-center gap-1.5 [&>svg]:w-4 [&>svg]:h-4 [&>svg]:shrink-0\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "</td><td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400\"><div class=\"flex items-center gap-1.5 [&>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, "</div></td><td class=\"px-6 py-4\"><div class=\"flex items-center gap-3\"><div class=\"flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-2 min-w-[80px]\"><div class=\"project-progress-bar h-2 rounded-full transition-all\" style=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</div></td><td class=\"px-6 py-4\"><div class=\"flex items-center gap-3\"><div class=\"flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-2 min-w-[80px]\"><div class=\"project-progress-bar h-2 rounded-full transition-all\" style=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var32 string
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(progressInlineStyle(tablo.Progress))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 237, Col: 114}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 241, Col: 114}
|
||||
}
|
||||
_, 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, 47, "\"></div></div><span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100 w-8 text-right\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "\"></div></div><span class=\"text-sm font-semibold text-gray-900 dark:text-gray-100 w-8 text-right\">")
|
||||
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, "</span></div></td><td class=\"px-6 py-4 text-right\"><div class=\"flex items-center justify-end gap-1\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</span></div></td><td class=\"px-6 py-4 text-right\"><div class=\"flex items-center justify-end gap-1\">")
|
||||
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, "</div></td></tr>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</div></td></tr>")
|
||||
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, "<script>\n\t\t(function () {\n\t\t\tfunction normalizeProjectFilterValue(value) {\n\t\t\t\treturn (value || \"\").toLowerCase().trim();\n\t\t\t}\n\t\t\tfunction applyProjectFilter(root) {\n\t\t\t\tif (!root) return;\n\t\t\t\tvar input = root.querySelector(\"[data-project-filter-input]\");\n\t\t\t\tvar results = root.querySelector(\"[data-project-filter-results]\");\n\t\t\t\tvar empty = root.querySelector(\"[data-project-filter-empty]\");\n\t\t\t\tif (!input || !results || !empty) return;\n\t\t\t\tvar query = normalizeProjectFilterValue(input.value);\n\t\t\t\tvar visibleCount = 0;\n\t\t\t\troot.querySelectorAll(\"[data-project-filter-item]\").forEach(function (item) {\n\t\t\t\t\tvar name = normalizeProjectFilterValue(item.getAttribute(\"data-project-name\") || item.textContent);\n\t\t\t\t\tvar matches = query === \"\" || name.indexOf(query) !== -1;\n\t\t\t\t\titem.hidden = !matches;\n\t\t\t\t\tif (matches) {\n\t\t\t\t\t\titem.removeAttribute(\"hidden\");\n\t\t\t\t\t\tvisibleCount += 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\titem.setAttribute(\"hidden\", \"\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tvar hasVisibleResults = visibleCount > 0;\n\t\t\t\tresults.hidden = !hasVisibleResults;\n\t\t\t\tempty.hidden = hasVisibleResults;\n\t\t\t\tif (hasVisibleResults) {\n\t\t\t\t\tresults.removeAttribute(\"hidden\");\n\t\t\t\t\tempty.setAttribute(\"hidden\", \"\");\n\t\t\t\t} else {\n\t\t\t\t\tresults.setAttribute(\"hidden\", \"\");\n\t\t\t\t\tempty.removeAttribute(\"hidden\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tfunction handleProjectFilterInput(event) {\n\t\t\t\tvar input = event.target.closest(\"[data-project-filter-input]\");\n\t\t\t\tif (!input) return;\n\t\t\t\tapplyProjectFilter(input.closest(\"[data-project-filter-root]\"));\n\t\t\t}\n\t\t\tif (!window.xtabloProjectFilterInitialized) {\n\t\t\t\tdocument.addEventListener(\"input\", handleProjectFilterInput);\n\t\t\t\twindow.xtabloProjectFilterInitialized = true;\n\t\t\t}\n\t\t\tdocument.querySelectorAll(\"[data-project-filter-root]\").forEach(applyProjectFilter);\n\t\t})();\n\t</script>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "<script>\n\t\t(function () {\n\t\t\tfunction normalizeProjectFilterValue(value) {\n\t\t\t\treturn (value || \"\").toLowerCase().trim();\n\t\t\t}\n\t\t\tfunction applyProjectFilter(root) {\n\t\t\t\tif (!root) return;\n\t\t\t\tvar input = root.querySelector(\"[data-project-filter-input]\");\n\t\t\t\tvar results = root.querySelector(\"[data-project-filter-results]\");\n\t\t\t\tvar empty = root.querySelector(\"[data-project-filter-empty]\");\n\t\t\t\tif (!input || !results || !empty) return;\n\t\t\t\tvar query = normalizeProjectFilterValue(input.value);\n\t\t\t\tvar visibleCount = 0;\n\t\t\t\troot.querySelectorAll(\"[data-project-filter-item]\").forEach(function (item) {\n\t\t\t\t\tvar name = normalizeProjectFilterValue(item.getAttribute(\"data-project-name\") || item.textContent);\n\t\t\t\t\tvar matches = query === \"\" || name.indexOf(query) !== -1;\n\t\t\t\t\titem.hidden = !matches;\n\t\t\t\t\tif (matches) {\n\t\t\t\t\t\titem.removeAttribute(\"hidden\");\n\t\t\t\t\t\tvisibleCount += 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\titem.setAttribute(\"hidden\", \"\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tvar hasVisibleResults = visibleCount > 0;\n\t\t\t\tresults.hidden = !hasVisibleResults;\n\t\t\t\tempty.hidden = hasVisibleResults;\n\t\t\t\tif (hasVisibleResults) {\n\t\t\t\t\tresults.removeAttribute(\"hidden\");\n\t\t\t\t\tempty.setAttribute(\"hidden\", \"\");\n\t\t\t\t} else {\n\t\t\t\t\tresults.setAttribute(\"hidden\", \"\");\n\t\t\t\t\tempty.removeAttribute(\"hidden\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tfunction handleProjectFilterInput(event) {\n\t\t\t\tvar input = event.target.closest(\"[data-project-filter-input]\");\n\t\t\t\tif (!input) return;\n\t\t\t\tapplyProjectFilter(input.closest(\"[data-project-filter-root]\"));\n\t\t\t}\n\t\t\tif (!window.xtabloProjectFilterInitialized) {\n\t\t\t\tdocument.addEventListener(\"input\", handleProjectFilterInput);\n\t\t\t\twindow.xtabloProjectFilterInitialized = true;\n\t\t\t}\n\t\t\tdocument.querySelectorAll(\"[data-project-filter-root]\").forEach(applyProjectFilter);\n\t\t})();\n\t</script>")
|
||||
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, "<tr class=\"bg-gray-50 dark:bg-gray-800/80 border-b border-[#EAECF0] dark:border-gray-700\"><th class=\"px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider\">Projet</th><th class=\"px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider\">Statut</th><th class=\"px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider\">Créé le</th><th class=\"px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider\">Progression</th><th class=\"px-6 py-3 w-12\"></th></tr>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<tr class=\"bg-gray-50 dark:bg-gray-800/80 border-b border-[#EAECF0] dark:border-gray-700\"><th class=\"px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider\">Projet</th><th class=\"px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider\">Statut</th><th class=\"px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider\">Créé le</th><th class=\"px-6 py-3 text-left text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider\">Progression</th><th class=\"px-6 py-3 w-12\"></th></tr>")
|
||||
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, "<form hx-post=\"/tablos\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" class=\"flex flex-col gap-4\"><input type=\"hidden\" name=\"view\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<form hx-post=\"/tablos\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" class=\"flex flex-col gap-4\"><input type=\"hidden\" name=\"view\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var39 string
|
||||
templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.ResolveAttributeValue(vm.View)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 331, Col: 50}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 335, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var39)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\"> <input type=\"hidden\" name=\"status\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\"> <input type=\"hidden\" name=\"status\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var40 string
|
||||
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.ResolveAttributeValue(vm.Status)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 332, Col: 54}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 336, Col: 54}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var40)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\"> <input type=\"hidden\" name=\"modal\" value=\"create\"> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\"> <input type=\"hidden\" name=\"modal\" value=\"create\"> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if vm.ErrorMessage != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<div class=\"mb-1 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<div class=\"mb-1 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\">")
|
||||
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, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</div>")
|
||||
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, "<div class=\"flex items-center justify-end gap-3\"><a href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<div class=\"flex items-center justify-end gap-3\"><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var42 templ.SafeURL
|
||||
templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(vm.CloseModalHref()))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 365, Col: 45}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 369, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\" hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var43 string
|
||||
templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.ResolveAttributeValue(vm.CloseModalHref())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 366, Col: 32}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 370, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var43)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\" 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</a>")
|
||||
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</a>")
|
||||
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, "</div></form>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "</div></form>")
|
||||
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, "<div class=\"flex items-center gap-3\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<div class=\"flex items-center gap-3\">")
|
||||
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, "<input id=\"edit-tablo-color-picker\" type=\"color\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "<input id=\"edit-tablo-color-picker\" type=\"color\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var46 string
|
||||
templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.ResolveAttributeValue(vm.FormColor)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 408, Col: 23}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 412, Col: 23}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var46)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "\" class=\"ui-input tablo-color-picker\" oninput=\"document.getElementById('edit-tablo-color').value=this.value\"></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "\" class=\"ui-input tablo-color-picker\" oninput=\"document.getElementById('edit-tablo-color').value=this.value\"></div>")
|
||||
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, "<form hx-post=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<form hx-post=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var48 string
|
||||
templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.ResolveAttributeValue(vm.EditSubmitHref())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 417, Col: 31}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 421, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var48)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" class=\"flex flex-col gap-4\"><input type=\"hidden\" name=\"view\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "\" hx-target=\"#app-main-content\" hx-swap=\"outerHTML\" class=\"flex flex-col gap-4\"><input type=\"hidden\" name=\"view\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var49 string
|
||||
templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.ResolveAttributeValue(vm.View)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 422, Col: 50}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 426, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var49)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "\"> <input type=\"hidden\" name=\"status\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\"> <input type=\"hidden\" name=\"status\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var50 string
|
||||
templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(vm.Status)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 423, Col: 54}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 427, Col: 54}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "\"> ")
|
||||
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, "<div class=\"mb-1 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "<div class=\"mb-1 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\">")
|
||||
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, "</div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</div>")
|
||||
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, "<div class=\"flex items-center justify-end gap-3\"><a href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "<div class=\"flex items-center justify-end gap-3\"><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var52 templ.SafeURL
|
||||
templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(vm.CloseModalHref()))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 446, Col: 45}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 450, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "\" hx-get=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "\" hx-get=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var53 string
|
||||
templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.ResolveAttributeValue(vm.CloseModalHref())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 447, Col: 32}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views/tablos.templ`, Line: 451, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var53)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "\" 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</a>")
|
||||
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</a>")
|
||||
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, "</div></form>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "</div></form>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue