Commit graph

41 commits

Author SHA1 Message Date
Arthur Belleville
a30a6f9088
feat(13-02): replace CSS files, migrate card.templ to typed API, update all template hardcodes
- button.css: replaced with go-backend multi-class selector version + ghost variant rules
- badge.css: replaced with go-backend pill-shape version + primary variant
- card.css: replaced with go-backend token-based header/body/footer version
- card.templ: migrated from children passthrough to typed CardProps{Header/Body/Footer}
- ui_test.go: rewrote TestCard_RendersChildren -> TestCard_RendersTypedRegions; added TestBadge_PrimaryVariant; added textComponent helper + io import
- auth_login.templ, auth_signup.templ: migrated Card usage to typed CardProps API
- tablos.templ: migrated TabloCard to typed CardProps API with extracted tabloCardBody
- planning.templ, tasks.templ, events.templ, etapes.templ: all compound button class strings updated to multi-class pattern
- go test ./... passes (all packages green)
- just generate succeeds
2026-05-16 13:55:30 +02:00
Arthur Belleville
409245eac0
fix(12-03): reset discussion composer from htmx form 2026-05-16 10:32:28 +02:00
Arthur Belleville
d15c3748e4
feat(12-03): add discussion SSE stream 2026-05-16 10:18:33 +02:00
Arthur Belleville
3111b6e011
feat(12-02): implement discussion unread badges 2026-05-16 10:15:38 +02:00
Arthur Belleville
c5477e4ceb
feat(12-01): implement discussion tab and posting 2026-05-16 10:11:14 +02:00
Arthur Belleville
bb84d70569
fix(11-02): show planning event dates 2026-05-16 08:39:10 +02:00
Arthur Belleville
2989c0b917
feat(11-01): implement planning agenda page 2026-05-16 07:26:49 +02:00
Arthur Belleville
ebd31b5645
feat(10-03): add event navigation and user range query 2026-05-16 00:35:09 +02:00
Arthur Belleville
614003f165
feat(10-02): add event edit and delete flows 2026-05-16 00:32:55 +02:00
Arthur Belleville
0bfe8cfbb4
feat(10-01): add events calendar creation slice 2026-05-16 00:27:58 +02:00
Arthur Belleville
f9fc7a1e34
fix(09): honor selected etape on task create 2026-05-15 23:46:32 +02:00
Arthur Belleville
ee62ff9f9b
fix(09): refresh etape counts on task delete 2026-05-15 23:02:05 +02:00
Arthur Belleville
0c95049447
fix(09): refresh etape counts on task create 2026-05-15 22:59:01 +02:00
Arthur Belleville
3a3ecf5803
fix(09-04): preserve etape filter on reorder 2026-05-15 22:49:40 +02:00
Arthur Belleville
b22d79d972
feat(09-03): add task etape selector 2026-05-15 22:47:56 +02:00
Arthur Belleville
4af623a57b
feat(09-02): manage etapes 2026-05-15 22:44:50 +02:00
Arthur Belleville
565bb88df5
feat(09-01): add etape task slice 2026-05-15 22:40:25 +02:00
Arthur Belleville
90af9bdaef
feat(08): disable apple sign-in 2026-05-15 21:41:22 +02:00
Arthur Belleville
6e6583636f
feat(08-05): add linked providers view and provider docs 2026-05-15 21:10:45 +02:00
Arthur Belleville
59fd6b15b5
feat(08-04): show social sign-in controls on auth pages 2026-05-15 21:09:14 +02:00
Arthur Belleville
cb7d5d1dd1
test(05-files): add pure unit tests for formatBytes, byteCountReader, and content-type sniff
Gap fill: three no-infrastructure unit tests that run without TEST_DATABASE_URL or S3_ENDPOINT:
- backend/templates/files_helpers_test.go — formatBytes boundary cases (B/KB/MB/GB)
- backend/internal/files/store_unit_test.go — byteCountReader accumulation, io.ErrUnexpectedEOF
  guard for small files, and MultiReader body reconstruction after 512-byte sniff

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 13:29:08 +02:00
Arthur Belleville
9d4dd4f3e2
feat(05-03): implement FileDownloadHandler, FileDeleteConfirmHandler, FileDeleteHandler
- FileDownloadHandler: nil guard → loadOwnedTabloForFile → PresignDownload → 302 redirect (FILE-04)
- FileDeleteConfirmHandler: nil guard → loadOwnedTabloForFile → render FileDeleteConfirmFragment
- FileDeleteHandler: nil guard → loadOwnedTabloForFile → S3 Delete (log+continue) → DeleteTabloFile → FileRowGone HTMX / 303 redirect (FILE-05, FILE-06)
- Add FileDeleteConfirmFragment templ component mirroring TaskDeleteConfirmFragment pattern (T-05-03-05)
2026-05-15 12:34:07 +02:00
Arthur Belleville
a12c5abea6
feat(05-02): 3-tab layout + files templates + router + main.go S3 wiring
- tablos.templ: TabloDetailPage gains files+activeTab params, 3-tab nav with hx-push-url
- tablos.templ: TabloOverviewTabFragment + TasksTabFragment (wraps KanbanBoard) added
- files.templ: FilesTabFragment, FileUploadForm (hx-encoding=multipart/form-data),
  FileListRow, FileListEmpty, FileRowGone, UploadErrorFragment
