From 9a92c4aad6a9a9be27f6ccb47612ff53d3a385df Mon Sep 17 00:00:00 2001 From: mikestefanello <552328+mikestefanello@users.noreply.github.com> Date: Mon, 31 Mar 2025 19:11:08 -0400 Subject: [PATCH] Started on dynamic entity add form. --- pkg/handlers/admin.go | 17 ++++++++++---- pkg/handlers/auth.go | 4 ++-- pkg/middleware/auth.go | 28 ++++++++++------------ pkg/ui/components/form.go | 6 +++++ pkg/ui/pages/entity.go | 49 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 23 deletions(-) diff --git a/pkg/handlers/admin.go b/pkg/handlers/admin.go index d1d045d..e5ba8f2 100644 --- a/pkg/handlers/admin.go +++ b/pkg/handlers/admin.go @@ -10,6 +10,7 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/entc/gen" + "entgo.io/ent/entc/load" "github.com/labstack/echo/v4" "github.com/mikestefanello/pagoda/ent" "github.com/mikestefanello/pagoda/ent/passwordtoken" @@ -45,7 +46,7 @@ func (h *Admin) Routes(g *echo.Group) { entities := g.Group("/admin/content") for _, p := range h.getEntityPlugins() { - pg := entities.Group(fmt.Sprintf("/%s", p.ID)) + pg := entities.Group(fmt.Sprintf("/%s", strings.ToLower(p.ID))) pg.GET("", h.EntityList(p)).Name = p.RouteNameList() pg.POST("", h.EntityList(p)).Name = p.RouteNameListSubmit() pg.GET("/add", h.EntityAdd(p)).Name = p.RouteNameAdd() @@ -103,13 +104,19 @@ func (h *Admin) EntityList(p AdminEntityPlugin) echo.HandlerFunc { func (h *Admin) EntityAdd(p AdminEntityPlugin) echo.HandlerFunc { return func(ctx echo.Context) error { - return nil + var schema *load.Schema + for _, s := range h.graph.Schemas { + if s.Name == p.ID { + schema = s + } + } + return pages.AdminEntityAdd(ctx, schema) } } func (h *Admin) EntityAddSubmit(p AdminEntityPlugin) echo.HandlerFunc { return func(ctx echo.Context) error { - return nil + return h.EntityAdd(p)(ctx) } } @@ -193,7 +200,7 @@ func (p *AdminEntityPlugin) RouteNameDeleteSubmit() string { func (h *Admin) getEntityPlugins() []AdminEntityPlugin { return []AdminEntityPlugin{ { - ID: "user", + ID: "User", Label: "User", LabelPlural: "Users", Heading: []string{ @@ -238,7 +245,7 @@ func (h *Admin) getEntityPlugins() []AdminEntityPlugin { }, }, { - ID: "passwordtoken", + ID: "PasswordToken", Label: "Password token", LabelPlural: "Password tokens", Heading: []string{ diff --git a/pkg/handlers/auth.go b/pkg/handlers/auth.go index 4638dca..a96ce8f 100644 --- a/pkg/handlers/auth.go +++ b/pkg/handlers/auth.go @@ -39,10 +39,10 @@ func (h *Auth) Init(c *services.Container) error { } func (h *Auth) Routes(g *echo.Group) { - g.GET("/logout", h.Logout, middleware.RequireAuthentication()).Name = routenames.Logout + g.GET("/logout", h.Logout, middleware.RequireAuthentication).Name = routenames.Logout g.GET("/email/verify/:token", h.VerifyEmail).Name = routenames.VerifyEmail - noAuth := g.Group("/user", middleware.RequireNoAuthentication()) + noAuth := g.Group("/user", middleware.RequireNoAuthentication) noAuth.GET("/login", h.LoginPage).Name = routenames.Login noAuth.POST("/login", h.LoginSubmit).Name = routenames.LoginSubmit noAuth.GET("/register", h.RegisterPage).Name = routenames.Register diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index 54a8346..542d1b9 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -83,27 +83,23 @@ func LoadValidPasswordToken(authClient *services.AuthClient) echo.MiddlewareFunc } // RequireAuthentication requires that the user be authenticated in order to proceed -func RequireAuthentication() echo.MiddlewareFunc { - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if u := c.Get(context.AuthenticatedUserKey); u == nil { - return echo.NewHTTPError(http.StatusUnauthorized) - } - - return next(c) +func RequireAuthentication(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + if u := c.Get(context.AuthenticatedUserKey); u == nil { + return echo.NewHTTPError(http.StatusUnauthorized) } + + return next(c) } } // RequireNoAuthentication requires that the user not be authenticated in order to proceed -func RequireNoAuthentication() echo.MiddlewareFunc { - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - if u := c.Get(context.AuthenticatedUserKey); u != nil { - return echo.NewHTTPError(http.StatusForbidden) - } - - return next(c) +func RequireNoAuthentication(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + if u := c.Get(context.AuthenticatedUserKey); u != nil { + return echo.NewHTTPError(http.StatusForbidden) } + + return next(c) } } diff --git a/pkg/ui/components/form.go b/pkg/ui/components/form.go index 7dee6cf..1dbc38b 100644 --- a/pkg/ui/components/form.go +++ b/pkg/ui/components/form.go @@ -153,6 +153,8 @@ func FileField(name, label string) Node { func formFieldStatusClass(fm form.Form, formField string) string { switch { + case fm == nil: + return "" case !fm.IsSubmitted(): return "" case fm.FieldHasErrors(formField): @@ -163,6 +165,10 @@ func formFieldStatusClass(fm form.Form, formField string) string { } func formFieldErrors(fm form.Form, field string) Node { + if fm == nil { + return nil + } + errs := fm.GetFieldErrors(field) if len(errs) == 0 { return nil diff --git a/pkg/ui/pages/entity.go b/pkg/ui/pages/entity.go index 30d4fe6..7c15358 100644 --- a/pkg/ui/pages/entity.go +++ b/pkg/ui/pages/entity.go @@ -1,8 +1,11 @@ package pages import ( + "fmt" "net/http" + "entgo.io/ent/entc/load" + "entgo.io/ent/schema/field" "github.com/labstack/echo/v4" "github.com/mikestefanello/pagoda/pkg/pager" "github.com/mikestefanello/pagoda/pkg/ui" @@ -31,6 +34,52 @@ func AdminEntityDelete(ctx echo.Context) error { return r.Render(layouts.Admin, form) } +func AdminEntityAdd(ctx echo.Context, schema *load.Schema) error { + r := ui.NewRequest(ctx) + r.Title = fmt.Sprintf("Add %s", "entity") // TODO + + nodes := make(Group, 0) + + for _, f := range schema.Fields { + switch f.Info.Type { + case field.TypeString: + nodes = append(nodes, InputField(InputFieldParams{ + Name: f.Name, + InputType: "text", + Label: f.Name, + })) + case field.TypeTime: + nodes = append(nodes, InputField(InputFieldParams{ + Name: f.Name, + InputType: "datetime", + Label: f.Name, + })) + case field.TypeBool: + nodes = append(nodes, P(Textf("%s not supported", f.Name))) + default: + nodes = append(nodes, P(Textf("%s not supported", f.Name))) + } + } + + for _, e := range schema.Edges { + if e.Inverse { + continue + } + nodes = append(nodes, InputField(InputFieldParams{ + Name: e.Name, + InputType: "number", + Label: e.Name, + })) + } + + nodes = append(nodes, ControlGroup( + FormButton("is-primary", "Submit"), + ButtonLink("/", "is-secondary", "Cancel"), + ), CSRF(r)) + + return r.Render(layouts.Admin, Form(Method(http.MethodPost), nodes)) +} + type AdminEntityListParams struct { Title string Headers []string