- Dashboard: French header "Mes Projets", underline-tab view toggle (grid/list), filter tabs (Tous/Pas commencé/En cours/Terminé) with JS client-side filtering
- Cards: display status derived from progress (À faire/En cours/Terminé), rounded-xl p-6, w-8 h-8 avatar, green-500 progress bar, dashed "Créé le" footer
- Click on card/row navigates to /tablos/{uuid} via event delegation (delete zone stops propagation)
- List view: single-column grid, rows show status + title + date + task count
- CSS: .view-tab and .filter-tab with .is-active state
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- TestTablosDashboard_ProgressBar: asserts "Progression:" label and project-card-progress-row class
- TestTablosDashboard_ViewToggle: asserts view-toggle-btn class and data-view="grid" attribute
- TestTablosDashboard_StatusBadge: asserts "Active" badge and tablo-list-row dual-element structure
- All tests use unique email addresses; go build ./... exits 0
- Rewrote TabloProjectCard with outer article.tablo-card-wrapper (display:contents)
- Added .project-card child: status badge top-left, delete button top-right
- Avatar circle now shows initial letter from card.Tablo.Title
- Added calendar icon + formatted date in .project-date-row
- Added .project-card-progress-row with "Progression: X%" label and .project-card-progress-bar
- Added .tablo-list-row sibling (hidden by default) for list view toggle
- Added strconv and strings imports for Itoa and title-casing Status
- Add Progress int field to TabloCardView (D-05)
- Map row.Status into Tablo struct in TabloCardsFromUnreadRows
- Wire ListTabloProgressByIDs batch query in TablosListHandler (no N+1)
- Non-fatal error handling: progress defaults to 0 on query failure
- Add migration 0010_tablo_status.sql with reversible goose Up/Down
- Update all tablo SQL queries to include status column in SELECT/RETURNING/GROUP BY
- Add ListTabloProgressByIDs batch aggregation query (D-06)
- sqlc regenerated locally (files gitignored — regen via sqlc generate)
- Sidebar: Tailwind utilities, French labels (Aperçu/Tâches/Projets/Planning/Discussions/Fichiers), hr separators, collapse button on group hover, tablo list with circle icons, org footer with avatar + "1 membre"
- Header: 75px height, border-b #EAECF0, search input left, bell + avatar-dropdown right (no breadcrumb visible — retained sr-only for a11y + tests)
- Layout: flex instead of grid, dashboard-main no padding, dashboard-main-content wrapper at 2rem
- Tests updated for new search-input assertion
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Add PageHeader templ component with three-zone layout (breadcrumb left, search placeholder center, bell/inbox/avatar right)
- Avatar dropdown uses native details/summary HTML (D-06, no Alpine.js)
- Logout form inside dropdown with CSRF token (T-18-03-01 mitigated)
- Remove Plan 01 breadcrumb/headerActions stubs from AppLayout body
- Wire @PageHeader call inside dashboard-main before children
- Add inline JS for avatar dropdown outside-click close behavior
- Add .dashboard-shell.sidebar-is-collapsed grid-template-columns: 4rem 1fr
- Hide sidebar-brand-title, section labels, nav labels, project labels when collapsed
- Center icons in collapsed state via justify-content: center on sidebar-nav-link-inner
- Hide sidebar-project-list in collapsed state (no icons for project entries)
- Flip collapse button chevron with rotate(180deg) when collapsed
- Regenerate static/tailwind.css via bin/tailwindcss --minify
- Add GENERAL section label above primary nav items list
- Add PROJECTS section label and tablo list inlined directly in DashboardSidebar
- Wire collapse button with inline JS toggling sidebar-is-collapsed on .dashboard-shell
- Remove SidebarOrganizationFooter call (moves to avatar dropdown in Plan 03)
- Remove DividerAfter rendering branch (section labels replace dividers)
- Replace Dashboard/Tasks/Planning/Chat/Files with Home/My Tasks/Projects/Events/Team Members
- Remove DividerAfter from all items (section labels replace dividers per D-08)
- Add "team" case to SidebarNavIcon switch using Lucide users SVG paths
Phases 13-17 (design-system-foundation, auth-pages, dashboard-tablos,
tablo-detail, chat-planning) moved from .planning/phases/ to
.planning/milestones/v3.0-phases/ as execution history archive.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Fresh REQUIREMENTS.md will be created for v4.0 via /gsd-new-milestone.
History preserved — see .planning/milestones/v3.0-REQUIREMENTS.md.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- PlanningShowDaySeparator in planning_forms.go — returns true when date
changes between consecutive events
- PlanningDaySeparator templ: slate-50 header row with date label and
data-day-separator attribute
- PlanningEventListItem: remove redundant DateLabel column (now in separator)
- Loop uses index to call PlanningShowDaySeparator before each event row
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- #discussion-messages: max-height 32rem, overflow-y auto, smooth scroll
- Auto-scroll to bottom on initial load, on own message sent (htmx:afterRequest),
and on SSE message received from other users
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Remove static/ from exclude_dir, add js to include_ext.
Exclude static/tailwind.css via regex to prevent rebuild loop from
the Tailwind output file triggering its own regeneration.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The server-side flush didn't eliminate the race because browser HTMX and SSE
event processing are inherently async.
Real fix: embed data-current-user-id on #discussion-tab (from DiscussionTabData.
CurrentUserID). The SSE handler now skips any event whose authorUserId matches
the current user — those messages are always delivered via HTMX (IsOwn=true,
right-aligned). SSE only appends messages from other users.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The server was publishing to the SSE broker before writing the HTMX response,
causing a race: if the SSE event (IsOwn=false, left-aligned) arrived at the
browser before HTMX appended the response (IsOwn=true, right-aligned), the
SSE path won and messageExists() then blocked the correct HTMX append.
Fix: write and flush the HTMX response first, then publish to SSE. This ensures
the sender's own message lands in the DOM right-aligned before the SSE event fires.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- CR-01: add id="discussion-message-list" to .divide-y; change hx-target
to #discussion-message-list so HTMX appends inside the list, not after it.
Always render the list div so the target exists even when messages is empty.
- WR-01: SSE broadcast now renders IsOwn=false for all recipients so other
users don't receive the sender's right-aligned bubble
- WR-02: add HX-Retarget/HX-Reswap headers on 422 and 500 error responses
so validation errors reach the composer form in the DOM
- WR-03: replace hardcoded rgba(128,78,236,0.10) with color-mix() using
the --color-brand-primary token
- WR-04: remove hardcoded "1 participant" subtitle (no participant count in data)
- IN-02: DiscussionMessageFromRow now accepts currentUserID uuid.UUID (matching
DiscussionMessagesFromRows) instead of a pre-computed bool
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
TabloDetailHandler was always rendering the full page regardless of
HX-Request, causing HTMX to swap the entire layout (sidebar, header)
into #tab-content instead of just the overview fragment.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Add IsOwn bool to DiscussionMessageView; set via user.ID comparison in
DiscussionMessagesFromRows and DiscussionMessageFromRow
- Thread currentUserID through loadDiscussionTabData and all call sites
- discussion.templ: branch message-own vs message-other on message.IsOwn
- Restore .divide-y wrapper inside #discussion-messages (discussion-sse.js
depends on it to locate the message list before appending SSE events)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>