- files_helpers.go: formatBytes() converts int64 bytes to human-readable string
- router.go: fileDeps FilesDeps param added; TabloTasksTabHandler + file routes wired
- handlers_tablos.go: both TabloDetailPage call sites updated (nil, 'overview')
- main.go: S3_ENDPOINT/S3_BUCKET/S3_REGION env vars read; files.NewStore constructed;
  fileDeps wired; nil filesStore allowed when S3 env unset (503 from handlers)
- All test routers updated to pass FilesDeps{} in new param position
2026-05-15 12:28:33 +02:00
Arthur Belleville
392b5321be
fix(04-CR-02): replace fmt.Fprintf in TaskDeleteHandler with TaskCardGone templ component
The raw fmt.Fprintf bypassed templ's auto-escaping pipeline and was
inconsistent with every other handler. Added TaskCardGone(taskID uuid.UUID)
to tasks.templ and updated TaskDeleteHandler to use it. Ran just generate.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:18:34 +02:00
Arthur Belleville
131c9fd6b3
fix(04): draggable:.task-card-zone — move wrapper not inner card
Sortable.js draggable must match direct children of .sortable-column.
Using .task-card (grandchild) caused Sortable to detach it from its
.task-card-zone wrapper, breaking HTMX OOB swap targets and making
drag appear to do nothing. Changed to .task-card-zone so the full
wrapper moves, keeping id= attributes intact for HTMX round-trips.

Also removed redundant form.dispatchEvent() before htmx.trigger()
which could cause a double submit on reorder.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 10:01:34 +02:00
Arthur Belleville
68f2ccdea3
fix(04): badge count + DnD init — use DOMContentLoaded/htmx:afterSettle
Replace htmx.onLoad (requires htmx at parse time) with native
document.addEventListener('DOMContentLoaded') + 'htmx:afterSettle'
so Sortable.js is guaranteed loaded before init runs.

