From 6f7bcc95032ea16331f3340a9d5ffbeb7448eba8 Mon Sep 17 00:00:00 2001 From: Michael Thomson Date: Mon, 17 Jun 2024 23:37:43 -0400 Subject: [PATCH] postgres and such --- .env.example | 1 + .gitignore | 2 + assets/output.css | 4 + go.mod | 14 ++- go.sum | 29 +++++ handlers/todoHandler.go | 68 ++++++++++- main.go | 113 +++++++++++++++++- models/todo.go | 6 +- .../inMemoryTodoRepository.go | 18 +-- repositories/postgresTodoRepository.go | 86 +++++++++++++ services/todoService.go | 37 ++++-- sql/init.sql | 5 + .../20240617T1630_createtable-todo.sql | 5 + templates/pages/home.templ | 2 +- templates/pages/home_templ.go | 2 +- templates/partials/todo.templ | 11 +- templates/partials/todo_templ.go | 56 ++++++++- 17 files changed, 423 insertions(+), 36 deletions(-) create mode 100644 .env.example rename db/db.go => repositories/inMemoryTodoRepository.go (60%) create mode 100644 repositories/postgresTodoRepository.go create mode 100644 sql/init.sql create mode 100644 sql/migrations/20240617T1630_createtable-todo.sql diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c7b439c --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +DATABASE_URL=postgres://postgres@localhost:5432/todos diff --git a/.gitignore b/.gitignore index e4a4cc3..a692fc7 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ go.work .direnv/ tmp/ + +.env diff --git a/assets/output.css b/assets/output.css index 4c60d1a..26dec3e 100644 --- a/assets/output.css +++ b/assets/output.css @@ -562,6 +562,10 @@ video { display: flex; } +.table { + display: table; +} + .flex-col { flex-direction: column; } diff --git a/go.mod b/go.mod index 687b23c..6c48a02 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,16 @@ go 1.22.1 require github.com/a-h/templ v0.2.707 -require github.com/google/uuid v1.6.0 // indirect +require ( + github.com/gofrs/uuid v4.4.0+incompatible // indirect + github.com/gofrs/uuid/v5 v5.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx-gofrs-uuid v0.0.0-20230224015001-1d428863c2e2 // indirect + github.com/jackc/pgx/v5 v5.6.0 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/joho/godotenv v1.5.1 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/text v0.14.0 // indirect +) diff --git a/go.sum b/go.sum index 8eeab4e..8868e3f 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,35 @@ github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U= github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= +github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx-gofrs-uuid v0.0.0-20230224015001-1d428863c2e2 h1:QWdhlQz98hUe1xmjADOl2mr8ERLrOqj0KWLdkrnNsRQ= +github.com/jackc/pgx-gofrs-uuid v0.0.0-20230224015001-1d428863c2e2/go.mod h1:Ti7pyNDU/UpXKmBTeFgxTvzYDM9xHLiYKMsLdt4b9cg= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handlers/todoHandler.go b/handlers/todoHandler.go index b1ace43..7d784fe 100644 --- a/handlers/todoHandler.go +++ b/handlers/todoHandler.go @@ -3,7 +3,7 @@ package handlers import ( "net/http" - "github.com/google/uuid" + "github.com/gofrs/uuid/v5" "michaelthomson.dev/mthomson/go-todos-app/services" "michaelthomson.dev/mthomson/go-todos-app/templates/partials" ) @@ -42,10 +42,74 @@ func (h *TodoHandler) Create(w http.ResponseWriter, r *http.Request) { } } +func (h *TodoHandler) Done(w http.ResponseWriter, r *http.Request) { + var err error + + id, err := uuid.FromString(r.PathValue("id")) + + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + todo, err := h.ts.GetTodoById(id) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + todo, err = h.ts.UpdateTodo(id, todo.Name, true) + + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + err = partials.Todo(todo).Render(r.Context(), w) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (h *TodoHandler) Undone(w http.ResponseWriter, r *http.Request) { + var err error + + id, err := uuid.FromString(r.PathValue("id")) + + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + todo, err := h.ts.GetTodoById(id) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + todo, err = h.ts.UpdateTodo(id, todo.Name, false) + + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + err = partials.Todo(todo).Render(r.Context(), w) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + func (h *TodoHandler) Delete(w http.ResponseWriter, r *http.Request) { var err error - id, err := uuid.Parse(r.PathValue("id")) + id, err := uuid.FromString(r.PathValue("id")) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) diff --git a/main.go b/main.go index 9bbb8f2..2421dec 100644 --- a/main.go +++ b/main.go @@ -1,20 +1,121 @@ package main import ( + "context" "log" "net/http" + "os" + "path/filepath" - "michaelthomson.dev/mthomson/go-todos-app/db" + pgxuuid "github.com/jackc/pgx-gofrs-uuid" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" + "github.com/joho/godotenv" "michaelthomson.dev/mthomson/go-todos-app/handlers" + "michaelthomson.dev/mthomson/go-todos-app/repositories" "michaelthomson.dev/mthomson/go-todos-app/services" ) func main() { - todosStore := db.NewTodoStore() - ts := services.NewTodoService(&todosStore) + // load environment + err := godotenv.Load() - homeHandler := handlers.NewHomeHandler(ts) - todoHandler := handlers.NewTodoHandler(ts) + if err != nil { + log.Fatalf("Error loading .env file: %v", err) + os.Exit(1) + } + + databaseUrl := os.Getenv("DATABASE_URL") + + // connect to Db + dbconfig, err := pgxpool.ParseConfig(databaseUrl) + if err != nil { + log.Fatalf("Unable to parse db config: %v", err) + os.Exit(1) + } + + dbconfig.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error { + pgxuuid.Register(conn.TypeMap()) + return nil + } + + dbpool, err := pgxpool.New(context.Background(), databaseUrl) + if err != nil { + log.Fatalf("Unable to create connection pool: %v", err) + os.Exit(1) + } + defer dbpool.Close() + + // apply initial sql + log.Print("Applying migrations table") + content, err := os.ReadFile(filepath.Join("sql", "init.sql")) + + if err != nil { + log.Fatal(err) + os.Exit(1) + } + + _, err = dbpool.Exec(context.Background(), string(content)) + + if err != nil { + log.Fatal(err) + os.Exit(1) + } + + //apply migrations + log.Print("Starting migrations...") + files, err := os.ReadDir("./sql/migrations") + if err != nil { + log.Fatal(err) + os.Exit(1) + } + + for _, file := range files { + path := filepath.Join("sql", "migrations", file.Name()) + content, err := os.ReadFile(path) + + if err != nil { + log.Fatal(err) + os.Exit(1) + } + + var count int + err = dbpool.QueryRow(context.Background(), "SELECT COUNT(1) FROM migration WHERE name=$1", file.Name()).Scan(&count) + + if err != nil { + log.Fatal(err) + os.Exit(1) + } + + if count == 0 { + log.Printf("Running migration: %s", file.Name()) + _, err = dbpool.Exec(context.Background(), string(content)) + + if err != nil { + log.Fatal(err) + os.Exit(1) + } + + _, err = dbpool.Exec(context.Background(), "INSERT INTO migration(name) values($1)", file.Name()) + + if err != nil { + log.Fatal(err) + os.Exit(1) + } + } else { + log.Printf("Migration already exists: %s", file.Name()) + } + } + + // set up repositories + todoRepository := repositories.NewPostgresTodoRepository(dbpool) + + // set up services + todoService := services.NewTodoService(&todoRepository) + + // set up handlers + homeHandler := handlers.NewHomeHandler(todoService) + todoHandler := handlers.NewTodoHandler(todoService) router := http.NewServeMux() @@ -25,6 +126,8 @@ func main() { router.HandleFunc("GET /{$}", homeHandler.Home) router.HandleFunc("POST /todos", todoHandler.Create) router.HandleFunc("DELETE /todos/{id}", todoHandler.Delete) + router.HandleFunc("PATCH /todos/{id}/done", todoHandler.Done) + router.HandleFunc("PATCH /todos/{id}/undone", todoHandler.Undone) server := http.Server{ Addr: "localhost:3000", diff --git a/models/todo.go b/models/todo.go index 074855f..815a5ea 100644 --- a/models/todo.go +++ b/models/todo.go @@ -1,6 +1,6 @@ package models -import "github.com/google/uuid" +import "github.com/gofrs/uuid/v5" type Todo struct { Id uuid.UUID @@ -8,9 +8,9 @@ type Todo struct { Done bool } -func NewTodo(name string, done bool) Todo { +func NewTodo(id uuid.UUID, name string, done bool) Todo { return Todo{ - Id: uuid.New(), + Id: id, Name: name, Done: done, } diff --git a/db/db.go b/repositories/inMemoryTodoRepository.go similarity index 60% rename from db/db.go rename to repositories/inMemoryTodoRepository.go index ceabd8a..884a4ef 100644 --- a/db/db.go +++ b/repositories/inMemoryTodoRepository.go @@ -1,21 +1,21 @@ -package db +package repositories import ( "fmt" - "github.com/google/uuid" + "github.com/gofrs/uuid/v5" "michaelthomson.dev/mthomson/go-todos-app/models" ) -type TodosStore struct { +type InMemoryTodoRepository struct { Todos []models.Todo } -func (ts *TodosStore) List() (todo []models.Todo, err error) { +func (ts *InMemoryTodoRepository) List() (todo []models.Todo, err error) { return ts.Todos, nil } -func (ts *TodosStore) Get(id uuid.UUID) (todo models.Todo, err error) { +func (ts *InMemoryTodoRepository) Get(id uuid.UUID) (todo models.Todo, err error) { index := -1 for i, todo := range ts.Todos { if id == todo.Id { @@ -32,12 +32,12 @@ func (ts *TodosStore) Get(id uuid.UUID) (todo models.Todo, err error) { return ts.Todos[index], nil } -func (ts *TodosStore) Add(todo models.Todo) (addedTodo models.Todo, err error) { +func (ts *InMemoryTodoRepository) Add(todo models.Todo) (addedTodo models.Todo, err error) { ts.Todos = append(ts.Todos, todo) return todo, nil } -func (ts *TodosStore) Delete(id uuid.UUID) (err error) { +func (ts *InMemoryTodoRepository) Delete(id uuid.UUID) (err error) { index := -1 for i, todo := range ts.Todos { if id == todo.Id { @@ -54,8 +54,8 @@ func (ts *TodosStore) Delete(id uuid.UUID) (err error) { return nil } -func NewTodoStore() TodosStore { - return TodosStore{ +func NewInMemoryTodoRepository() InMemoryTodoRepository { + return InMemoryTodoRepository{ Todos: []models.Todo{}, } } diff --git a/repositories/postgresTodoRepository.go b/repositories/postgresTodoRepository.go new file mode 100644 index 0000000..fe90473 --- /dev/null +++ b/repositories/postgresTodoRepository.go @@ -0,0 +1,86 @@ +package repositories + +import ( + "context" + "log" + + "github.com/gofrs/uuid/v5" + "github.com/jackc/pgx/v5/pgxpool" + "michaelthomson.dev/mthomson/go-todos-app/models" +) + +type PostgresTodoRepository struct { + db *pgxpool.Pool +} + +func (ts *PostgresTodoRepository) List() ([]models.Todo, error) { + var todos []models.Todo + rows, err := ts.db.Query(context.Background(), "SELECT id, name, done FROM todo") + + if err != nil { + log.Fatal(err) + return todos, err + } + + for rows.Next() { + var r models.Todo + err := rows.Scan(&r.Id, &r.Name, &r.Done) + if err != nil { + log.Fatal(err) + return todos, err + } + todos = append(todos, r) + } + + return todos, err +} + +func (ts *PostgresTodoRepository) Get(id uuid.UUID) (models.Todo, error) { + var todo models.Todo + err := ts.db.QueryRow(context.Background(), "SELECT id, name, done FROM todo WHERE id=$1", id).Scan(&todo.Id, &todo.Name, &todo.Done) + + if err != nil { + log.Fatal(err) + return todo, err + } + + return todo, err +} + +func (ts *PostgresTodoRepository) Update(id uuid.UUID, name string, done bool) error { + _, err := ts.db.Exec(context.Background(), "UPDATE todo SET name=$1, done=$2 WHERE id=$3", name, done, id) + + if err != nil { + log.Fatal(err) + } + + return err +} + +func (ts *PostgresTodoRepository) Add(todo models.Todo) error { + _, err := ts.db.Exec(context.Background(), "INSERT INTO todo(id, name, done) values($1, $2, $3)", todo.Id, todo.Name, todo.Done) + + if err != nil { + log.Fatal(err) + return err + } + + return err +} + +func (ts *PostgresTodoRepository) Delete(id uuid.UUID) error { + _, err := ts.db.Exec(context.Background(), "DELETE FROM todo where id=$1", id) + + if err != nil { + log.Fatal(err) + return err + } + + return err +} + +func NewPostgresTodoRepository(db *pgxpool.Pool) PostgresTodoRepository { + return PostgresTodoRepository{ + db: db, + } +} diff --git a/services/todoService.go b/services/todoService.go index e6edb7a..139d94d 100644 --- a/services/todoService.go +++ b/services/todoService.go @@ -3,15 +3,16 @@ package services import ( "fmt" - "github.com/google/uuid" + "github.com/gofrs/uuid/v5" "michaelthomson.dev/mthomson/go-todos-app/models" ) type Db interface { - List() (todo []models.Todo, err error) - Get(id uuid.UUID) (todo models.Todo, err error) - Add(todo models.Todo) (addedTodo models.Todo, err error) - Delete(id uuid.UUID) (err error) + List() ([]models.Todo, error) + Get(id uuid.UUID) (models.Todo, error) + Add(todo models.Todo) error + Delete(id uuid.UUID) error + Update(id uuid.UUID, name string, done bool) error } type TodoService struct { @@ -24,11 +25,33 @@ func NewTodoService(db Db) *TodoService { } } -func (ts *TodoService) AddTodo(name string) (todo models.Todo, err error) { +func (ts *TodoService) AddTodo(name string) (models.Todo, error) { if name == "" { return models.Todo{}, fmt.Errorf("Must provide a name") } - return ts.db.Add(models.NewTodo(name, false)) + + id, err := uuid.NewV4() + if err != nil { + return models.Todo{}, err + } + + err = ts.db.Add(models.NewTodo(id, name, false)) + + if err != nil { + return models.Todo{}, err + } + + return ts.db.Get(id) +} + +func (ts *TodoService) UpdateTodo(id uuid.UUID, name string, done bool) (models.Todo, error) { + err := ts.db.Update(id, name, done) + + if err != nil { + return models.Todo{}, err + } + + return ts.db.Get(id) } func (ts *TodoService) GetTodos() (todos []models.Todo, err error) { diff --git a/sql/init.sql b/sql/init.sql new file mode 100644 index 0000000..94921c0 --- /dev/null +++ b/sql/init.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS migration ( + id serial primary key not null, + name text not null, + applied_at timestamp not null default now() +); diff --git a/sql/migrations/20240617T1630_createtable-todo.sql b/sql/migrations/20240617T1630_createtable-todo.sql new file mode 100644 index 0000000..ec23285 --- /dev/null +++ b/sql/migrations/20240617T1630_createtable-todo.sql @@ -0,0 +1,5 @@ +CREATE TABLE todo ( + id UUID PRIMARY KEY, + name VARCHAR(255) NOT NULL, + done BOOLEAN NOT NULL DEFAULT false +); diff --git a/templates/pages/home.templ b/templates/pages/home.templ index 800958d..96a4886 100644 --- a/templates/pages/home.templ +++ b/templates/pages/home.templ @@ -6,7 +6,7 @@ import "michaelthomson.dev/mthomson/go-todos-app/models" templ Home(todos []models.Todo) { @shared.Page("Todos") { -
+

