diff --git a/api/apiRoutes.go b/api/apiRoutes.go index fbc78f7..1c9426f 100644 --- a/api/apiRoutes.go +++ b/api/apiRoutes.go @@ -25,6 +25,7 @@ func ApiRoutes() *http.ServeMux { //Project mux.HandleFunc("POST /project", handlers.CreateProjectHandler) mux.HandleFunc("PATCH /project/{id}", handlers.UpdateProjectHandler) + mux.HandleFunc("PATCH /projects", handlers.UpdateProjectsHandler) mux.HandleFunc("GET /project/{id}", handlers.GetProjectHandler) mux.HandleFunc("GET /projects", handlers.GetProjectsHandler) diff --git a/api/docs/openAPI/go.mod b/api/docs/openAPI/go.mod index 5c33d18..5a71a9b 100644 --- a/api/docs/openAPI/go.mod +++ b/api/docs/openAPI/go.mod @@ -2,7 +2,10 @@ module openAPI go 1.22 -require github.com/a-h/rest v0.0.0-20240504113546-6729b3328f85 +require ( + github.com/a-h/respond v0.0.2 + github.com/a-h/rest v0.0.0-20240504113546-6729b3328f85 +) require ( github.com/getkin/kin-openapi v0.124.0 // indirect diff --git a/api/docs/openAPI/go.sum b/api/docs/openAPI/go.sum index b571f5f..7e4dda1 100644 --- a/api/docs/openAPI/go.sum +++ b/api/docs/openAPI/go.sum @@ -1,3 +1,5 @@ +github.com/a-h/respond v0.0.2 h1:mhBwB2XuM+34gfIFs9LuXGfCCbu00rvaCWpTVNHvkPU= +github.com/a-h/respond v0.0.2/go.mod h1:k9UvuVDWmHAb91OsdrqG0xFv7X+HelBpfMJIn9xMYWM= github.com/a-h/rest v0.0.0-20240504113546-6729b3328f85 h1:Lj+OmK3+dKMuR8OdlnUeIrgdt8JkcNlA9isS2Aey5Mg= github.com/a-h/rest v0.0.0-20240504113546-6729b3328f85/go.mod h1:5wH1imbpKnMjll8xpGRdg0Sb0HwH7nYiM5VPm0Zl5Bw= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/api/docs/openAPI/openApi.go b/api/docs/openAPI/openApi.go index 5e563d6..4d65985 100644 --- a/api/docs/openAPI/openApi.go +++ b/api/docs/openAPI/openApi.go @@ -4,7 +4,6 @@ import ( "encoding/json" "github.com/a-h/rest" "log" - "net/http" "os" ) @@ -15,13 +14,12 @@ func main() { api = rest.NewAPI("portfolio") api.StripPkgPaths = []string{"github.com/a-h/rest/example", "github.com/a-h/respond"} - api.Get("/nfc/{uid}"). - HasPathParameter("uid", rest.PathParam{ - Description: "id of the user", - Regexp: `\d+`, - }). - HasDescription("Get nfc data by uid."). - HasResponseModel(http.StatusOK, rest.ModelOf[string]()) + // register models + RegisterModels() + // register endpoints + RegisterGenericEndpoints() + RegisterUserEndpoints() + RegisterProjectEndpoints() // Create the specification. spec, err := api.Spec() diff --git a/api/docs/openAPI/openApiGenericEndpoints.go b/api/docs/openAPI/openApiGenericEndpoints.go new file mode 100644 index 0000000..40dcbf8 --- /dev/null +++ b/api/docs/openAPI/openApiGenericEndpoints.go @@ -0,0 +1,20 @@ +package main + +import ( + "github.com/a-h/respond" + "github.com/a-h/rest" + "net/http" +) + +func RegisterGenericEndpoints() { + + api.Get("/check"). + HasDescription("check for user jwt cookie"). + HasResponseModel(http.StatusOK, rest.ModelOf[string]()). + HasResponseModel(http.StatusInternalServerError, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnprocessableEntity, rest.ModelOf[respond.Error]()) + + api.Get("/htmx/canEdit"). + HasDescription("check if user is allowed to edit"). + HasResponseModel(http.StatusOK, rest.ModelOf[string]()) +} diff --git a/api/docs/openAPI/openApiModels.go b/api/docs/openAPI/openApiModels.go new file mode 100644 index 0000000..7eae57c --- /dev/null +++ b/api/docs/openAPI/openApiModels.go @@ -0,0 +1,9 @@ +package main + +// ############## MUST DO ######################## +// local copy of types to fix unintended behaviour +// ############################################### + +func RegisterModels() { + +} diff --git a/api/docs/openAPI/openApiProjectEndpoints.go b/api/docs/openAPI/openApiProjectEndpoints.go new file mode 100644 index 0000000..c6047be --- /dev/null +++ b/api/docs/openAPI/openApiProjectEndpoints.go @@ -0,0 +1,52 @@ +package main + +import ( + "github.com/a-h/respond" + "github.com/a-h/rest" + "net/http" + "portfolio/database/ent" +) + +func RegisterProjectEndpoints() { + api.Post("/project"). + HasDescription("Create project"). + HasResponseModel(http.StatusOK, rest.ModelOf[string]()). + HasResponseModel(http.StatusInternalServerError, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnprocessableEntity, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnauthorized, rest.ModelOf[string]()) + + api.Patch("/project/{id}"). + HasPathParameter("id", rest.PathParam{ + Description: "id of the project", + Regexp: `\d+`, + }). + HasDescription("Update project by id"). + HasRequestModel(rest.ModelOf[ent.Project]()). + HasResponseModel(http.StatusOK, rest.ModelOf[string]()). + HasResponseModel(http.StatusBadRequest, rest.ModelOf[string]()). + HasResponseModel(http.StatusInternalServerError, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnprocessableEntity, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnauthorized, rest.ModelOf[string]()) + + api.Patch("/projects"). + HasDescription("Update projects WIP"). + HasResponseModel(http.StatusOK, rest.ModelOf[[]ent.Project]()). + HasResponseModel(http.StatusInternalServerError, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnprocessableEntity, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnauthorized, rest.ModelOf[string]()) + + api.Get("/project/{id}"). + HasPathParameter("id", rest.PathParam{ + Description: "id of the project", + Regexp: `\d+`, + }). + HasDescription("Get project by id"). + HasResponseModel(http.StatusOK, rest.ModelOf[ent.Project]()). + HasResponseModel(http.StatusBadRequest, rest.ModelOf[string]()). + HasResponseModel(http.StatusUnprocessableEntity, rest.ModelOf[respond.Error]()) + + api.Get("/projects"). + HasDescription("Get projects"). + HasResponseModel(http.StatusOK, rest.ModelOf[[]ent.Project]()). + HasResponseModel(http.StatusUnprocessableEntity, rest.ModelOf[respond.Error]()) +} diff --git a/api/docs/openAPI/openApiUserEndpoints.go b/api/docs/openAPI/openApiUserEndpoints.go new file mode 100644 index 0000000..8698754 --- /dev/null +++ b/api/docs/openAPI/openApiUserEndpoints.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/a-h/respond" + "github.com/a-h/rest" + "net/http" + "portfolio/api/types" + "portfolio/database/ent" +) + +func RegisterUserEndpoints() { + api.Get("/user/{uid}"). + HasPathParameter("id", rest.PathParam{ + Description: "id of the user", + Regexp: `\d+`, + }). + HasDescription("Get user by uid."). + HasResponseModel(http.StatusOK, rest.ModelOf[ent.User]()). + HasResponseModel(http.StatusBadRequest, rest.ModelOf[string]()). + HasResponseModel(http.StatusInternalServerError, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnprocessableEntity, rest.ModelOf[respond.Error]()) + + api.Post("/register"). + HasDescription("Register."). + HasRequestModel(rest.ModelOf[ent.User]()). + HasResponseModel(http.StatusCreated, rest.ModelOf[string]()). + HasResponseModel(http.StatusBadRequest, rest.ModelOf[string]()). + HasResponseModel(http.StatusInternalServerError, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnprocessableEntity, rest.ModelOf[respond.Error]()) + + api.Post("/login"). + HasDescription("Login."). + HasRequestModel(rest.ModelOf[types.LoginUser]()). + HasResponseModel(http.StatusOK, rest.ModelOf[string]()). + HasResponseModel(http.StatusInternalServerError, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnprocessableEntity, rest.ModelOf[respond.Error]()). + HasResponseModel(http.StatusUnauthorized, rest.ModelOf[string]()) + +} diff --git a/api/handlers/authHandler.go b/api/handlers/authHandler.go index 1cfc0c4..5ef952e 100644 --- a/api/handlers/authHandler.go +++ b/api/handlers/authHandler.go @@ -65,23 +65,19 @@ func Login(w http.ResponseWriter, r *http.Request) { } func CanEdit(w http.ResponseWriter, r *http.Request) { - jwtCookie, _ := r.Cookie("jwt") - if jwtCookie != nil { - _, audience, err := jwt.VerifyJWT(jwtCookie.Value) - if err != nil { - InternalServerErrorHandler(w, err) - return - } - if audience == "owner" || audience == "visitor" { - w.WriteHeader(http.StatusOK) - w.Write([]byte("")) - } else { - w.WriteHeader(http.StatusOK) - w.Write([]byte("")) - } + _, audience, err := jwt.VerifyUser(r) + if err != nil { + w.WriteHeader(http.StatusOK) + w.Write([]byte("")) return } - w.WriteHeader(http.StatusOK) - w.Write([]byte("")) + if audience == "owner" || audience == "visitor" { + w.WriteHeader(http.StatusOK) + w.Write([]byte("")) + } else { + w.WriteHeader(http.StatusOK) + w.Write([]byte("")) + } + return } diff --git a/api/handlers/projectHandler.go b/api/handlers/projectHandler.go index 4500d3f..aff27f0 100644 --- a/api/handlers/projectHandler.go +++ b/api/handlers/projectHandler.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "portfolio/api/service/jwt" + "portfolio/api/service/parse" "portfolio/database/ent" "portfolio/database/query" "strconv" @@ -67,6 +68,54 @@ func UpdateProjectHandler(w http.ResponseWriter, r *http.Request) { } } +func UpdateProjectsHandler(w http.ResponseWriter, r *http.Request) { + + isHtmx := r.Header.Get("HX-Request") + + _, _, err := jwt.VerifyUser(r) + if err != nil { + UnauthorizedHandler(w) + return + } + + var newProjects []*ent.Project + var p []*ent.Project + + if isHtmx == "true" { + p = parse.ParseProjectInput(r) + } else { + + p, err = query.GetFullProjects(context.Background()) + if err != nil { + UnprocessableEntityHandler(w, err) + return + } + + err = json.NewDecoder(r.Body).Decode(&newProjects) + if err != nil { + InternalServerErrorHandler(w, err) + return + } + + for _, project := range p { + for _, newProject := range newProjects { + if project.ID == newProject.ID { + // todo add update from api + } + } + } + } + + for _, project := range p { + err = query.UpdateProject(context.Background(), *project, project.ID) + if err != nil { + UnprocessableEntityHandler(w, err) + return + } + } + +} + func GetProjectHandler(w http.ResponseWriter, r *http.Request) { projectID, err := strconv.Atoi(r.PathValue("id")) diff --git a/api/handlers/userHandler.go b/api/handlers/userHandler.go index 1c1d060..0beb8f0 100644 --- a/api/handlers/userHandler.go +++ b/api/handlers/userHandler.go @@ -57,6 +57,7 @@ func GetUserHandler(w http.ResponseWriter, r *http.Request) { User, err := query.GetUser(context.Background(), userID) if err != nil { + UnprocessableEntityHandler(w, err) return } @@ -64,6 +65,7 @@ func GetUserHandler(w http.ResponseWriter, r *http.Request) { err = json.NewEncoder(w).Encode(User) if err != nil { + InternalServerErrorHandler(w, err) return } } diff --git a/api/service/jwt/verify.go b/api/service/jwt/verify.go index a121f51..15e1501 100644 --- a/api/service/jwt/verify.go +++ b/api/service/jwt/verify.go @@ -10,6 +10,11 @@ import ( ) func VerifyUser(r *http.Request) (int, string, error) { + jwtCookie, _ := r.Cookie("jwt") + + if jwtCookie != nil { + return VerifyJWT(jwtCookie.Value) + } bearerToken := r.Header.Get("Authorization") jwtToken := "" if len(bearerToken) > 7 { diff --git a/api/service/parse/parseInput.go b/api/service/parse/parseInput.go new file mode 100644 index 0000000..14c1525 --- /dev/null +++ b/api/service/parse/parseInput.go @@ -0,0 +1,40 @@ +package parse + +import ( + "context" + "io" + "log" + "net/http" + "net/url" + "portfolio/database/ent" + "portfolio/database/query" + "strconv" +) + +func ParseProjectInput(r *http.Request) []*ent.Project { + b, err := io.ReadAll(r.Body) + if err != nil { + log.Fatalln(err) + } + + body, err := url.ParseQuery(string(b)) + + var projects []*ent.Project + + for x := range body["project_name"] { + var p *ent.Project + + projectID, err := strconv.Atoi(body["project_id"][x]) + + p, err = query.GetFullProject(context.Background(), projectID) + if err != nil { + break + } + p.Name = body["project_name"][x] + p.URL = body["project_repo"][x] + p.DocURL = body["project_docs"][x] + p.Description = body["project_description"][x] + projects = append(projects, p) + } + return projects +} diff --git a/api/types/userTypes.go b/api/types/userTypes.go index 7a0066b..cfcae0a 100644 --- a/api/types/userTypes.go +++ b/api/types/userTypes.go @@ -3,3 +3,8 @@ package types type Username struct { Name string } + +type LoginUser struct { + Email string + Password string +} diff --git a/common/docs/topics/test.md b/common/docs/topics/test.md index f2099fe..b5fb66c 100644 --- a/common/docs/topics/test.md +++ b/common/docs/topics/test.md @@ -1,3 +1,6 @@ -# test +# About + +## WIP + +Mijn portfolio website heb ik volledig gemaakt in go. Hiervoor heb ik gebruik gemaakt van gomponents en bijgeleverde plugins. Gomponents is een library waarmee ik binnen go html kan schrijven als object. Voor alle functionaliteit heb ik gebruik gemaakt van htmx. -Start typing here... \ No newline at end of file diff --git a/database/ent/schema/user.go b/database/ent/schema/user.go index c602ec6..fb01432 100644 --- a/database/ent/schema/user.go +++ b/database/ent/schema/user.go @@ -18,7 +18,8 @@ func (User) Fields() []ent.Field { Unique(), field.String("email"). Unique(), - field.String("password"), + field.String("password"). + Sensitive(), field.Enum("role"). Values("owner", "admin", "user", "visitor"), } diff --git a/database/ent/user.go b/database/ent/user.go index c57b43b..dbd22a9 100644 --- a/database/ent/user.go +++ b/database/ent/user.go @@ -21,7 +21,7 @@ type User struct { // Email holds the value of the "email" field. Email string `json:"email,omitempty"` // Password holds the value of the "password" field. - Password string `json:"password,omitempty"` + Password string `json:"-"` // Role holds the value of the "role" field. Role user.Role `json:"role,omitempty"` // Edges holds the relations/edges for other nodes in the graph. @@ -165,8 +165,7 @@ func (u *User) String() string { builder.WriteString("email=") builder.WriteString(u.Email) builder.WriteString(", ") - builder.WriteString("password=") - builder.WriteString(u.Password) + builder.WriteString("password=") builder.WriteString(", ") builder.WriteString("role=") builder.WriteString(fmt.Sprintf("%v", u.Role)) diff --git a/database/query/projectQuery.go b/database/query/projectQuery.go index 04622c0..4db6e37 100644 --- a/database/query/projectQuery.go +++ b/database/query/projectQuery.go @@ -69,6 +69,19 @@ func GetFullProject(ctx context.Context, projectID int) (*ent.Project, error) { return p, err } +func GetFullProjects(ctx context.Context) ([]*ent.Project, error) { + + p, err := database.Client.Project. + Query(). + WithUsers(). + WithTeams(). + All(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get project: %w", err) + } + return p, err +} + func GetProjects(ctx context.Context) (ent.Projects, error) { p, err := database.Client.Project.Query(). diff --git a/go.mod b/go.mod index 5cf9ae0..d49eab1 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/lib/pq v1.10.9 github.com/maragudk/gomponents v0.20.2 github.com/maragudk/gomponents-htmx v0.5.0 + github.com/rs/cors v1.11.0 github.com/willoma/bulma-gomponents v0.11.1 github.com/willoma/gomplements v0.6.1 golang.org/x/crypto v0.23.0 @@ -24,7 +25,6 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/rs/cors v1.11.0 // indirect github.com/zclconf/go-cty v1.14.2 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/text v0.15.0 // indirect diff --git a/go.work.sum b/go.work.sum index f622113..692bff0 100644 --- a/go.work.sum +++ b/go.work.sum @@ -18,7 +18,6 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -63,23 +62,16 @@ github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY3 github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg= k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY= diff --git a/web/components/edit.go b/web/components/Buttons.go similarity index 53% rename from web/components/edit.go rename to web/components/Buttons.go index 6117683..cd25b58 100644 --- a/web/components/edit.go +++ b/web/components/Buttons.go @@ -4,6 +4,7 @@ import ( g "github.com/maragudk/gomponents" hx "github.com/maragudk/gomponents-htmx" . "github.com/maragudk/gomponents/html" + b "github.com/willoma/bulma-gomponents" ) func Edit() g.Node { @@ -20,3 +21,15 @@ func Edit() g.Node { hx.Swap("outerHTML")), ) } + +func Save() g.Node { + return Div( + Class("px-3 py-2"), + hx.Patch(BaseUrl+"/projects"), + hx.Swap("none"), + hx.SelectOOB("true"), + hx.Include("[name='project_name'], [name='project_repo'], [name='project_docs'], [name='project_description'], [name='project_id']"), + hx.Confirm("Are u sure?"), + b.Button("Save", b.Link), + ) +} diff --git a/web/components/projectList.go b/web/components/projectList.go index 3af6bba..fffcc21 100644 --- a/web/components/projectList.go +++ b/web/components/projectList.go @@ -7,6 +7,7 @@ import ( b "github.com/willoma/bulma-gomponents" e "github.com/willoma/gomplements" "portfolio/database/ent" + "strconv" ) func ProjectList(projects []*ent.Project) g.Node { @@ -55,17 +56,23 @@ func EditProject(project *ent.Project) g.Node { b.ImgSq64, ), ), + Input(Type("hidden"), Value(strconv.Itoa(project.ID)), Name("project_id")), + //b.Label("ID: "+strconv.Itoa(project.ID), Name("project_id")), b.Label("Name"), - b.Textarea(project.Name, b.Rows(1)), + b.Textarea(project.Name, b.Rows(1), Name("project_name")), b.Subtitle( 6, b.Label("Repo"), - b.Textarea(project.URL, b.Rows(1)), + b.Textarea(project.URL, b.Rows(1), Name("project_repo")), b.Label("Docs"), - b.Textarea(project.DocURL, b.Rows(1))), + b.Textarea(project.DocURL, b.Rows(1), Name("project_docs"))), ), b.Content( - b.Textarea(project.Description), + b.Textarea(project.Description, Name("project_description")), ), + + //b.CardFooter( + //Save(), + //), ) } diff --git a/web/handlers/projectPageHandler.go b/web/handlers/projectPageHandler.go index 50470e7..636dadc 100644 --- a/web/handlers/projectPageHandler.go +++ b/web/handlers/projectPageHandler.go @@ -42,5 +42,6 @@ func CreateProjectEditBody(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) Body( components.EditProjectList(projects), + components.Save(), ).Render(w) }