diff --git a/cmd/main.go b/cmd/main.go index b5660e5..288a725 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -3,9 +3,10 @@ package main import ( "html/template" "log" + "net/http" - "github.com/gin-gonic/gin" _ "github.com/go-sql-driver/mysql" + "github.com/gorilla/mux" "go_selva/internal/api" "go_selva/internal/db" @@ -19,44 +20,48 @@ func main() { } defer database.Close() - // Initialize Gin router - router := gin.Default() + // Initialize Gorilla Mux router + router := mux.NewRouter() - // Register the safeHTML function - router.SetFuncMap(template.FuncMap{ + // Create a template registry and register the safeHTML function + funcMap := template.FuncMap{ "safeHTML": func(s string) template.HTML { return template.HTML(s) }, - }) + } + tmpl := template.New("").Funcs(funcMap) // Load HTML templates - router.LoadHTMLGlob("templates/*") - - // Serve static files (CSS) - router.Static("/static", "./static") - - // Initialize API handlers - apiHandler := api.NewAPIHandler(database) - - // Define API routes - apiGroup := router.Group("/nombres") - { - apiGroup.GET("/search", apiHandler.SearchNombres) - apiGroup.POST("", apiHandler.CreateNombre) - apiGroup.GET("", apiHandler.GetNombres) - apiGroup.GET("/:id", apiHandler.GetNombreByID) - apiGroup.PUT("/:id", apiHandler.UpdateNombre) - apiGroup.DELETE("/:id", apiHandler.DeleteNombre) - apiGroup.GET("/html/edit/:id", apiHandler.EditNombreHTML) - apiGroup.POST("/html/update/:id", apiHandler.UpdateNombreHTML) + tmpl, err = tmpl.ParseGlob("templates/*") + if err != nil { + log.Fatalf("Failed to load HTML templates: %v", err) } + // Serve static files (CSS) + router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./static")))) + + // Initialize API handlers + apiHandler := api.NewAPIHandler(database, tmpl) // Pass the loaded templates + + // Define API routes + apiGroup := router.PathPrefix("/nombres").Subrouter() + apiGroup.HandleFunc("/search", apiHandler.SearchNombres).Methods("GET") + apiGroup.HandleFunc("", apiHandler.CreateNombre).Methods("POST") + apiGroup.HandleFunc("", apiHandler.GetNombres).Methods("GET") + apiGroup.HandleFunc("/{id}", apiHandler.GetNombreByID).Methods("GET") + apiGroup.HandleFunc("/{id}", apiHandler.UpdateNombre).Methods("PUT") + apiGroup.HandleFunc("/{id}", apiHandler.DeleteNombre).Methods("DELETE") + apiGroup.HandleFunc("/html/edit/{id}", apiHandler.EditNombreHTML).Methods("GET") + apiGroup.HandleFunc("/html/update/{id}", apiHandler.UpdateNombreHTML).Methods("POST") + // HTML Routes - apiGroup.GET("/html", apiHandler.GetNombresHTML) - apiGroup.GET("/html/:id", apiHandler.GetNombreByIDHTML) + router.HandleFunc("/", apiHandler.GetIndexPlantsHTML).Methods("GET") + router.HandleFunc("/nombres/html", apiHandler.GetNombresHTML).Methods("GET") + router.HandleFunc("/nombres/html/{id}", apiHandler.GetNombreByIDHTML).Methods("GET") // Start the server - if err := router.Run(":8080"); err != nil { + log.Println("Server started on :8080") + if err := http.ListenAndServe(":8080", router); err != nil { log.Fatalf("Failed to start server: %v", err) } } diff --git a/go.mod b/go.mod index dec4fd2..1b4f39e 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module go_selva go 1.21.1 +require github.com/joho/godotenv v1.5.1 + require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/bytedance/sonic v1.11.6 // indirect @@ -16,7 +18,7 @@ require ( github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/go-sql-driver/mysql v1.9.1 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/joho/godotenv v1.5.1 // indirect + github.com/gorilla/mux v1.8.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect @@ -33,5 +35,4 @@ require ( golang.org/x/text v0.15.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - github.com/joho/godotenv v1.5.1 ) diff --git a/go.sum b/go.sum index 7611f9b..eb60615 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6 github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= diff --git a/internal/api/handlers.go b/internal/api/handlers.go index 742e25e..57946fb 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -2,14 +2,16 @@ package api import ( "database/sql" + "encoding/json" + "html/template" "log" "net/http" "strconv" - "github.com/gin-gonic/gin" + "github.com/gorilla/mux" ) -// Nombre represents the structure of the Nombres table. +// Nombre representa la estructura de la tabla Nombres (assuming this is your plant table). type Nombre struct { NombreID int `json:"NombreID"` FamiliaID int `json:"FamiliaID"` @@ -18,44 +20,91 @@ type Nombre struct { ProveedorID int `json:"ProveedorID"` Precio float64 `json:"Precio"` Inactivo bool `json:"Inactivo"` + // Add other relevant fields if you want to display them } +// APIHandler ... (rest of your struct definition) + +// NewAPIHandler ... (rest of your function) + +// GetIndexPlantsHTML recupera the first 20 plants from the Nombres table. +// internal/api/api.go + +func (h *APIHandler) GetIndexPlantsHTML(w http.ResponseWriter, r *http.Request) { + log.Println("GetIndexPlantsHTML called") + + rows, err := h.DB.Query("SELECT NombreID, FamiliaID, Nombre, Fecha, ProveedorID, Precio, Inactivo FROM Nombres WHERE Inactivo = 0 LIMIT 20") + if err != nil { + log.Println("Database query error:", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer rows.Close() + + var plants []Nombre + for rows.Next() { + var plant Nombre + if err := rows.Scan(&plant.NombreID, &plant.FamiliaID, &plant.Nombre, &plant.Fecha, &plant.ProveedorID, &plant.Precio, &plant.Inactivo); err != nil { + log.Println("Row scan error:", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + plants = append(plants, plant) + } + + log.Println("Retrieved plants:", plants) + + if err := h.template.ExecuteTemplate(w, "index.html", plants); err != nil { + log.Println("Template execution error:", err) + http.Error(w, "Failed to render HTML", http.StatusInternalServerError) + return + } + log.Println("index.html rendered") +} + +// APIHandler maneja las operaciones de la API para la tabla Nombres. type APIHandler struct { - DB *sql.DB + DB *sql.DB + template *template.Template // To store loaded HTML templates } -func NewAPIHandler(db *sql.DB) *APIHandler { - return &APIHandler{DB: db} +// NewAPIHandler crea una nueva instancia de APIHandler. +func NewAPIHandler(db *sql.DB, tmpl *template.Template) *APIHandler { + return &APIHandler{DB: db, template: tmpl} } -func (h *APIHandler) CreateNombre(c *gin.Context) { +// CreateNombre crea un nuevo registro en la tabla Nombres. +func (h *APIHandler) CreateNombre(w http.ResponseWriter, r *http.Request) { var nombre Nombre - if err := c.ShouldBindJSON(&nombre); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + if err := json.NewDecoder(r.Body).Decode(&nombre); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) return } result, err := h.DB.Exec("INSERT INTO Nombres (FamiliaID, Nombre, Fecha, ProveedorID, Precio, Inactivo) VALUES (?, ?, ?, ?, ?, ?)", nombre.FamiliaID, nombre.Nombre, nombre.Fecha, nombre.ProveedorID, nombre.Precio, nombre.Inactivo) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + http.Error(w, err.Error(), http.StatusInternalServerError) return } id, err := result.LastInsertId() if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + http.Error(w, err.Error(), http.StatusInternalServerError) return } nombre.NombreID = int(id) - c.JSON(http.StatusCreated, nombre) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + json.NewEncoder(w).Encode(nombre) } -func (h *APIHandler) GetNombres(c *gin.Context) { +// GetNombres recupera todos los registros de la tabla Nombres. +func (h *APIHandler) GetNombres(w http.ResponseWriter, r *http.Request) { rows, err := h.DB.Query("SELECT NombreID, FamiliaID, Nombre, Fecha, ProveedorID, Precio, Inactivo FROM Nombres") if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + http.Error(w, err.Error(), http.StatusInternalServerError) return } defer rows.Close() @@ -64,19 +113,27 @@ func (h *APIHandler) GetNombres(c *gin.Context) { for rows.Next() { var nombre Nombre if err := rows.Scan(&nombre.NombreID, &nombre.FamiliaID, &nombre.Nombre, &nombre.Fecha, &nombre.ProveedorID, &nombre.Precio, &nombre.Inactivo); err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + http.Error(w, err.Error(), http.StatusInternalServerError) return } nombres = append(nombres, nombre) } - c.JSON(http.StatusOK, nombres) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(nombres) } -func (h *APIHandler) GetNombreByID(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) +// GetNombreByID recupera un registro de la tabla Nombres por su ID. +func (h *APIHandler) GetNombreByID(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + idStr, ok := vars["id"] + if !ok { + http.Error(w, "Invalid ID", http.StatusBadRequest) + return + } + id, err := strconv.Atoi(idStr) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"}) + http.Error(w, "Invalid ID", http.StatusBadRequest) return } @@ -85,50 +142,60 @@ func (h *APIHandler) GetNombreByID(c *gin.Context) { &nombre.NombreID, &nombre.FamiliaID, &nombre.Nombre, &nombre.Fecha, &nombre.ProveedorID, &nombre.Precio, &nombre.Inactivo) if err != nil { if err == sql.ErrNoRows { - c.JSON(http.StatusNotFound, gin.H{"error": "Nombre not found"}) + http.Error(w, "Nombre not found", http.StatusNotFound) } else { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + http.Error(w, err.Error(), http.StatusInternalServerError) } return } - c.JSON(http.StatusOK, nombre) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(nombre) } -func (h *APIHandler) UpdateNombre(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) +// UpdateNombre actualiza un registro existente en la tabla Nombres. +func (h *APIHandler) UpdateNombre(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + idStr, ok := vars["id"] + if !ok { + http.Error(w, "Invalid ID", http.StatusBadRequest) + return + } + id, err := strconv.Atoi(idStr) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"}) + http.Error(w, "Invalid ID", http.StatusBadRequest) return } var nombre Nombre - if err := c.ShouldBindJSON(&nombre); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + if err := json.NewDecoder(r.Body).Decode(&nombre); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) return } _, err = h.DB.Exec("UPDATE Nombres SET FamiliaID = ?, Nombre = ?, Fecha = ?, ProveedorID = ?, Precio = ?, Inactivo = ? WHERE NombreID = ?", nombre.FamiliaID, nombre.Nombre, nombre.Fecha, nombre.ProveedorID, nombre.Precio, nombre.Inactivo, id) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + http.Error(w, err.Error(), http.StatusInternalServerError) return } nombre.NombreID = id - c.JSON(http.StatusOK, nombre) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(nombre) } -func (h *APIHandler) SearchNombres(c *gin.Context) { - nombre := c.Query("nombre") +// SearchNombres busca registros en la tabla Nombres por nombre. +func (h *APIHandler) SearchNombres(w http.ResponseWriter, r *http.Request) { + nombre := r.URL.Query().Get("nombre") if nombre == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": "Nombre parameter is required"}) + http.Error(w, "Nombre parameter is required", http.StatusBadRequest) return } rows, err := h.DB.Query("SELECT NombreID, FamiliaID, Nombre, Fecha, ProveedorID, Precio, Inactivo FROM Nombres WHERE Nombre = ?", nombre) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + http.Error(w, err.Error(), http.StatusInternalServerError) return } defer rows.Close() @@ -137,37 +204,47 @@ func (h *APIHandler) SearchNombres(c *gin.Context) { for rows.Next() { var nombre Nombre if err := rows.Scan(&nombre.NombreID, &nombre.FamiliaID, &nombre.Nombre, &nombre.Fecha, &nombre.ProveedorID, &nombre.Precio, &nombre.Inactivo); err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + http.Error(w, err.Error(), http.StatusInternalServerError) return } nombres = append(nombres, nombre) } - c.JSON(http.StatusOK, nombres) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(nombres) } -func (h *APIHandler) DeleteNombre(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) +// DeleteNombre elimina un registro de la tabla Nombres por su ID. +func (h *APIHandler) DeleteNombre(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + idStr, ok := vars["id"] + if !ok { + http.Error(w, "Invalid ID", http.StatusBadRequest) + return + } + id, err := strconv.Atoi(idStr) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"}) + http.Error(w, "Invalid ID", http.StatusBadRequest) return } _, err = h.DB.Exec("DELETE FROM Nombres WHERE NombreID = ?", id) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + http.Error(w, err.Error(), http.StatusInternalServerError) return } - c.JSON(http.StatusOK, gin.H{"message": "Nombre deleted"}) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]string{"message": "Nombre deleted"}) } -func (h *APIHandler) GetNombresHTML(c *gin.Context) { - log.Println("GetNombresHTML called") // Add logging +// GetNombresHTML recupera todos los registros de la tabla Nombres y los muestra en una página HTML. +func (h *APIHandler) GetNombresHTML(w http.ResponseWriter, r *http.Request) { + log.Println("GetNombresHTML called") rows, err := h.DB.Query("SELECT NombreID, FamiliaID, Nombre, Fecha, ProveedorID, Precio, Inactivo FROM Nombres") if err != nil { - log.Println("Database query error:", err) // Add logging - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + log.Println("Database query error:", err) + http.Error(w, err.Error(), http.StatusInternalServerError) return } defer rows.Close() @@ -176,94 +253,185 @@ func (h *APIHandler) GetNombresHTML(c *gin.Context) { for rows.Next() { var nombre Nombre if err := rows.Scan(&nombre.NombreID, &nombre.FamiliaID, &nombre.Nombre, &nombre.Fecha, &nombre.ProveedorID, &nombre.Precio, &nombre.Inactivo); err != nil { - log.Println("Row scan error:", err) // Add logging - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + log.Println("Row scan error:", err) + http.Error(w, err.Error(), http.StatusInternalServerError) return } nombres = append(nombres, nombre) } - log.Println("Retrieved nombres:", nombres) // Add logging - c.HTML(http.StatusOK, "nombres.html", nombres) - log.Println("nombres.html rendered") //add log + log.Println("Retrieved nombres:", nombres) + if err := h.template.ExecuteTemplate(w, "nombres.html", nombres); err != nil { + log.Println("Template execution error:", err) + http.Error(w, "Failed to render HTML", http.StatusInternalServerError) + return + } + log.Println("nombres.html rendered") } -func (h *APIHandler) GetNombreByIDHTML(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) +// GetNombreByIDHTML recupera un registro de la tabla Nombres por su ID, el nombre de su familia, +// y el nombre del proveedor, luego los muestra en una página HTML. +func (h *APIHandler) GetNombreByIDHTML(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + idStr, ok := vars["id"] + if !ok { + http.Error(w, "Invalid ID", http.StatusBadRequest) + return + } + id, err := strconv.Atoi(idStr) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"}) + http.Error(w, "Invalid ID", http.StatusBadRequest) + return + } + + var nombreDetalle struct { + Nombre Nombre + FamiliaName string + ProveedorName string + } + + // Join nombres, familias, and proveedores tables + err = h.DB.QueryRow(` + SELECT + n.NombreID, + n.FamiliaID, + n.Nombre, + n.Fecha, + n.ProveedorID, + n.Precio, + n.Inactivo, + f.Familia, + p.Nombre + FROM nombres n + INNER JOIN familias f ON n.FamiliaID = f.FamiliaID + INNER JOIN proveedores p ON n.ProveedorID = p.ProveedorID + WHERE n.NombreID = ? + `, id).Scan( + &nombreDetalle.Nombre.NombreID, + &nombreDetalle.Nombre.FamiliaID, + &nombreDetalle.Nombre.Nombre, + &nombreDetalle.Nombre.Fecha, + &nombreDetalle.Nombre.ProveedorID, + &nombreDetalle.Nombre.Precio, + &nombreDetalle.Nombre.Inactivo, + &nombreDetalle.FamiliaName, + &nombreDetalle.ProveedorName, + ) + if err != nil { + if err == sql.ErrNoRows { + http.Error(w, "Nombre not found", http.StatusNotFound) + } else { + log.Println("Database query error:", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + } + return + } + + if err := h.template.ExecuteTemplate(w, "nombre.html", nombreDetalle); err != nil { + log.Println("Template execution error:", err) + http.Error(w, "Failed to render HTML", http.StatusInternalServerError) + return + } +} + +func (h *APIHandler) EditNombreHTML(w http.ResponseWriter, r *http.Request) { + log.Println("EditNombreHTML called") + vars := mux.Vars(r) + idStr, ok := vars["id"] + log.Printf("Extracted ID string: %s, ok: %v", idStr, ok) + if !ok { + http.Error(w, "Invalid ID", http.StatusBadRequest) + return + } + id, err := strconv.Atoi(idStr) + log.Printf("Converted ID: %d, error: %v", id, err) + if err != nil { + http.Error(w, "Invalid ID", http.StatusBadRequest) return } var nombre Nombre err = h.DB.QueryRow("SELECT NombreID, FamiliaID, Nombre, Fecha, ProveedorID, Precio, Inactivo FROM Nombres WHERE NombreID = ?", id).Scan( &nombre.NombreID, &nombre.FamiliaID, &nombre.Nombre, &nombre.Fecha, &nombre.ProveedorID, &nombre.Precio, &nombre.Inactivo) + log.Printf("Database query result: %+v, error: %v", nombre, err) if err != nil { if err == sql.ErrNoRows { - c.JSON(http.StatusNotFound, gin.H{"error": "Nombre not found"}) + http.Error(w, "Nombre not found", http.StatusNotFound) } else { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + log.Println("Database query error:", err) + http.Error(w, err.Error(), http.StatusInternalServerError) } return } - c.HTML(http.StatusOK, "nombre.html", nombre) -} + log.Printf("Data passed to template: %+v", nombre) -func (h *APIHandler) EditNombreHTML(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"}) + w.Header().Set("Content-Type", "text/html; charset=utf-8") // Set header BEFORE executing template + + if err := h.template.ExecuteTemplate(w, "edit_nombre.html", nombre); err != nil { + log.Println("Template execution error:", err) + http.Error(w, "Failed to render HTML", http.StatusInternalServerError) return } - - var nombre Nombre - err = h.DB.QueryRow("SELECT NombreID, FamiliaID, Nombre, Fecha, ProveedorID, Precio, Inactivo FROM Nombres WHERE NombreID = ?", id).Scan( - &nombre.NombreID, &nombre.FamiliaID, &nombre.Nombre, &nombre.Fecha, &nombre.ProveedorID, &nombre.Precio, &nombre.Inactivo) - if err != nil { - if err == sql.ErrNoRows { - c.JSON(http.StatusNotFound, gin.H{"error": "Nombre not found"}) - } else { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - } - return - } - - c.HTML(http.StatusOK, "edit_nombre.html", nombre) + log.Println("edit_nombre.html rendered") } -func (h *APIHandler) UpdateNombreHTML(c *gin.Context) { - id, err := strconv.Atoi(c.Param("id")) // Declare id and err +// UpdateNombreHTML actualiza un registro en la tabla Nombres desde una página HTML. +func (h *APIHandler) UpdateNombreHTML(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + idStr, ok := vars["id"] + if !ok { + http.Error(w, "Invalid ID", http.StatusBadRequest) + return + } + id, err := strconv.Atoi(idStr) if err != nil { log.Println("Invalid ID:", err) - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"}) + http.Error(w, "Invalid ID", http.StatusBadRequest) return } - var nombre Nombre - if err := c.ShouldBind(&nombre); err != nil { // Declare err - log.Println("Binding error:", err) - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + if err := r.ParseForm(); err != nil { + log.Println("Form parsing error:", err) + http.Error(w, "Failed to parse form", http.StatusBadRequest) return } + nombre := Nombre{ + NombreID: id, + FamiliaID: atoi(r.FormValue("FamiliaID")), + Nombre: r.FormValue("Nombre"), + Fecha: r.FormValue("Fecha"), + ProveedorID: atoi(r.FormValue("ProveedorID")), + Precio: atof64(r.FormValue("Precio")), + Inactivo: r.FormValue("Inactivo") == "on", + } + log.Println("Updating nombre:", nombre) - // Handle empty date fecha := nombre.Fecha if fecha == "" { - fecha = "0000-00-00" // Or "NULL", if your database allows it + fecha = "0000-00-00" } _, err = h.DB.Exec("UPDATE Nombres SET FamiliaID = ?, Nombre = ?, Fecha = ?, ProveedorID = ?, Precio = ?, Inactivo = ? WHERE NombreID = ?", - nombre.FamiliaID, nombre.Nombre, fecha, nombre.ProveedorID, nombre.Precio, nombre.Inactivo, id) // Use declared id and err + nombre.FamiliaID, nombre.Nombre, fecha, nombre.ProveedorID, nombre.Precio, nombre.Inactivo, id) if err != nil { log.Println("Database update error:", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + http.Error(w, err.Error(), http.StatusInternalServerError) return } log.Println("Nombre updated successfully") - - c.Redirect(http.StatusFound, "/nombres/html/"+strconv.Itoa(id)) + http.Redirect(w, r, "/nombres/html/"+strconv.Itoa(id), http.StatusFound) +} + +func atoi(s string) int { + i, _ := strconv.Atoi(s) // Ignore error for simplicity in this context + return i +} + +func atof64(s string) float64 { + f, _ := strconv.ParseFloat(s, 64) // Ignore error for simplicity + return f } diff --git a/misc/go_selva.sql b/misc/go_selva.sql new file mode 100644 index 0000000..2f95c4a --- /dev/null +++ b/misc/go_selva.sql @@ -0,0 +1,173 @@ +-- -------------------------------------------------------- +-- Host: 127.0.0.1 +-- Server version: 11.7.2-MariaDB - mariadb.org binary distribution +-- Server OS: Win64 +-- HeidiSQL Version: 12.10.0.7000 +-- -------------------------------------------------------- + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET NAMES utf8 */; +/*!50503 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + + +-- Dumping database structure for go_selva +CREATE DATABASE IF NOT EXISTS `go_selva` /*!40100 DEFAULT CHARACTER SET utf32 COLLATE utf32_unicode_ci */; +USE `go_selva`; + +-- Dumping structure for table go_selva.familias +CREATE TABLE IF NOT EXISTS `familias` ( + `FamiliaID` int(11) NOT NULL AUTO_INCREMENT, + `Familia` varchar(35) NOT NULL, + PRIMARY KEY (`FamiliaID`), + UNIQUE KEY `Familia` (`Familia`) +) ENGINE=InnoDB AUTO_INCREMENT=112 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Data exporting was unselected. + +-- Dumping structure for table go_selva.fotos +CREATE TABLE IF NOT EXISTS `fotos` ( + `FotoID` int(11) NOT NULL AUTO_INCREMENT, + `NombreID` int(11) NOT NULL DEFAULT 0, + `Direccion` varchar(75) NOT NULL, + `HayVideo` char(1) DEFAULT '0', + `Video` varchar(75) DEFAULT '', + PRIMARY KEY (`FotoID`), + KEY `NombreID` (`NombreID`), + CONSTRAINT `Fotos_ibfk_1` FOREIGN KEY (`NombreID`) REFERENCES `nombres` (`NombreID`) +) ENGINE=InnoDB AUTO_INCREMENT=1093 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Data exporting was unselected. + +-- Dumping structure for table go_selva.logs +CREATE TABLE IF NOT EXISTS `logs` ( + `LogID` int(9) NOT NULL AUTO_INCREMENT, + `Tiempo` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `UsuarioID` int(4) NOT NULL DEFAULT 0, + `Query` text NOT NULL, + PRIMARY KEY (`LogID`) +) ENGINE=InnoDB AUTO_INCREMENT=14427 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Data exporting was unselected. + +-- Dumping structure for table go_selva.logs20191029 +CREATE TABLE IF NOT EXISTS `logs20191029` ( + `LogID` int(9) NOT NULL DEFAULT 0, + `Tiempo` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `UsuarioID` int(4) NOT NULL DEFAULT 0, + `Query` text NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Data exporting was unselected. + +-- Dumping structure for table go_selva.nombres +CREATE TABLE IF NOT EXISTS `nombres` ( + `NombreID` int(11) NOT NULL AUTO_INCREMENT, + `FamiliaID` int(6) NOT NULL DEFAULT 1, + `Nombre` varchar(75) NOT NULL, + `Fecha` date NOT NULL DEFAULT '0000-00-00', + `ProveedorID` int(4) NOT NULL DEFAULT 1, + `Precio` float(10,2) NOT NULL DEFAULT 0.00, + `Inactivo` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`NombreID`), + UNIQUE KEY `Nombre` (`Nombre`), + KEY `FamiliaID` (`FamiliaID`), + KEY `ProveedorID` (`ProveedorID`), + CONSTRAINT `Nombres_ibfk_1` FOREIGN KEY (`FamiliaID`) REFERENCES `familias` (`FamiliaID`), + CONSTRAINT `Nombres_ibfk_2` FOREIGN KEY (`ProveedorID`) REFERENCES `proveedores` (`ProveedorID`) +) ENGINE=InnoDB AUTO_INCREMENT=1140 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Data exporting was unselected. + +-- Dumping structure for table go_selva.nombresvulgares +CREATE TABLE IF NOT EXISTS `nombresvulgares` ( + `NombreVulgarID` int(11) NOT NULL AUTO_INCREMENT, + `NombreID` int(11) NOT NULL DEFAULT 0, + `NombreVulgar` varchar(50) DEFAULT NULL, + PRIMARY KEY (`NombreVulgarID`), + KEY `NombreID` (`NombreID`), + CONSTRAINT `NombresVulgares_ibfk_1` FOREIGN KEY (`NombreID`) REFERENCES `nombres` (`NombreID`) +) ENGINE=InnoDB AUTO_INCREMENT=2453 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Data exporting was unselected. + +-- Dumping structure for table go_selva.notas +CREATE TABLE IF NOT EXISTS `notas` ( + `NotaID` int(11) NOT NULL AUTO_INCREMENT, + `NombreID` int(11) NOT NULL, + `Nota` tinytext DEFAULT NULL, + PRIMARY KEY (`NotaID`), + KEY `NombreID` (`NombreID`), + CONSTRAINT `Notas_ibfk_1` FOREIGN KEY (`NombreID`) REFERENCES `nombres` (`NombreID`) +) ENGINE=InnoDB AUTO_INCREMENT=6426 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Data exporting was unselected. + +-- Dumping structure for table go_selva.proveedores +CREATE TABLE IF NOT EXISTS `proveedores` ( + `ProveedorID` int(3) NOT NULL AUTO_INCREMENT, + `Nombre` varchar(95) NOT NULL DEFAULT '', + `RFC` varchar(15) DEFAULT '', + `Direccion` varchar(75) DEFAULT '', + `Colonia` varchar(50) DEFAULT '', + `Ciudad` varchar(25) DEFAULT '', + `Estado` varchar(40) DEFAULT '', + `Pais` varchar(25) DEFAULT '', + `CP` varchar(20) DEFAULT '', + `Tel1` varchar(20) DEFAULT '', + `Tel2` varchar(20) DEFAULT '', + `URL` varchar(75) DEFAULT NULL, + `Desactivado` char(1) NOT NULL DEFAULT 'N', + `Observaciones` varchar(150) DEFAULT NULL, + `Contacto1Nombre` varchar(75) DEFAULT '', + `Contacto1Tel` varchar(20) DEFAULT '', + `Contacto1Cel` varchar(20) DEFAULT '', + `Contacto1EMail` varchar(75) DEFAULT '', + `Contacto2Nombre` varchar(75) DEFAULT '', + `Contacto2Tel` varchar(20) DEFAULT '', + `Contacto2Cel` varchar(20) DEFAULT '', + `Contacto2EMail` varchar(75) DEFAULT '', + PRIMARY KEY (`ProveedorID`), + UNIQUE KEY `Nombre` (`Nombre`) +) ENGINE=InnoDB AUTO_INCREMENT=58 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Data exporting was unselected. + +-- Dumping structure for table go_selva.ubicaciones +CREATE TABLE IF NOT EXISTS `ubicaciones` ( + `UbiID` int(11) NOT NULL AUTO_INCREMENT, + `NombreID` int(11) NOT NULL, + `Ubicacion` varchar(20) NOT NULL, + PRIMARY KEY (`UbiID`), + KEY `NombreID` (`NombreID`), + CONSTRAINT `Ubicaciones_ibfk_1` FOREIGN KEY (`NombreID`) REFERENCES `nombres` (`NombreID`) +) ENGINE=InnoDB AUTO_INCREMENT=1665 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Data exporting was unselected. + +-- Dumping structure for table go_selva.usuarios +CREATE TABLE IF NOT EXISTS `usuarios` ( + `UID` int(5) NOT NULL AUTO_INCREMENT, + `ApellidoPaterno` varchar(25) NOT NULL DEFAULT '', + `ApellidoMaterno` varchar(25) DEFAULT '', + `Nombres` varchar(30) NOT NULL DEFAULT '', + `Login` varchar(25) NOT NULL DEFAULT '', + `PWD` varchar(70) NOT NULL DEFAULT '', + `Fecha` date NOT NULL DEFAULT '0000-00-00', + `Nivel` enum('Admin','Capturista','Almacenista','Consulta','Instalador','Vendedor','PedOrdInv') DEFAULT NULL, + `Deshabilitado` char(1) NOT NULL DEFAULT 'N', + PRIMARY KEY (`UID`), + UNIQUE KEY `Login` (`Login`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Data exporting was unselected. + +/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */; +/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; +/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */; diff --git a/static/style.css b/static/style.css index 15cd132..222cd1f 100644 --- a/static/style.css +++ b/static/style.css @@ -1,22 +1,105 @@ body { - font-family: sans-serif; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + margin: 20px; + background-color: #f4f4f4; + color: #333; + line-height: 1.6; +} + +h1, h2 { + color: #007bff; + margin-bottom: 20px; } table { border-collapse: collapse; width: 100%; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + background-color: white; + border-radius: 8px; + overflow: hidden; /* Para que los bordes redondeados funcionen bien */ } th, td { - border: 1px solid #ddd; - padding: 8px; + border: 1px solid #e0e0e0; + padding: 12px 15px; text-align: left; } th { - background-color: #f2f2f2; + background-color: #f0f8ff; /* Un azul muy claro para los encabezados */ + font-weight: 600; } tr:nth-child(even) { background-color: #f9f9f9; +} + +tr:hover { + background-color: #f0f0f0; /* Efecto hover para las filas */ +} + +a.button, button.button { + display: inline-block; + padding: 10px 15px; + background-color: #007bff; + color: white; + text-decoration: none; + border: none; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s ease; + margin: 5px; +} + +a.button:hover, button.button:hover { + background-color: #0056b3; +} + +.button.delete { + background-color: #dc3545; +} + +.button.delete:hover { + background-color: #c82333; +} + +form { + background-color: white; + padding: 20px; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + margin-bottom: 20px; +} + +label { + display: block; + margin-bottom: 5px; + font-weight: 600; +} + +input[type="text"], +input[type="number"], +input[type="date"], +select { + width: calc(100% - 22px); /* Ajuste para el padding y el borde */ + padding: 10px; + margin-bottom: 15px; + border: 1px solid #ddd; + border-radius: 5px; + box-sizing: border-box; +} + +input[type="checkbox"]{ + margin-bottom: 15px; +} + +@media (max-width: 600px) { + table, form { + width: 100%; + } + th, td { + padding: 8px; + font-size: 0.9em; + } } \ No newline at end of file diff --git a/templates/edit_nombre.html b/templates/edit_nombre.html index c52fbf1..25791b2 100644 --- a/templates/edit_nombre.html +++ b/templates/edit_nombre.html @@ -1,21 +1,41 @@ -
- -

+ + - -

+ + + + Editar Nombre + + - -

+ +

Editar Nombre

- -

+ + +

- -

+ +

- -

+ +

- -
\ No newline at end of file + +

+ + +

+ + +

+ + + + +
+ +
+ + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..a95353a --- /dev/null +++ b/templates/index.html @@ -0,0 +1,22 @@ + + + + + + + Plant Index + + + + +

Our Plants

+ + + + \ No newline at end of file diff --git a/templates/nombre.html b/templates/nombre.html index ba50517..283b027 100644 --- a/templates/nombre.html +++ b/templates/nombre.html @@ -1,53 +1,49 @@ - + - Nombre Details + + + Detalle del Nombre -

Nombre Details

+

Detalle del Nombre

+ + {{ if . }} - - - - - - - - + + - - + + - - + + - - + + - - + + - - - - - - + +
FieldValue
NombreID{{ .NombreID }}Nombre:{{ .Nombre.Nombre }}
FamiliaID{{ .FamiliaID }}Familia:{{ .FamiliaName }}
Nombre{{ .Nombre | safeHTML }}Fecha:{{ .Nombre.Fecha }}
Fecha{{ .Fecha }}Proveedor:{{ .ProveedorName }}
ProveedorID{{ .ProveedorID }}Precio:{{ .Nombre.Precio }}
Precio{{ .Precio }}
Inactivo{{ .Inactivo }}Inactivo:{{ .Nombre.Inactivo }}
- Back to List - Edit +

Editar | Volver

+ {{ else }} +

Nombre no encontrado.

+ {{ end }} \ No newline at end of file