Todos

diff --git a/templates/pages/home_templ.go b/templates/pages/home_templ.go index 4a2937d..13950b6 100644 --- a/templates/pages/home_templ.go +++ b/templates/pages/home_templ.go @@ -33,7 +33,7 @@ func Home(todos []models.Todo) templ.Component { templ_7745c5c3_Buffer = templ.GetBuffer() defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Todos

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Todos

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/templates/partials/todo.templ b/templates/partials/todo.templ index 27ee505..a350d20 100644 --- a/templates/partials/todo.templ +++ b/templates/partials/todo.templ @@ -1,14 +1,23 @@ package partials import "michaelthomson.dev/mthomson/go-todos-app/models" +import "fmt" func todoId(todo models.Todo) string { return "todo-" + todo.Id.String() } +func patchUrl(todo models.Todo) string { + if todo.Done { + return string(templ.URL(fmt.Sprintf("/todos/%s/undone", todo.Id))) + } + return string(templ.URL(fmt.Sprintf("/todos/%s/done", todo.Id))) +} + templ Todo(todo models.Todo) {
{ todo.Name } - + +
} diff --git a/templates/partials/todo_templ.go b/templates/partials/todo_templ.go index a7071e0..201801d 100644 --- a/templates/partials/todo_templ.go +++ b/templates/partials/todo_templ.go @@ -11,11 +11,19 @@ import "io" import "bytes" import "michaelthomson.dev/mthomson/go-todos-app/models" +import "fmt" func todoId(todo models.Todo) string { return "todo-" + todo.Id.String() } +func patchUrl(todo models.Todo) string { + if todo.Done { + return string(templ.URL(fmt.Sprintf("/todos/%s/undone", todo.Id))) + } + return string(templ.URL(fmt.Sprintf("/todos/%s/done", todo.Id))) +} + func Todo(todo models.Todo) templ.Component { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) @@ -36,7 +44,7 @@ func Todo(todo models.Todo) templ.Component { var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(todoId(todo)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/partials/todo.templ`, Line: 10, Col: 24} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/partials/todo.templ`, Line: 18, Col: 24} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { @@ -49,7 +57,7 @@ func Todo(todo models.Todo) templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(todo.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/partials/todo.templ`, Line: 11, Col: 15} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/partials/todo.templ`, Line: 19, Col: 15} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -60,9 +68,9 @@ func Todo(todo models.Todo) templ.Component { return templ_7745c5c3_Err } var templ_7745c5c3_Var4 string - templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs("/todos/" + todo.Id.String()) + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(string(templ.URL(fmt.Sprintf("/todos/%s", todo.Id)))) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/partials/todo.templ`, Line: 12, Col: 52} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/partials/todo.templ`, Line: 20, Col: 76} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { @@ -75,13 +83,49 @@ func Todo(todo models.Todo) templ.Component { var templ_7745c5c3_Var5 string templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs("#" + todoId(todo)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/partials/todo.templ`, Line: 12, Col: 85} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/partials/todo.templ`, Line: 20, Col: 109} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"delete\">Delete
") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"delete\">Delete
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }