Merge pull request #1 from DariusKlein/EditProjects

Edit projects
This commit is contained in:
DariusKlein 2024-07-04 12:41:31 +02:00 committed by GitHub
commit c606efd7dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 294 additions and 44 deletions

View File

@ -25,6 +25,7 @@ func ApiRoutes() *http.ServeMux {
//Project //Project
mux.HandleFunc("POST /project", handlers.CreateProjectHandler) mux.HandleFunc("POST /project", handlers.CreateProjectHandler)
mux.HandleFunc("PATCH /project/{id}", handlers.UpdateProjectHandler) mux.HandleFunc("PATCH /project/{id}", handlers.UpdateProjectHandler)
mux.HandleFunc("PATCH /projects", handlers.UpdateProjectsHandler)
mux.HandleFunc("GET /project/{id}", handlers.GetProjectHandler) mux.HandleFunc("GET /project/{id}", handlers.GetProjectHandler)
mux.HandleFunc("GET /projects", handlers.GetProjectsHandler) mux.HandleFunc("GET /projects", handlers.GetProjectsHandler)

View File

@ -2,7 +2,10 @@ module openAPI
go 1.22 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 ( require (
github.com/getkin/kin-openapi v0.124.0 // indirect github.com/getkin/kin-openapi v0.124.0 // indirect

View File

@ -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 h1:Lj+OmK3+dKMuR8OdlnUeIrgdt8JkcNlA9isS2Aey5Mg=
github.com/a-h/rest v0.0.0-20240504113546-6729b3328f85/go.mod h1:5wH1imbpKnMjll8xpGRdg0Sb0HwH7nYiM5VPm0Zl5Bw= 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= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=

View File

@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"github.com/a-h/rest" "github.com/a-h/rest"
"log" "log"
"net/http"
"os" "os"
) )
@ -15,13 +14,12 @@ func main() {
api = rest.NewAPI("portfolio") api = rest.NewAPI("portfolio")
api.StripPkgPaths = []string{"github.com/a-h/rest/example", "github.com/a-h/respond"} api.StripPkgPaths = []string{"github.com/a-h/rest/example", "github.com/a-h/respond"}
api.Get("/nfc/{uid}"). // register models
HasPathParameter("uid", rest.PathParam{ RegisterModels()
Description: "id of the user", // register endpoints
Regexp: `\d+`, RegisterGenericEndpoints()
}). RegisterUserEndpoints()
HasDescription("Get nfc data by uid."). RegisterProjectEndpoints()
HasResponseModel(http.StatusOK, rest.ModelOf[string]())
// Create the specification. // Create the specification.
spec, err := api.Spec() spec, err := api.Spec()

View File

@ -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]())
}

View File

@ -0,0 +1,9 @@
package main
// ############## MUST DO ########################
// local copy of types to fix unintended behaviour
// ###############################################
func RegisterModels() {
}

View File

@ -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]())
}

View File

@ -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]())
}

View File

@ -65,12 +65,11 @@ func Login(w http.ResponseWriter, r *http.Request) {
} }
func CanEdit(w http.ResponseWriter, r *http.Request) { func CanEdit(w http.ResponseWriter, r *http.Request) {
jwtCookie, _ := r.Cookie("jwt")
if jwtCookie != nil { _, audience, err := jwt.VerifyUser(r)
_, audience, err := jwt.VerifyJWT(jwtCookie.Value)
if err != nil { if err != nil {
InternalServerErrorHandler(w, err) w.WriteHeader(http.StatusOK)
w.Write([]byte(""))
return return
} }
if audience == "owner" || audience == "visitor" { if audience == "owner" || audience == "visitor" {
@ -81,7 +80,4 @@ func CanEdit(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("")) w.Write([]byte(""))
} }
return return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(""))
} }

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"portfolio/api/service/jwt" "portfolio/api/service/jwt"
"portfolio/api/service/parse"
"portfolio/database/ent" "portfolio/database/ent"
"portfolio/database/query" "portfolio/database/query"
"strconv" "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) { func GetProjectHandler(w http.ResponseWriter, r *http.Request) {
projectID, err := strconv.Atoi(r.PathValue("id")) projectID, err := strconv.Atoi(r.PathValue("id"))

View File

@ -57,6 +57,7 @@ func GetUserHandler(w http.ResponseWriter, r *http.Request) {
User, err := query.GetUser(context.Background(), userID) User, err := query.GetUser(context.Background(), userID)
if err != nil { if err != nil {
UnprocessableEntityHandler(w, err)
return return
} }
@ -64,6 +65,7 @@ func GetUserHandler(w http.ResponseWriter, r *http.Request) {
err = json.NewEncoder(w).Encode(User) err = json.NewEncoder(w).Encode(User)
if err != nil { if err != nil {
InternalServerErrorHandler(w, err)
return return
} }
} }

View File