Add task-count-badge-{status} wrapper IDs and updateBadges() that
recounts .task-card elements on every HTMX settle so badge counts
stay in sync after create, delete, and reorder operations.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 09:56:28 +02:00
Arthur Belleville
2b299e21f4
feat(04-03): implement TaskEditHandler, TaskUpdateHandler, TaskEditFragment
- TaskEditHandler: GET /tablos/{id}/tasks/{task_id}/edit returns TaskEditFragment pre-filled with existing title+description
- TaskUpdateHandler: POST validates title (required, max 255), updates title+description preserving status+position (T-04-12)
- TaskEditFragment: outer .task-card-zone wrapper with outerHTML round-trip, discard restores via /show
- Sortable.js htmx.onLoad init script added to KanbanBoard (Pitfall 2 protection)
- TaskEditFragment added to tasks.templ; remove t.Skip from TestTaskUpdate
2026-05-15 09:37:46 +02:00
Arthur Belleville
889164b437
feat(04-02): KanbanBoard, TaskCard, TaskDeleteConfirmFragment templates
- Add tasks.templ with KanbanBoard, KanbanColumn, TaskCard, TaskCreateFormFragment, TaskDeleteConfirmFragment, AddTaskTrigger, TaskCardOOB
- Add TaskColumns/TaskColumnLabels to tasks_forms.go (moved from web package to break import cycle)
- Update TabloDetailPage signature to accept tasks []sqlc.Task; embed KanbanBoard below tablo header
- Update handlers_tablos.go TabloDetailHandler to fetch tasks via ListTasksByTablo
- Update layout.templ: add sortable.min.js script tag, update footer to Phase 4 · Tasks
2026-05-15 09:32:06 +02:00
Arthur Belleville
8b9543db6f
test(04-01): add RED test scaffold and task form structs
- handlers_tasks_test.go: 9 TestTask* functions (TASK-01..07 + IDOR) all skip
- TasksDeps stub struct declared in test file for Plan 02 wiring
- tasks_forms.go: TaskCreateForm, TaskCreateErrors, TaskUpdateForm, TaskUpdateErrors structs
- go build ./... passes; go test -run TestTask exits 0 with all 9 SKIP
2026-05-15 09:24:05 +02:00
Arthur Belleville
79435602c4
fix(03): WR-04 add color field error display to create form template
- TabloCreateErrors: add Color field for server-side hex validation error
- TabloCreateFormFragment: render FieldError for color field and update
  placeholder to hex-only hint (#6366f1) matching the validation constraint

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 08:30:27 +02:00
Arthur Belleville
6f167e2956
feat(03-03): detail page, edit and delete templ fragments + TabloUpdateErrors
- TabloDetailPage: full detail layout with title/desc/delete zones
- TabloTitleDisplay/EditFragment: outerHTML-swappable title zone with _zone=title hidden field
- TabloDescDisplay/EditFragment: outerHTML-swappable desc zone with _zone=desc hidden field
- TabloDeleteButtonFragment: canonical single-source delete zone (TabloCard now delegates here)
- TabloDeleteConfirmFragment: inline confirm with "Delete tablo?", "Yes, delete", "Keep tablo"
- TabloNotFoundPage: 404 page with UI-SPEC copy
- TabloUpdateErrors struct added to tablos_forms.go
- just generate + go build ./... both exit 0
2026-05-15 07:59:10 +02:00
Arthur Belleville
c08da7f5bd
fix(03-02): delete retired index.templ to stop templ generating stale imports 2026-05-15 07:51:50 +02:00
Arthur Belleville
5db9215a73
feat(03-02): tablo handlers + router wiring — list/new/create green
- Implement TablosListHandler, TablosNewHandler, TablosCreateHandler in
  handlers_tablos.go replacing the Plan 01 stub
- TablosCreateHandler: reads via r.PostFormValue, validates title (required,
  <=255), inserts with pgtype.Text nullable params, sends HX-Retarget +
  HX-Reswap on HTMX success, 303 redirect on non-HTMX success
- router.go: replace r.Get("/", IndexHandler()) with TablosListHandler;
  add GET /tablos/new and POST /tablos (static before parametric — Pitfall 1)
- handlers.go: remove IndexHandler + unused auth/csrf imports
- index.templ: reduced to bare package declaration (dashboard moved to tablos.templ)
- index_templ.go: deleted (empty templ file generates broken import)
- TestTabloList, TestTabloList_Empty, TestTabloCreate, TestTabloCreate_Validation: PASS
- TestSignup, TestLogin, TestLogout, TestCSRF: still PASS (no regression)
2026-05-15 00:20:25 +02:00
Arthur Belleville
43ddf25364
feat(03-02): tablos templates — dashboard, empty state, card, create form, OOB-clear
- Create backend/templates/tablos.templ with TablosDashboard, TablosEmptyState,
  TabloCard, TabloCreateFormFragment, TabloCardWithOOBFormClear components
- Create backend/templates/tablos_forms.go declaring TabloCreateForm and
  TabloCreateErrors types (mirrors auth_forms.go pattern)
- Update layout.templ footer: "Phase 2 · Authentication" → "Phase 3 · Tablos"
- TabloCardWithOOBFormClear emits OOB div as top-level sibling (Pitfall 5)
- TabloCard guards description/color rendering with pgtype.Text null checks
- All UI-SPEC copywriting copy strings present; templ generate succeeds
2026-05-15 00:17:56 +02:00
Arthur Belleville
389e1bc8b4
feat(02-07): gorilla/csrf integration — mount middleware, wire all forms, env-driven key
- auth.Mount(env, key) wraps csrf.Protect with locked D-14/D-24 options
- auth.LoadKeyFromEnv() reads SESSION_SECRET, hex-decodes, validates 32 bytes; fails fast on error
- ui.CSRFField(token) templ component renders hidden _csrf input
- Layout, LoginPage/Fragment, SignupPage/Fragment, Index all embed @ui.CSRFField(csrfToken)
- Handlers thread csrf.Token(r) into every page/fragment render call
- NewRouter mounts auth.Mount after ResolveSession, before all route groups (D-24)
- main.go calls auth.LoadKeyFromEnv(); logs.Fatalf on missing/invalid SESSION_SECRET
- SESSION_SECRET documented in .env.example with openssl rand -hex 32 instruction
- go.mod: gorilla/csrf v1.7.3 (direct); prior tests updated with getCSRFToken helper
- All Plan 04/05/06 tests updated to acquire and submit valid _csrf tokens
2026-05-14 22:59:06 +02:00
Arthur Belleville
8b54ff4bec
feat(02-06): implement logout, protect GET /, and update layout with auth state
- Add LogoutHandler: deletes session row (D-06), clears cookie, redirects to /login
- Protect GET / inside RequireAuth group; remove old top-level registration
- Add POST /logout inside same RequireAuth group (D-22: POST-only logout)
- Update Layout signature to accept *auth.User; render logout form + email when authed
- Update Index template to accept *auth.User and show "Signed in as {email}"
- Update SignupPage/LoginPage to pass nil to Layout (auth pages are unauthed)
- Update IndexHandler to pull user from auth.Authed(ctx) and pass to template
- Update TestIndex_RendersHxGet -> TestIndex_UnauthRedirects (GET / now protected)
- AUTH-04 (logout) and AUTH-05 (protected /) are now closed
2026-05-14 22:40:10 +02:00
Arthur Belleville
b5c3fc4d48
test(02-06): add failing tests for logout, protected routes, and layout auth
- TestLogout_Success: POST /logout with valid cookie -> 303, cookie cleared, session deleted
- TestLogout_UnauthRedirectsToLogin: POST /logout without cookie -> 303 from RequireAuth
- TestLogout_HXRedirect: HTMX logout -> 200 + HX-Redirect: /login
- TestLogout_AfterLogoutSubsequentRequestUnauth: stale cookie blocked after logout
- TestProtected_HomeUnauthRedirects: GET / without session -> 303 /login
- TestProtected_HomeUnauthHXRedirect: HTMX GET / without session -> 200 + HX-Redirect
- TestProtected_HomeAuthRendersUserEmail: authed GET / -> 200 with user email
- TestLayout_LogoutFormVisibleWhenAuthed: Layout with user shows logout form
- TestLayout_LogoutFormHiddenWhenUnauthed: Layout with nil user hides logout form
2026-05-14 22:32:33 +02:00
Arthur Belleville
7d8c498980
feat(02-05): login vertical slice with rate limiting
- auth_login.templ: LoginPage + LoginFormFragment (mirrors signup shape)
- LoginForm + LoginErrors types added to templates/auth_forms.go
- LoginPageHandler + LoginPostHandler in handlers_auth.go
  - Rate-limit check before user lookup (D-16, T-2-14)
  - Single errInvalidCreds constant for D-20 enumeration defense
  - Session rotation via Store.Rotate on success (D-10, T-2-04)
  - HTMX-aware redirect and fragment responses (D-19, D-21)
- AuthDeps extended with Limiter *auth.LimiterStore field
- router.go: GET /login in RedirectIfAuthed group (D-23)
- main.go: LimiterStore created with janitor goroutine (D-16)
- Export NewLimiterStoreWithClock + SetLimiterClock for cross-package tests
- 12 TestLogin_* integration tests all pass with real DB
2026-05-14 22:27:54 +02:00
Arthur Belleville
73935ed11c
feat(02-04): signup templates (full page + HTMX fragment) with render tests
- Create auth_form_errors.templ: FieldError and GeneralError primitives
- Create auth_signup.templ: SignupPage (full) and SignupFormFragment (HTMX swap target)
- Define SignupForm and SignupErrors types in templates/auth_forms.go
- Add three smoke tests: renders form, renders errors, does not echo password
2026-05-14 22:14:28 +02:00
Arthur Belleville
3a12f8f47d
feat(01-03): templ layout/index/fragments + handlers + chi router
- templates/layout.templ: base HTML shell per UI-SPEC §Base Layout Contract
  (max-w-5xl container, slate-50 header, slate-200 borders, footer copy,
  /static/tailwind.css in <head>, /static/htmx.min.js deferred at body end —
  D-10: HTMX never loaded from a CDN)
- templates/index.templ: root page consuming @ui.Card and @ui.Button for the
  canonical HTMX demo (UI-SPEC §Component Library Contract canonical block)
- templates/fragments.templ: TimeFragment renders <span> with RFC3339 UTC
  timestamp; templ auto-escapes interpolation (T-01-13)
- internal/web/handlers.go: HealthzHandler (200 ok / 503 degraded per D-20,
  2s Ping timeout), IndexHandler, DemoTimeHandler with injected clock
- internal/web/router.go: Pinger interface; NewRouter wires
  RequestIDMiddleware → RealIP → SlogLoggerMiddleware → Recoverer (D-08
  + Pitfall 6 — chi middleware.Logger deliberately NOT registered) and
  routes /, /healthz, /demo/time, /static/* via http.FileServer (T-01-08
  path traversal blocked by http.Dir)

All six handler tests + ui package tests are GREEN under default go test.
2026-05-14 19:25:43 +02:00
Arthur Belleville
4de96854b5
feat(01-01): create directory skeleton and per-package doc.go placeholders
- internal/{db,session,tablos,tasks,files}/doc.go (D-02 placeholder packages)
- internal/db/{queries,sqlc}, templates/, migrations/, bin/, static/ via .gitkeep
- 'go build ./internal/...' compiles cleanly
- cmd/web, cmd/worker, internal/web are deliberately deferred to Plans 01-02 / 01-03
2026-05-14 17:53:55 +02:00