diff --git a/README.md b/README.md index 5c1ed9d..38bcf4e 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,6 @@ The container is located at `pkg/services/container.go` and is meant to house al - Configuration - Database - Files -- Graph - Mail - ORM - Tasks @@ -433,7 +432,7 @@ Users with admin [access](#access) will see additional links on the default side In order to automatically and dynamically provide admin functionality for entities, code generation is used by means of leveraging Ent's [extension API](https://entgo.io/docs/extensions) which makes generating code using the Ent graph schema very easy. A [custom extension](https://github.com/mikestefanello/pagoda/blob/master/ent/admin/extension.go) is provided to generate code that provides flat entity type structs and handler code that work directly with Echo. So, both of those are required in order for any of this to work. Whenever you modify one of your entity types or generate a new one, the admin code will also automatically generate. -Without going in to too much detail here, the generated code provides a [handler](https://github.com/mikestefanello/pagoda/blob/master/ent/admin/handler.go) that is then used by a provided [web handler](https://github.com/mikestefanello/pagoda/blob/master/pkg/handlers/admin.go) to power all the routes used in the admin UI. While the rest of the related code should be simple enough to follow, it's worth calling attention to the highly-dynamic [entity form](https://github.com/mikestefanello/pagoda/blob/master/pkg/ui/forms/admin_entity.go) that is constructed using the _Ent_ graph data structure. +Without going in to too much detail here, the generated code provides a [handler](https://github.com/mikestefanello/pagoda/blob/master/ent/admin/handler.go) that is then used by a provided [web handler](https://github.com/mikestefanello/pagoda/blob/master/pkg/handlers/admin.go) to power all the routes used in the admin UI. While the rest of the related code should be simple enough to follow, it's worth calling attention to the highly dynamic [entity form](https://github.com/mikestefanello/pagoda/blob/master/pkg/ui/forms/admin_entity.go) that is constructed using the _Ent_ graph data structure. ### Access @@ -451,7 +450,7 @@ Since the generated code is completely dynamic, all entity functionality related * Determine which tests should be included and provide them. * Inline validation. -* Either exposed sorting, or allow the _handler_ to be configured with sort criteria for each type. +* Either exposed sorting or allow the _handler_ to be configured with sort criteria for each type. * Exposed filters. * Support all field types (types such as _JSON_ as currently not supported). * Control which fields appear in the entity list table. diff --git a/ent/admin/handler.go b/ent/admin/handler.go index c6108a8..bb74b15 100644 --- a/ent/admin/handler.go +++ b/ent/admin/handler.go @@ -30,55 +30,55 @@ func NewHandler(client *ent.Client, cfg HandlerConfig) *Handler { } } -func (h *Handler) Create(ctx echo.Context, entityType string) error { - switch entityType { - case "PasswordToken": +func (h *Handler) Create(ctx echo.Context, entityType EntityType) error { + switch entityType.(type) { + case *PasswordToken: return h.PasswordTokenCreate(ctx) - case "User": + case *User: return h.UserCreate(ctx) default: return fmt.Errorf("unsupported entity type: %s", entityType) } } -func (h *Handler) Get(ctx echo.Context, entityType string, id int) (url.Values, error) { - switch entityType { - case "PasswordToken": +func (h *Handler) Get(ctx echo.Context, entityType EntityType, id int) (url.Values, error) { + switch entityType.(type) { + case *PasswordToken: return h.PasswordTokenGet(ctx, id) - case "User": + case *User: return h.UserGet(ctx, id) default: return nil, fmt.Errorf("unsupported entity type: %s", entityType) } } -func (h *Handler) Delete(ctx echo.Context, entityType string, id int) error { - switch entityType { - case "PasswordToken": +func (h *Handler) Delete(ctx echo.Context, entityType EntityType, id int) error { + switch entityType.(type) { + case *PasswordToken: return h.PasswordTokenDelete(ctx, id) - case "User": + case *User: return h.UserDelete(ctx, id) default: return fmt.Errorf("unsupported entity type: %s", entityType) } } -func (h *Handler) Update(ctx echo.Context, entityType string, id int) error { - switch entityType { - case "PasswordToken": +func (h *Handler) Update(ctx echo.Context, entityType EntityType, id int) error { + switch entityType.(type) { + case *PasswordToken: return h.PasswordTokenUpdate(ctx, id) - case "User": + case *User: return h.UserUpdate(ctx, id) default: return fmt.Errorf("unsupported entity type: %s", entityType) } } -func (h *Handler) List(ctx echo.Context, entityType string) (*EntityList, error) { - switch entityType { - case "PasswordToken": +func (h *Handler) List(ctx echo.Context, entityType EntityType) (*EntityList, error) { + switch entityType.(type) { + case *PasswordToken: return h.PasswordTokenList(ctx) - case "User": + case *User: return h.UserList(ctx) default: return nil, fmt.Errorf("unsupported entity type: %s", entityType) diff --git a/ent/admin/schema.go b/ent/admin/schema.go new file mode 100644 index 0000000..e6ae61d --- /dev/null +++ b/ent/admin/schema.go @@ -0,0 +1,101 @@ +// Code generated by ent, DO NOT EDIT. +package admin + +import ( + "entgo.io/ent/schema/field" +) + +type Enum struct { + Label, Value string +} + +type FieldSchema struct { + Name string + Type field.Type + Optional bool + Immutable bool + Sensitive bool + Enums []Enum +} + +const NamePasswordToken = "PasswordToken" + +var fieldsPasswordToken = []*FieldSchema{ + { + Name: "token", + Type: field.TypeString, + Optional: false, + Immutable: false, + Sensitive: true, + Enums: nil, + }, + { + Name: "user_id", + Type: field.TypeInt, + Optional: false, + Immutable: false, + Sensitive: false, + Enums: nil, + }, + { + Name: "created_at", + Type: field.TypeTime, + Optional: false, + Immutable: false, + Sensitive: false, + Enums: nil, + }, +} + +const NameUser = "User" + +var fieldsUser = []*FieldSchema{ + { + Name: "name", + Type: field.TypeString, + Optional: false, + Immutable: false, + Sensitive: false, + Enums: nil, + }, + { + Name: "email", + Type: field.TypeString, + Optional: false, + Immutable: false, + Sensitive: false, + Enums: nil, + }, + { + Name: "password", + Type: field.TypeString, + Optional: false, + Immutable: false, + Sensitive: true, + Enums: nil, + }, + { + Name: "verified", + Type: field.TypeBool, + Optional: false, + Immutable: false, + Sensitive: false, + Enums: nil, + }, + { + Name: "admin", + Type: field.TypeBool, + Optional: false, + Immutable: false, + Sensitive: false, + Enums: nil, + }, + { + Name: "created_at", + Type: field.TypeTime, + Optional: false, + Immutable: true, + Sensitive: false, + Enums: nil, + }, +} diff --git a/ent/admin/templates/handler.tmpl b/ent/admin/templates/handler.tmpl index 6f3f8da..e373ca4 100644 --- a/ent/admin/templates/handler.tmpl +++ b/ent/admin/templates/handler.tmpl @@ -35,10 +35,10 @@ } } - func (h *Handler) Create(ctx echo.Context, entityType string) error { - switch entityType { + func (h *Handler) Create(ctx echo.Context, entityType EntityType) error { + switch entityType.(type) { {{- range $n := $.Nodes }} - case "{{ $n.Name }}": + case *{{ $n.Name }}: return h.{{ $n.Name }}Create(ctx) {{- end }} default: @@ -46,10 +46,10 @@ } } - func (h *Handler) Get(ctx echo.Context, entityType string, id int) (url.Values, error) { - switch entityType { + func (h *Handler) Get(ctx echo.Context, entityType EntityType, id int) (url.Values, error) { + switch entityType.(type) { {{- range $n := $.Nodes }} - case "{{ $n.Name }}": + case *{{ $n.Name }}: return h.{{ $n.Name }}Get(ctx, id) {{- end }} default: @@ -57,10 +57,10 @@ } } - func (h *Handler) Delete(ctx echo.Context, entityType string, id int) error { - switch entityType { + func (h *Handler) Delete(ctx echo.Context, entityType EntityType, id int) error { + switch entityType.(type) { {{- range $n := $.Nodes }} - case "{{ $n.Name }}": + case *{{ $n.Name }}: return h.{{ $n.Name }}Delete(ctx, id) {{- end }} default: @@ -68,10 +68,10 @@ } } - func (h *Handler) Update(ctx echo.Context, entityType string, id int) error { - switch entityType { + func (h *Handler) Update(ctx echo.Context, entityType EntityType, id int) error { + switch entityType.(type) { {{- range $n := $.Nodes }} - case "{{ $n.Name }}": + case *{{ $n.Name }}: return h.{{ $n.Name }}Update(ctx, id) {{- end }} default: @@ -79,10 +79,10 @@ } } - func (h *Handler) List(ctx echo.Context, entityType string) (*EntityList, error) { - switch entityType { + func (h *Handler) List(ctx echo.Context, entityType EntityType) (*EntityList, error) { + switch entityType.(type) { {{- range $n := $.Nodes }} - case "{{ $n.Name }}": + case *{{ $n.Name }}: return h.{{ $n.Name }}List(ctx) {{- end }} default: diff --git a/ent/admin/templates/schema.tmpl b/ent/admin/templates/schema.tmpl new file mode 100644 index 0000000..37a0d09 --- /dev/null +++ b/ent/admin/templates/schema.tmpl @@ -0,0 +1,54 @@ +{{/* Tell Intellij/GoLand to enable the autocompletion based on the *gen.Graph type. */}} +{{/* gotype: entgo.io/ent/entc/gen.Graph */}} + +{{ define "admin/schema" }} + // Code generated by ent, DO NOT EDIT. + package admin + + import ( + "entgo.io/ent/schema/field" + ) + + type Enum struct { + Label, Value string + } + + type FieldSchema struct { + Name string + Type field.Type + Optional bool + Immutable bool + Sensitive bool + Enums []Enum + } + + + {{- range $n := $.Nodes }} + const Name{{ $n.Name }} = "{{ $n.Name }}" + + var fields{{ $n.Name }} = []*FieldSchema{ + {{- range $f := $n.Fields }} + { + Name: "{{ $f.Name }}", + Type: field.{{ $f.Type.Type.ConstName }}, + Optional: {{ $f.Optional }}, + Immutable: {{ $f.Immutable }}, + Sensitive: {{ $f.Sensitive }}, + {{- if len $f.Enums }} + Enums: []Enum{ + {{- range $e := $f.Enums }} + { + Label: "{{ $e.Label }}", + Value: "{{ $e.Value }}", + }, + {{- end }} + }, + {{- else }} + Enums: nil, + {{- end }} + }, + {{- end }} + } + {{ end }} + +{{ end }} \ No newline at end of file diff --git a/ent/admin/templates/types.tmpl b/ent/admin/templates/types.tmpl index 5231503..e6c3f83 100644 --- a/ent/admin/templates/types.tmpl +++ b/ent/admin/templates/types.tmpl @@ -11,8 +11,27 @@ {{ fieldName $f.Name }} {{ if (fieldIsPointer $f) }}*{{ end }}{{ $f.Type }} `form:"{{ $f.Name }}"` {{- end }} } + + func (e *{{ $n.Name }}) GetName() string { + return Name{{ $n.Name }} + } + + func (e *{{ $n.Name }}) GetSchema() []*FieldSchema { + return fields{{ $n.Name }} + } {{ end }} + type EntityType interface { + GetName() string + GetSchema() []*FieldSchema + } + + var entityTypes = []EntityType{ + {{- range $n := $.Nodes }} + &{{ $n.Name }}{}, + {{- end }} + } + type EntityList struct { Columns []string Entities []EntityValues @@ -31,12 +50,7 @@ TimeFormat string } - func GetEntityTypeNames() []string { - return []string{ - {{- range $n := $.Nodes }} - "{{ $n.Name }}", - {{- end }} - } + func GetEntityTypes() []EntityType { + return entityTypes } - {{ end }} \ No newline at end of file diff --git a/ent/admin/types.go b/ent/admin/types.go index a786a92..54f3a52 100644 --- a/ent/admin/types.go +++ b/ent/admin/types.go @@ -9,6 +9,14 @@ type PasswordToken struct { CreatedAt *time.Time `form:"created_at"` } +func (e *PasswordToken) GetName() string { + return NamePasswordToken +} + +func (e *PasswordToken) GetSchema() []*FieldSchema { + return fieldsPasswordToken +} + type User struct { Name string `form:"name"` Email string `form:"email"` @@ -18,6 +26,24 @@ type User struct { CreatedAt *time.Time `form:"created_at"` } +func (e *User) GetName() string { + return NameUser +} + +func (e *User) GetSchema() []*FieldSchema { + return fieldsUser +} + +type EntityType interface { + GetName() string + GetSchema() []*FieldSchema +} + +var entityTypes = []EntityType{ + &PasswordToken{}, + &User{}, +} + type EntityList struct { Columns []string Entities []EntityValues @@ -36,9 +62,6 @@ type HandlerConfig struct { TimeFormat string } -func GetEntityTypeNames() []string { - return []string{ - "PasswordToken", - "User", - } +func GetEntityTypes() []EntityType { + return entityTypes } diff --git a/pkg/handlers/admin.go b/pkg/handlers/admin.go index fd92683..59e2504 100644 --- a/pkg/handlers/admin.go +++ b/pkg/handlers/admin.go @@ -7,8 +7,6 @@ import ( "strings" "time" - "entgo.io/ent/entc/gen" - "entgo.io/ent/entc/load" "github.com/labstack/echo/v4" "github.com/mikestefanello/backlite/ui" "github.com/mikestefanello/pagoda/ent" @@ -25,7 +23,6 @@ import ( type Admin struct { orm *ent.Client - graph *gen.Graph admin *admin.Handler backlite *ui.Handler } @@ -36,7 +33,6 @@ func init() { func (h *Admin) Init(c *services.Container) error { var err error - h.graph = c.Graph h.orm = c.ORM h.admin = admin.NewHandler(h.orm, admin.HandlerConfig{ ItemsPerPage: 25, @@ -56,22 +52,22 @@ func (h *Admin) Routes(g *echo.Group) { ag := g.Group("/admin", middleware.RequireAdmin) entities := ag.Group("/entity") - for _, n := range h.graph.Nodes { - ng := entities.Group(fmt.Sprintf("/%s", strings.ToLower(n.Name))) + for _, n := range admin.GetEntityTypes() { + ng := entities.Group(fmt.Sprintf("/%s", strings.ToLower(n.GetName()))) ng.GET("", h.EntityList(n)). - Name = routenames.AdminEntityList(n.Name) + Name = routenames.AdminEntityList(n.GetName()) ng.GET("/add", h.EntityAdd(n)). - Name = routenames.AdminEntityAdd(n.Name) + Name = routenames.AdminEntityAdd(n.GetName()) ng.POST("/add", h.EntityAddSubmit(n)). - Name = routenames.AdminEntityAddSubmit(n.Name) + Name = routenames.AdminEntityAddSubmit(n.GetName()) ng.GET("/:id/edit", h.EntityEdit(n), h.middlewareEntityLoad(n)). - Name = routenames.AdminEntityEdit(n.Name) + Name = routenames.AdminEntityEdit(n.GetName()) ng.POST("/:id/edit", h.EntityEditSubmit(n), h.middlewareEntityLoad(n)). - Name = routenames.AdminEntityEditSubmit(n.Name) + Name = routenames.AdminEntityEditSubmit(n.GetName()) ng.GET("/:id/delete", h.EntityDelete(n), h.middlewareEntityLoad(n)). - Name = routenames.AdminEntityDelete(n.Name) + Name = routenames.AdminEntityDelete(n.GetName()) ng.POST("/:id/delete", h.EntityDeleteSubmit(n), h.middlewareEntityLoad(n)). - Name = routenames.AdminEntityDeleteSubmit(n.Name) + Name = routenames.AdminEntityDeleteSubmit(n.GetName()) } tasks := ag.Group("/tasks") @@ -84,7 +80,7 @@ func (h *Admin) Routes(g *echo.Group) { } // middlewareEntityLoad is middleware to extract the entity ID and attempt to load the given entity. -func (h *Admin) middlewareEntityLoad(n *gen.Type) echo.MiddlewareFunc { +func (h *Admin) middlewareEntityLoad(n admin.EntityType) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(ctx echo.Context) error { id, err := strconv.Atoi(ctx.Param("id")) @@ -92,7 +88,7 @@ func (h *Admin) middlewareEntityLoad(n *gen.Type) echo.MiddlewareFunc { return echo.NewHTTPError(http.StatusBadRequest, "invalid entity ID") } - entity, err := h.admin.Get(ctx, n.Name, id) + entity, err := h.admin.Get(ctx, n, id) switch { case err == nil: ctx.Set(context.AdminEntityIDKey, id) @@ -107,100 +103,91 @@ func (h *Admin) middlewareEntityLoad(n *gen.Type) echo.MiddlewareFunc { } } -func (h *Admin) EntityList(n *gen.Type) echo.HandlerFunc { +func (h *Admin) EntityList(n admin.EntityType) echo.HandlerFunc { return func(ctx echo.Context) error { - list, err := h.admin.List(ctx, n.Name) + list, err := h.admin.List(ctx, n) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err) } - return pages.AdminEntityList(ctx, n.Name, list) + return pages.AdminEntityList(ctx, n, list) } } -func (h *Admin) EntityAdd(n *gen.Type) echo.HandlerFunc { +func (h *Admin) EntityAdd(n admin.EntityType) echo.HandlerFunc { return func(ctx echo.Context) error { - return pages.AdminEntityInput(ctx, h.getEntitySchema(n), nil) + return pages.AdminEntityInput(ctx, n, nil) } } -func (h *Admin) EntityAddSubmit(n *gen.Type) echo.HandlerFunc { +func (h *Admin) EntityAddSubmit(n admin.EntityType) echo.HandlerFunc { return func(ctx echo.Context) error { - err := h.admin.Create(ctx, n.Name) + err := h.admin.Create(ctx, n) if err != nil { msg.Error(ctx, err.Error()) return h.EntityAdd(n)(ctx) } - msg.Success(ctx, fmt.Sprintf("Successfully added %s.", n.Name)) + msg.Success(ctx, fmt.Sprintf("Successfully added %s.", n.GetName())) return redirect. New(ctx). - Route(routenames.AdminEntityList(n.Name)). + Route(routenames.AdminEntityList(n.GetName())). StatusCode(http.StatusFound). Go() } } -func (h *Admin) EntityEdit(n *gen.Type) echo.HandlerFunc { +func (h *Admin) EntityEdit(n admin.EntityType) echo.HandlerFunc { return func(ctx echo.Context) error { v := ctx.Get(context.AdminEntityKey).(map[string][]string) - return pages.AdminEntityInput(ctx, h.getEntitySchema(n), v) + return pages.AdminEntityInput(ctx, n, v) } } -func (h *Admin) EntityEditSubmit(n *gen.Type) echo.HandlerFunc { +func (h *Admin) EntityEditSubmit(n admin.EntityType) echo.HandlerFunc { return func(ctx echo.Context) error { id := ctx.Get(context.AdminEntityIDKey).(int) - err := h.admin.Update(ctx, n.Name, id) + err := h.admin.Update(ctx, n, id) if err != nil { msg.Error(ctx, err.Error()) return h.EntityEdit(n)(ctx) } - msg.Success(ctx, fmt.Sprintf("Updated %s.", n.Name)) + msg.Success(ctx, fmt.Sprintf("Updated %s.", n.GetName())) return redirect. New(ctx). - Route(routenames.AdminEntityList(n.Name)). + Route(routenames.AdminEntityList(n.GetName())). StatusCode(http.StatusFound). Go() } } -func (h *Admin) EntityDelete(n *gen.Type) echo.HandlerFunc { +func (h *Admin) EntityDelete(n admin.EntityType) echo.HandlerFunc { return func(ctx echo.Context) error { - return pages.AdminEntityDelete(ctx, n.Name) + return pages.AdminEntityDelete(ctx, n) } } -func (h *Admin) EntityDeleteSubmit(n *gen.Type) echo.HandlerFunc { +func (h *Admin) EntityDeleteSubmit(n admin.EntityType) echo.HandlerFunc { return func(ctx echo.Context) error { id := ctx.Get(context.AdminEntityIDKey).(int) - if err := h.admin.Delete(ctx, n.Name, id); err != nil { + if err := h.admin.Delete(ctx, n, id); err != nil { msg.Error(ctx, err.Error()) return h.EntityDelete(n)(ctx) } - msg.Success(ctx, fmt.Sprintf("Successfully deleted %s (ID %d).", n.Name, id)) + msg.Success(ctx, fmt.Sprintf("Successfully deleted %s (ID %d).", n.GetName(), id)) return redirect. New(ctx). - Route(routenames.AdminEntityList(n.Name)). + Route(routenames.AdminEntityList(n.GetName())). StatusCode(http.StatusFound). Go() } } -func (h *Admin) getEntitySchema(n *gen.Type) *load.Schema { - for _, s := range h.graph.Schemas { - if s.Name == n.Name { - return s - } - } - return nil -} - func (h *Admin) Backlite(handler func(http.ResponseWriter, *http.Request) error) echo.HandlerFunc { return func(c echo.Context) error { if id := c.Param("id"); id != "" { diff --git a/pkg/services/container.go b/pkg/services/container.go index c5f8f60..2a2ab45 100644 --- a/pkg/services/container.go +++ b/pkg/services/container.go @@ -7,14 +7,9 @@ import ( "log/slog" "math/rand" "os" - "path" - "path/filepath" - "runtime" "strings" entsql "entgo.io/ent/dialect/sql" - "entgo.io/ent/entc" - "entgo.io/ent/entc/gen" "github.com/labstack/echo/v4" _ "github.com/mattn/go-sqlite3" "github.com/mikestefanello/backlite" @@ -51,9 +46,6 @@ type Container struct { // ORM stores a client to the ORM. ORM *ent.Client - // Graph is the entity graph defined by your Ent schema. - Graph *gen.Graph - // Mail stores an email sending client. Mail *MailClient @@ -192,16 +184,6 @@ func (c *Container) initORM() { if err := c.ORM.Schema.Create(context.Background()); err != nil { panic(err) } - - // Load the graph. - _, b, _, _ := runtime.Caller(0) - d := path.Join(path.Dir(b)) - p := filepath.Join(filepath.Dir(d), "../ent/schema") - g, err := entc.LoadGraph(p, &gen.Config{}) - if err != nil { - panic(err) - } - c.Graph = g } // initAuth initializes the authentication client. diff --git a/pkg/ui/forms/admin_entity.go b/pkg/ui/forms/admin_entity.go index 5f05b78..45939eb 100644 --- a/pkg/ui/forms/admin_entity.go +++ b/pkg/ui/forms/admin_entity.go @@ -4,7 +4,6 @@ import ( "net/http" "net/url" - "entgo.io/ent/entc/load" "entgo.io/ent/schema/field" "github.com/mikestefanello/pagoda/ent/admin" "github.com/mikestefanello/pagoda/pkg/routenames" @@ -14,7 +13,7 @@ import ( . "maragu.dev/gomponents/html" ) -func AdminEntity(r *ui.Request, schema *load.Schema, values url.Values) Node { +func AdminEntity(r *ui.Request, entityType admin.EntityType, values url.Values) Node { // TODO inline validation? isNew := values == nil nodes := make(Group, 0) @@ -34,13 +33,13 @@ func AdminEntity(r *ui.Request, schema *load.Schema, values url.Values) Node { } // Attempt to add form elements for all editable entity fields. - for _, f := range schema.Fields { + for _, f := range entityType.GetSchema() { // TODO cardinality? if !isNew && f.Immutable { continue } - switch f.Info.Type { + switch f.Type { case field.TypeString: p := InputFieldParams{ Name: f.Name, @@ -93,8 +92,8 @@ func AdminEntity(r *ui.Request, schema *load.Schema, values url.Values) Node { } for _, enum := range f.Enums { options = append(options, Choice{ - Label: enum.V, - Value: enum.V, + Label: enum.Label, + Value: enum.Value, }) } nodes = append(nodes, SelectList(OptionsParams{ @@ -116,7 +115,7 @@ func AdminEntity(r *ui.Request, schema *load.Schema, values url.Values) Node { FormButton(ColorPrimary, "Submit"), ButtonLink( ColorNone, - r.Path(routenames.AdminEntityList(schema.Name)), + r.Path(routenames.AdminEntityList(entityType.GetName())), "Cancel", ), ), diff --git a/pkg/ui/forms/admin_entity_delete.go b/pkg/ui/forms/admin_entity_delete.go index 82f9b16..36ad49f 100644 --- a/pkg/ui/forms/admin_entity_delete.go +++ b/pkg/ui/forms/admin_entity_delete.go @@ -3,6 +3,7 @@ package forms import ( "net/http" + "github.com/mikestefanello/pagoda/ent/admin" "github.com/mikestefanello/pagoda/pkg/routenames" "github.com/mikestefanello/pagoda/pkg/ui" . "github.com/mikestefanello/pagoda/pkg/ui/components" @@ -10,17 +11,17 @@ import ( . "maragu.dev/gomponents/html" ) -func AdminEntityDelete(r *ui.Request, entityTypeName string) Node { +func AdminEntityDelete(r *ui.Request, entityType admin.EntityType) Node { return Form( Method(http.MethodPost), P( - Textf("Are you sure you want to delete this %s?", entityTypeName), + Textf("Are you sure you want to delete this %s?", entityType.GetName()), ), ControlGroup( FormButton(ColorError, "Delete"), ButtonLink( ColorNone, - r.Path(routenames.AdminEntityList(entityTypeName)), + r.Path(routenames.AdminEntityList(entityType.GetName())), "Cancel", ), ), diff --git a/pkg/ui/layouts/primary.go b/pkg/ui/layouts/primary.go index 30de8c3..21f6e46 100644 --- a/pkg/ui/layouts/primary.go +++ b/pkg/ui/layouts/primary.go @@ -121,10 +121,12 @@ func sidebarMenu(r *ui.Request) Node { } adminSubMenu := func() Node { - entityTypeNames := admin.GetEntityTypeNames() - entityTypeLinks := make(Group, len(entityTypeNames)) - for _, n := range entityTypeNames { - entityTypeLinks = append(entityTypeLinks, MenuLink(r, icons.PencilSquare(), n, routenames.AdminEntityList(n))) + entityTypeLinks := make(Group, len(admin.GetEntityTypes())) + for _, n := range admin.GetEntityTypes() { + entityTypeLinks = append( + entityTypeLinks, + MenuLink(r, icons.PencilSquare(), n.GetName(), routenames.AdminEntityList(n.GetName())), + ) } return Group{ diff --git a/pkg/ui/pages/admin_entity.go b/pkg/ui/pages/admin_entity.go index 2cd61ab..adefed2 100644 --- a/pkg/ui/pages/admin_entity.go +++ b/pkg/ui/pages/admin_entity.go @@ -4,7 +4,6 @@ import ( "fmt" "net/url" - "entgo.io/ent/entc/load" "github.com/labstack/echo/v4" "github.com/mikestefanello/pagoda/ent/admin" "github.com/mikestefanello/pagoda/pkg/routenames" @@ -16,37 +15,37 @@ import ( . "maragu.dev/gomponents/html" ) -func AdminEntityDelete(ctx echo.Context, entityTypeName string) error { +func AdminEntityDelete(ctx echo.Context, entityType admin.EntityType) error { r := ui.NewRequest(ctx) - r.Title = fmt.Sprintf("Delete %s", entityTypeName) + r.Title = fmt.Sprintf("Delete %s", entityType.GetName()) return r.Render( layouts.Primary, - forms.AdminEntityDelete(r, entityTypeName), + forms.AdminEntityDelete(r, entityType), ) } -func AdminEntityInput(ctx echo.Context, schema *load.Schema, values url.Values) error { +func AdminEntityInput(ctx echo.Context, entityType admin.EntityType, values url.Values) error { r := ui.NewRequest(ctx) if values == nil { - r.Title = fmt.Sprintf("Add %s", schema.Name) + r.Title = fmt.Sprintf("Add %s", entityType.GetName()) } else { - r.Title = fmt.Sprintf("Edit %s", schema.Name) + r.Title = fmt.Sprintf("Edit %s", entityType.GetName()) } return r.Render( layouts.Primary, - forms.AdminEntity(r, schema, values), + forms.AdminEntity(r, entityType, values), ) } func AdminEntityList( ctx echo.Context, - entityTypeName string, + entityType admin.EntityType, entityList *admin.EntityList, ) error { r := ui.NewRequest(ctx) - r.Title = entityTypeName + r.Title = entityType.GetName() genHeader := func() Node { g := make(Group, 0, len(entityList.Columns)+2) @@ -68,13 +67,13 @@ func AdminEntityList( Td( ButtonLink( ColorInfo, - r.Path(routenames.AdminEntityEdit(entityTypeName), row.ID), + r.Path(routenames.AdminEntityEdit(entityType.GetName()), row.ID), "Edit", ), Span(Class("mr-2")), ButtonLink( ColorError, - r.Path(routenames.AdminEntityDelete(entityTypeName), row.ID), + r.Path(routenames.AdminEntityDelete(entityType.GetName()), row.ID), "Delete", ), ), @@ -95,8 +94,8 @@ func AdminEntityList( Class("form-control mb-2"), ButtonLink( ColorAccent, - r.Path(routenames.AdminEntityAdd(entityTypeName)), - fmt.Sprintf("Add %s", entityTypeName), + r.Path(routenames.AdminEntityAdd(entityType.GetName())), + fmt.Sprintf("Add %s", entityType.GetName()), ), ), Table( @@ -108,7 +107,7 @@ func AdminEntityList( ), Pager( entityList.Page, - r.Path(routenames.AdminEntityAdd(entityTypeName)), + r.Path(routenames.AdminEntityAdd(entityType.GetName())), entityList.HasNextPage, "", ),