@ -10,6 +10,11 @@ import (
) )
func VerifyUser(r *http.Request) (int, string, error) { func VerifyUser(r *http.Request) (int, string, error) {
jwtCookie, _ := r.Cookie("jwt")
if jwtCookie != nil {
return VerifyJWT(jwtCookie.Value)
}
bearerToken := r.Header.Get("Authorization") bearerToken := r.Header.Get("Authorization")
jwtToken := "" jwtToken := ""
if len(bearerToken) > 7 { if len(bearerToken) > 7 {

View File

@ -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
}

View File

@ -3,3 +3,8 @@ package types
type Username struct { type Username struct {
Name string Name string
} }
type LoginUser struct {
Email string
Password string
}

View File

@ -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...

View File

@ -18,7 +18,8 @@ func (User) Fields() []ent.Field {
Unique(), Unique(),
field.String("email"). field.String("email").
Unique(), Unique(),
field.String("password"), field.String("password").
Sensitive(),
field.Enum("role"). field.Enum("role").
Values("owner", "admin", "user", "visitor"), Values("owner", "admin", "user", "visitor"),
} }

View File

@ -21,7 +21,7 @@ type User struct {
// Email holds the value of the "email" field. // Email holds the value of the "email" field.
Email string `json:"email,omitempty"` Email string `json:"email,omitempty"`
// Password holds the value of the "password" field. // 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 holds the value of the "role" field.
Role user.Role `json:"role,omitempty"` Role user.Role `json:"role,omitempty"`
// Edges holds the relations/edges for other nodes in the graph. // 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("email=")
builder.WriteString(u.Email) builder.WriteString(u.Email)
builder.WriteString(", ") builder.WriteString(", ")
builder.WriteString("password=") builder.WriteString("password=<sensitive>")
builder.WriteString(u.Password)
builder.WriteString(", ") builder.WriteString(", ")
builder.WriteString("role=") builder.WriteString("role=")
builder.WriteString(fmt.Sprintf("%v", u.Role)) builder.WriteString(fmt.Sprintf("%v", u.Role))

View File

@ -69,6 +69,19 @@ func GetFullProject(ctx context.Context, projectID int) (*ent.Project, error) {
return p, err 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) { func GetProjects(ctx context.Context) (ent.Projects, error) {
p, err := database.Client.Project.Query(). p, err := database.Client.Project.Query().

2
go.mod
View File

@ -10,6 +10,7 @@ require (
github.com/lib/pq v1.10.9 github.com/lib/pq v1.10.9
github.com/maragudk/gomponents v0.20.2 github.com/maragudk/gomponents v0.20.2
github.com/maragudk/gomponents-htmx v0.5.0 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/bulma-gomponents v0.11.1
github.com/willoma/gomplements v0.6.1 github.com/willoma/gomplements v0.6.1
golang.org/x/crypto v0.23.0 golang.org/x/crypto v0.23.0
@ -24,7 +25,6 @@ require (
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/hashicorp/hcl/v2 v2.19.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.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 github.com/zclconf/go-cty v1.14.2 // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/text v0.15.0 // indirect golang.org/x/text v0.15.0 // indirect

View File

@ -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/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 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 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 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 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= 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 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 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 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= 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 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 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 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 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 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg=
k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY= modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=

View File

@ -4,6 +4,7 @@ import (
g "github.com/maragudk/gomponents" g "github.com/maragudk/gomponents"
hx "github.com/maragudk/gomponents-htmx" hx "github.com/maragudk/gomponents-htmx"
. "github.com/maragudk/gomponents/html" . "github.com/maragudk/gomponents/html"
b "github.com/willoma/bulma-gomponents"
) )
func Edit() g.Node { func Edit() g.Node {
@ -20,3 +21,15 @@ func Edit() g.Node {
hx.Swap("outerHTML")), 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),
)
}

View File

@ -7,6 +7,7 @@ import (
b "github.com/willoma/bulma-gomponents" b "github.com/willoma/bulma-gomponents"
e "github.com/willoma/gomplements" e "github.com/willoma/gomplements"
"portfolio/database/ent" "portfolio/database/ent"
"strconv"
) )
func ProjectList(projects []*ent.Project) g.Node { func ProjectList(projects []*ent.Project) g.Node {
@ -55,17 +56,23 @@ func EditProject(project *ent.Project) g.Node {
b.ImgSq64, 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.Label("Name"),
b.Textarea(project.Name, b.Rows(1)), b.Textarea(project.Name, b.Rows(1), Name("project_name")),
b.Subtitle( b.Subtitle(
6, 6,
b.Label("Repo"), b.Label("Repo"),
b.Textarea(project.URL, b.Rows(1)), b.Textarea(project.URL, b.Rows(1), Name("project_repo")),
b.Label("Docs"), b.Label("Docs"),
b.Textarea(project.DocURL, b.Rows(1))), b.Textarea(project.DocURL, b.Rows(1), Name("project_docs"))),
), ),
b.Content( b.Content(
b.Textarea(project.Description), b.Textarea(project.Description, Name("project_description")),
), ),
//b.CardFooter(
//Save(),
//),
) )
} }

View File

@ -42,5 +42,6 @@ func CreateProjectEditBody(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
Body( Body(
components.EditProjectList(projects), components.EditProjectList(projects),
components.Save(),
).Render(w) ).Render(w)
} }