+
+
+From backend/internal/web/handlers_files.go (after Plan 02):
+ type FilesDeps struct {
+ Queries *sqlc.Queries
+ Files FileStorer
+ MaxUploadMB int
+ }
+ func loadOwnedTabloForFile(w, r, deps FilesDeps) (sqlc.Tablo, sqlc.TabloFile, *auth.User, bool)
+ // Stub handlers replaced in this plan:
+ func FileDownloadHandler(deps FilesDeps) http.HandlerFunc // currently returns 501
+ func FileDeleteConfirmHandler(deps FilesDeps) http.HandlerFunc // currently returns 501
+ func FileDeleteHandler(deps FilesDeps) http.HandlerFunc // currently returns 501
+
+From backend/internal/files/store.go (FileStorer interface from Plan 01):
+ type FileStorer interface {
+ Upload(ctx context.Context, key string, file io.Reader) (contentType string, bytesWritten int64, err error)
+ Delete(ctx context.Context, key string) error
+ PresignDownload(ctx context.Context, key string) (string, error)
+ }
+
+From backend/internal/db/sqlc/files.sql.go (from Plan 01 generation):
+ type GetTabloFileByIDParams struct { ID pgtype.UUID; TabloID pgtype.UUID }
+ type DeleteTabloFileParams struct { ID pgtype.UUID; TabloID pgtype.UUID }
+ func (q *Queries) GetTabloFileByID(ctx, arg GetTabloFileByIDParams) (TabloFile, error)
+ func (q *Queries) DeleteTabloFile(ctx, arg DeleteTabloFileParams) error
+
+From backend/templates/tasks.templ (TaskDeleteConfirmFragment pattern — exact template to mirror):
+ templ TaskDeleteConfirmFragment(tabloID uuid.UUID, task sqlc.Task, csrfToken string) {
+
+
+ // cancel button that re-fetches the display fragment
+
+ }
+
+From backend/internal/web/handlers_tasks.go (delete handler pattern lines 285-309):
+ func TaskDeleteHandler(deps TasksDeps) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ tablo, task, _, ok := loadOwnedTabloForTask(w, r, deps)
+ if !ok { return }
+ if err := deps.Queries.DeleteTask(r.Context(), ...); err != nil {
+ slog.Default().Error("tasks delete: DeleteTask failed", "id", task.ID, "err", err)
+ http.Error(w, "internal server error", http.StatusInternalServerError)
+ return
+ }
+ if r.Header.Get("HX-Request") == "true" {
+ _ = templates.TaskCardGone(task.ID).Render(r.Context(), w)
+ return
+ }
+ http.Redirect(w, r, "/tablos/"+tablo.ID.String(), http.StatusSeeOther)
+ }
+ }
+
+