diff --git a/crud/.crud b/crud/.crud new file mode 100755 index 0000000..0958104 Binary files /dev/null and b/crud/.crud differ diff --git a/crud/.server/api/board.go b/crud/.server/api/board.go index 04a486c..321f29a 100644 --- a/crud/.server/api/board.go +++ b/crud/.server/api/board.go @@ -3,37 +3,33 @@ package api import ( "context" "crud/sqlite" + "fmt" "html/template" "net/http" + "strconv" ) +const baseQuery = `SELECT u.id, u.name, u.username, u.email, u.phone, u.website, a.street, a.suite, a.zipcode, a.city, c.name as company, c.catch_phrase, c.bs + FROM user u + JOIN company c ON u.company_id = c.id + JOIN address a ON u.address_id = a.id + ` + func Board(ctx context.Context, db *sqlite.Database, templ *template.Template) http.Handler { - // Implementation of Board handler return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { - query := `SELECT u.id, u.name, u.username, u.email, u.phone, u.website, a.street, a.suite, a.zipcode, a.city, c.name as company, c.catch_phrase, c.bs - FROM user u - JOIN company c ON u.company_id = c.id - JOIN address a ON u.address_id = a.id; - ` w.Header().Set("Content-Type", "text/html") - // w.Header().Set("Content-Type", "application/json") - records, err := db.ReadRecords(ctx, query) + records, err := sqlite.NoRowsOk(db.ReadRecords(ctx, baseQuery+";")) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - // Create a data structure to pass to template - data := map[string]interface{}{ - "records": records, - } - // Execute template with proper error handling - if err := templ.ExecuteTemplate(w, "board", data); err != nil { + if err := templ.ExecuteTemplate(w, "board", sqlite.Record{"records": records}); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -47,13 +43,15 @@ func DeleteRecord(ctx context.Context, db *sqlite.Database, templ *template.Temp func(w http.ResponseWriter, r *http.Request) { - id := r.URL.Query().Get("id") - if id == "" { - http.Error(w, "Missing id parameter", http.StatusBadRequest) - return + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) } - if err := db.DeleteRecord(ctx, "users", "id", id); err != nil { + // fmt.Println("DeleteRecord handler called ", id) + + if err := db.DeleteRecord(ctx, "user", "id", id); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -63,3 +61,65 @@ func DeleteRecord(ctx context.Context, db *sqlite.Database, templ *template.Temp }, ) } + +func EditRecord(ctx context.Context, db *sqlite.Database, templ *template.Template) http.Handler { + // Implementation of EditRecord handler + return http.HandlerFunc( + + func(w http.ResponseWriter, r *http.Request) { + + // fmt.Println("EditRecord handler called") + + id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + } + + records, err := db.ReadRecords(ctx, baseQuery+" WHERE u.id = ?;", id) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + rec := records[0] + + // fmt.Printf("Record to edit: %+v\n", rec) + + w.Header().Set("Content-Type", "text/html") + + // Execute template with proper error handling + if err := templ.ExecuteTemplate(w, "edit-user", rec); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + }, + ) +} + +func PatchRecord(ctx context.Context, db *sqlite.Database, templ *template.Template) http.Handler { + // Implementation of PatchRecord handler + return http.HandlerFunc( + + func(w http.ResponseWriter, r *http.Request) { + + fmt.Println("PatchRecord handler called") + + // id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) + // if err != nil { + // w.WriteHeader(http.StatusInternalServerError) + // w.Write([]byte(err.Error())) + // } + + // if err := db.DeleteRecord(ctx, "user", "id", id); err != nil { + // http.Error(w, err.Error(), http.StatusInternalServerError) + // return + // } + + // Redirect or respond with success + http.Redirect(w, r, "/board", http.StatusSeeOther) + + }, + ) +} diff --git a/crud/.server/crud b/crud/.server/crud index 08e2c35..0958104 100755 Binary files a/crud/.server/crud and b/crud/.server/crud differ diff --git a/crud/.server/frontend/index.html b/crud/.server/frontend/index.html index eeba1ef..673cf85 100644 --- a/crud/.server/frontend/index.html +++ b/crud/.server/frontend/index.html @@ -10,8 +10,7 @@ diff --git a/crud/.server/main_dev.go b/crud/.server/main_dev.go index 2434bdd..7aa0e5e 100644 --- a/crud/.server/main_dev.go +++ b/crud/.server/main_dev.go @@ -44,7 +44,7 @@ func main() { ctx := context.Background() // logging - logFileName := "./crud.log" + logFileName := "./.crud.log" logFile, err := os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { log.Printf("error opening file: %v", err) diff --git a/crud/.server/routes.go b/crud/.server/routes.go index a3d345c..012b0c2 100644 --- a/crud/.server/routes.go +++ b/crud/.server/routes.go @@ -22,9 +22,7 @@ func addRoutes( ) { mux.Handle("GET /", http.FileServer(http.FS(static))) mux.Handle("GET /board", api.Board(ctx, database, templ)) - mux.Handle("GET /delete-record/{id}", api.DeleteRecord(ctx, database, templ)) - // mux.Handle("GET /count", api.ProductCount(database)) - // mux.Handle("GET /nutriments/{page}", api.DataNutriments(database, templ)) - // mux.Handle("GET /products/{page}", api.DataProducts(database, templ)) - // mux.Handle("GET /brandowner/{page}", api.DataBrandOwner(database, templ)) + mux.Handle("GET /edit-record/{id}", api.EditRecord(ctx, database, templ)) + mux.Handle("PATCH /patch-record/{id}", api.PatchRecord(ctx, database, templ)) + mux.Handle("DELETE /delete-record/{id}", api.DeleteRecord(ctx, database, templ)) } diff --git a/crud/.server/templates/board-content.html b/crud/.server/templates/board-content.html index 254ea10..de44aec 100644 --- a/crud/.server/templates/board-content.html +++ b/crud/.server/templates/board-content.html @@ -1,10 +1,10 @@ {{define "board"}}
{{range .records}} -
+

{{.name}}

-

@{{.username}}

+

{{.username}}

{{end}}
+{{end}} + +{{define "edit-user"}} +
+
+

{{.name}}

+

{{.username}}

+
+
+ + + + + + + + +
+ + + +
+ +
+ + + +
+ +
+
{{end}} \ No newline at end of file diff --git a/crud/.show-it b/crud/.show-it index b9c658a..e06118b 100644 --- a/crud/.show-it +++ b/crud/.show-it @@ -3,3 +3,6 @@ cp .user.db user.db ls -l echo "The CRUD Example" ---PAUSE--- +micro index.html board-content.html sample.go +xdg-open http://localhost:8080 +./.crud diff --git a/crud/board-content.html b/crud/board-content.html new file mode 100644 index 0000000..7fbaf01 --- /dev/null +++ b/crud/board-content.html @@ -0,0 +1,119 @@ +{{define "board"}} +
+ {{range .records}} +
+
+

{{.name}}

+

{{.username}}

+
+
+ + + + + + + + +
+ + + +
+ +
+ + + +
+ +
+
+ {{end}} +
+{{end}} + +{{define "edit-user"}} +
+
+

{{.name}}

+

{{.username}}

+
+
+ + + + + + + + +
+ + + +
+ +
+ + + +
+ +
+
+{{end}} diff --git a/crud/index.html b/crud/index.html index 43e961b..673cf85 100644 --- a/crud/index.html +++ b/crud/index.html @@ -1,25 +1,28 @@ - + - Hypermedia + CRUD Example + - + -

Hypermedia as the Engine of Application State

+

The CRUD Example

-
-
-
-
+
+
- + \ No newline at end of file diff --git a/crud/sample.go b/crud/sample.go new file mode 100644 index 0000000..49afd35 --- /dev/null +++ b/crud/sample.go @@ -0,0 +1,44 @@ +// addRoutes combines the URL endpoints with the applications's services +// and dependencies and required middleware +func addRoutes( + ctx context.Context, + mux *http.ServeMux, + database *sqlite.Database, + static fs.FS, + templ *template.Template, +) { + mux.Handle("GET /", http.FileServer(http.FS(static))) + mux.Handle("GET /board", api.Board(ctx, database, templ)) + mux.Handle("GET /edit-record/{id}", api.EditRecord(ctx, database, templ)) + mux.Handle("PATCH /patch-record/{id}", api.PatchRecord(ctx, database, templ)) + mux.Handle("DELETE /delete-record/{id}", api.DeleteRecord(ctx, database, templ)) +} + +const query = `SELECT u.id, u.name, u.username, u.email, u.phone, u.website, a.street, a.suite, a.zipcode, a.city, c.name as company, c.catch_phrase, c.bs + FROM user u + JOIN company c ON u.company_id = c.id + JOIN address a ON u.address_id = a.id; + ` + +// load user data and fill template +func Board(ctx context.Context, db *sqlite.Database, templ *template.Template) http.Handler { + return http.HandlerFunc( + + func(w http.ResponseWriter, r *http.Request) { + + w.Header().Set("Content-Type", "text/html") + + records, err := sqlite.NoRowsOk(db.ReadRecords(ctx, query)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Execute template with proper error handling + if err := templ.ExecuteTemplate(w, "board", sqlite.Record{"records": records}); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + }, + ) +}