This commit is contained in:
Ersteller 1970-01-01 00:00:00 +00:00
parent 9b5be193db
commit 6dfd8b29e7
12 changed files with 75 additions and 22 deletions

View File

@ -13,8 +13,6 @@ RUN bash scripts/db-push.sh
RUN go build -o /main main.go RUN go build -o /main main.go
RUN go build -o /create-user cli/create-user_gen.go
FROM alpine FROM alpine
ARG USER=default ARG USER=default
@ -31,7 +29,6 @@ USER $USER
WORKDIR $HOME WORKDIR $HOME
COPY --from=build --chown=default:default /main ./main COPY --from=build --chown=default:default /main ./main
COPY --from=build --chown=default:default /create-user ./create-user
COPY --from=build --chown=default:default /app/.env .env COPY --from=build --chown=default:default /app/.env .env
COPY --chown=default:default views/ ./views COPY --chown=default:default views/ ./views
COPY --chown=default:default static/ ./static/ COPY --chown=default:default static/ ./static/

14
crud/tracing.go Normal file
View File

@ -0,0 +1,14 @@
package crud
import (
"encoding/json"
"fmt"
)
func Trace(object any) string {
marshalled, err := json.Marshal(object)
if err != nil {
return fmt.Sprint(object)
}
return string(marshalled)
}

7
crud/util.go Normal file
View File

@ -0,0 +1,7 @@
package crud
import "strings"
func FirstLetterToUpper(name string) string {
return strings.ToUpper(name[:1]) + name[1:]
}

View File

@ -26,7 +26,7 @@ type PersonStruct struct {
func AddTablePrototype(e *Echo) { func AddTablePrototype(e *Echo) {
items := getPrototypeStructs() items := getPrototypeStructs()
e.GET(tablePrototypePath, func(c Context) error { e.GET(tablePrototypePath, func(c Context) error {
table := Table{ table := GohtmlTable{
Headers: []string{"Name", "Age", "Profession", "Member Since"}, Headers: []string{"Name", "Age", "Profession", "Member Since"},
Rows: prototypeStructsToTableRows(items), Rows: prototypeStructsToTableRows(items),
EntityUrl: tablePrototypePath, EntityUrl: tablePrototypePath,
@ -120,7 +120,7 @@ func ParseDateTime(since string) time.Time {
} }
func returnRenderTable(c Context, items []PersonStruct) error { func returnRenderTable(c Context, items []PersonStruct) error {
table := Table{ table := GohtmlTable{
Headers: []string{"Name", "Age", "Profession", "Member Since"}, Headers: []string{"Name", "Age", "Profession", "Member Since"},
Rows: prototypeStructsToTableRows(items), Rows: prototypeStructsToTableRows(items),
EntityUrl: tablePrototypePath, EntityUrl: tablePrototypePath,

View File

@ -31,7 +31,7 @@ type Pagination struct {
NextPage int NextPage int
} }
type Table struct { type GohtmlTable struct {
Headers []string Headers []string
Rows []TableRow Rows []TableRow
EntityUrl string EntityUrl string

View File

@ -25,6 +25,7 @@ model user {
model todo { model todo {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
name String @default("") name String @default("")
completed Boolean @default(false)
user_id Int @default(0) user_id Int @default(0)
created_at DateTime @default(now()) created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt updated_at DateTime @default(now()) @updatedAt

View File

@ -1,2 +1,2 @@
#!/bin/bash #!/bin/bash
go run github.com/steebchen/prisma-client-go db push go run github.com/steebchen/prisma-client-go db push --skip-generate

View File

@ -37,6 +37,7 @@ func (r *TodoRepository) Create(todo Todo) (int, error) {
"updated_at": time.Now(), "updated_at": time.Now(),
"name": todo.Name, "name": todo.Name,
"completed": todo.Completed,
"user_id": todo.UserId, "user_id": todo.UserId,
}). }).
Returning("id"). Returning("id").
@ -78,7 +79,7 @@ func (e TodoAlreadyExistsError) Error() string {
func (r *TodoRepository) getSelectColumns() []any { func (r *TodoRepository) getSelectColumns() []any {
return []any{"id", "created_at", "updated_at", return []any{"id", "created_at", "updated_at",
"name", "user_id", "name", "completed", "user_id",
} }
} }
@ -121,6 +122,7 @@ func (r *TodoRepository) rowToItem(rows pgx.Rows, rowId bool) (Todo, int, error)
&item.CreatedAt, &item.CreatedAt,
&item.UpdatedAt, &item.UpdatedAt,
&item.Name, &item.Name,
&item.Completed,
&item.UserId, &item.UserId,
) )
if err != nil { if err != nil {
@ -132,6 +134,7 @@ func (r *TodoRepository) rowToItem(rows pgx.Rows, rowId bool) (Todo, int, error)
&item.CreatedAt, &item.CreatedAt,
&item.UpdatedAt, &item.UpdatedAt,
&item.Name, &item.Name,
&item.Completed,
&item.UserId, &item.UserId,
) )
if err != nil { if err != nil {
@ -143,6 +146,7 @@ func (r *TodoRepository) rowToItem(rows pgx.Rows, rowId bool) (Todo, int, error)
CreatedAt: item.CreatedAt, CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt, UpdatedAt: item.UpdatedAt,
Name: item.Name, Name: item.Name,
Completed: item.Completed,
UserId: item.UserId, UserId: item.UserId,
}, item.Count, nil }, item.Count, nil
} }
@ -154,6 +158,7 @@ func (r *TodoRepository) Update(userId int, todo Todo) error {
"updated_at": time.Now(), "updated_at": time.Now(),
"name": todo.Name, "name": todo.Name,
"completed": todo.Completed,
"user_id": todo.UserId, "user_id": todo.UserId,
}). }).
Where(goqu.Ex{ Where(goqu.Ex{
@ -201,6 +206,7 @@ type TodoField string
const ( const (
TodoFieldName TodoField = "name" TodoFieldName TodoField = "name"
TodoFieldCompleted TodoField = "completed"
) )
type TodoNameFilter struct { type TodoNameFilter struct {
@ -208,6 +214,11 @@ type TodoNameFilter struct {
Value string Value string
} }
type TodoCompletedFilter struct {
Active bool
Value bool
}
type TodoOrderDirection string type TodoOrderDirection string
const ( const (
@ -227,6 +238,8 @@ type TodoPaginationParams struct {
NameFilter TodoNameFilter NameFilter TodoNameFilter
CompletedFilter TodoCompletedFilter
References TodoReferences References TodoReferences
} }
@ -296,6 +309,12 @@ func (r *TodoRepository) addPageFilters(params TodoPaginationParams, whereExpres
}) })
} }
if params.CompletedFilter.Active {
whereExpressions = append(whereExpressions, goqu.Ex{
"completed": params.CompletedFilter.Value,
})
}
return whereExpressions return whereExpressions
} }
@ -306,7 +325,3 @@ func (r *TodoRepository) jsonToString(jsonData any) string {
} }
return string(bytes) return string(bytes)
} }
func (r *TodoRepository) FirstLetterToUpper(name string) string {
return strings.ToUpper(name[:1]) + name[1:]
}

View File

@ -57,6 +57,8 @@ func (i *TodoCrud) GetItem(c Context) error {
Columns: []ItemDisplayColumn{ Columns: []ItemDisplayColumn{
{Label: "Name", Value: item.Name, Type: ItemDisplayTypeText}, {Label: "Name", Value: item.Name, Type: ItemDisplayTypeText},
{Label: "Completed", Value: fmt.Sprint(item.Completed), Type: ItemDisplayTypeText},
}, },
SubItems: i.getSubItemDisplays(item, c), SubItems: i.getSubItemDisplays(item, c),
EditItemUrl: fmt.Sprint(i.GetEntityUrl(c), "/", item.Id, "/edit", queryString), EditItemUrl: fmt.Sprint(i.GetEntityUrl(c), "/", item.Id, "/edit", queryString),
@ -80,7 +82,7 @@ func (i *TodoCrud) getSubItemDisplays(item Todo, c Context) []ItemDisplaySubItem
type TodoDisplay struct { type TodoDisplay struct {
IsTable bool IsTable bool
Table Table Table GohtmlTable
IsDisplay bool IsDisplay bool
ItemDisplay ItemDisplay ItemDisplay ItemDisplay
IsEdit bool IsEdit bool
@ -115,6 +117,8 @@ func (i *TodoCrud) CreateNewItemInputs(c Context) error {
inputs := []EditItemInputs{ inputs := []EditItemInputs{
{Label: "Name", Value: "", Name: "Name", Type: InputTypeText, Options: []SelectInputOption{}}, {Label: "Name", Value: "", Name: "Name", Type: InputTypeText, Options: []SelectInputOption{}},
{Label: "Completed", Value: "", Name: "Completed", Type: InputTypeBool, Options: []SelectInputOption{}},
} }
url := fmt.Sprint(i.GetEntityUrl(c), "?", c.QueryString()) url := fmt.Sprint(i.GetEntityUrl(c), "?", c.QueryString())
@ -140,6 +144,8 @@ func (i *TodoCrud) CreateItem(c Context) error {
UserId: userId, UserId: userId,
Name: c.FormValue("Name"), Name: c.FormValue("Name"),
Completed: ParseCheckboxWithDefault(c.FormValue("Completed"), false),
} }
_, err := i.repo.Create(item) _, err := i.repo.Create(item)
if err != nil { if err != nil {
@ -158,6 +164,8 @@ func (i *TodoCrud) EditItem(c Context) error {
inputs := []EditItemInputs{ inputs := []EditItemInputs{
{Label: "Name", Value: item.Name, Name: "Name", Type: InputTypeText, Options: []SelectInputOption{}}, {Label: "Name", Value: item.Name, Name: "Name", Type: InputTypeText, Options: []SelectInputOption{}},
{Label: "Completed", Value: fmt.Sprint(item.Completed), Name: "Completed", Type: InputTypeBool, Options: []SelectInputOption{}},
} }
path := fmt.Sprint(i.GetEntityUrl(c), "/", item.Id) path := fmt.Sprint(i.GetEntityUrl(c), "/", item.Id)
queryString := GetCurrentUrlQueryParams(c) queryString := GetCurrentUrlQueryParams(c)
@ -193,6 +201,8 @@ func (i *TodoCrud) UpdateItem(c Context) error {
item.Name = c.FormValue("Name") item.Name = c.FormValue("Name")
item.Completed = ParseCheckboxWithDefault(c.FormValue("Completed"), false)
err = i.repo.Update(userId, item) err = i.repo.Update(userId, item)
if err != nil { if err != nil {
return err return err
@ -236,7 +246,7 @@ func (i *TodoCrud) returnRenderTable(c Context, items []Todo, count int) error {
return i.html.RenderComponent(c, "table", table) return i.html.RenderComponent(c, "table", table)
} }
func (i *TodoCrud) itemsToTable(c Context, items []Todo, count int) Table { func (i *TodoCrud) itemsToTable(c Context, items []Todo, count int) GohtmlTable {
filter := c.FormValue("filter") filter := c.FormValue("filter")
page := ParseIntWithDefault(c.FormValue("pageNumber"), 1) page := ParseIntWithDefault(c.FormValue("pageNumber"), 1)
index := (page - 1) * 5 index := (page - 1) * 5
@ -244,10 +254,12 @@ func (i *TodoCrud) itemsToTable(c Context, items []Todo, count int) Table {
if itemEnd > count { if itemEnd > count {
itemEnd = count itemEnd = count
} }
return Table{ return GohtmlTable{
Headers: []string{ Headers: []string{
"Name", "Name",
"Completed",
}, },
Rows: i.structsToTableRows(c, items), Rows: i.structsToTableRows(c, items),
EntityUrl: i.GetEntityUrl(c), EntityUrl: i.GetEntityUrl(c),
@ -262,6 +274,8 @@ func (i *TodoCrud) itemsToTable(c Context, items []Todo, count int) Table {
Options: []SelectInputOption{ Options: []SelectInputOption{
{Label: "Name filter", Value: "Name", Selected: filter == "Name"}, {Label: "Name filter", Value: "Name", Selected: filter == "Name"},
{Label: "Completed filter", Value: "Completed", Selected: filter == "Completed"},
}, },
}, },
Pagination: Pagination{ Pagination: Pagination{
@ -293,6 +307,8 @@ func (i *TodoCrud) structToRow(c Context, item Todo) TableRow {
Columns: []TableColumn{ Columns: []TableColumn{
{Value: item.Name, Type: TableColumnTypeText}, {Value: item.Name, Type: TableColumnTypeText},
{Value: fmt.Sprint(item.Completed), Type: TableColumnTypeText},
}, },
EntityUrl: i.GetEntityUrl(c), EntityUrl: i.GetEntityUrl(c),
EditItemUrl: fmt.Sprint(i.GetEntityUrl(c), "/", item.Id, "/edit"), EditItemUrl: fmt.Sprint(i.GetEntityUrl(c), "/", item.Id, "/edit"),
@ -324,6 +340,11 @@ func (i *TodoCrud) getPage(c Context) ([]Todo, int, error) {
Value: filterValue, Value: filterValue,
}, },
CompletedFilter: TodoCompletedFilter{
Active: filter == "Completed",
Value: ParseBoolWithDefault(filterValue, false),
},
References: TodoReferences{ References: TodoReferences{
UserId: userId, UserId: userId,
}, },

View File

@ -11,15 +11,17 @@ import (
type Todo struct { type Todo struct {
Id int `db:"id"` Id int `db:"id"`
Name string `db:"name"` Name string `db:"name"`
Completed bool `db:"completed"`
UserId int `db:"user_id"` UserId int `db:"user_id"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"` UpdatedAt time.Time `db:"updated_at"`
} }
func (s *Todo) String() string { func (s Todo) String() string {
return fmt.Sprint("Todo{ ", return fmt.Sprint("Todo{ ",
"Id: ", s.Id, ", ", "Id: ", s.Id, ", ",
"Name: ", s.Name, ", ", "Name: ", s.Name, ", ",
"Completed: ", s.Completed, ", ",
"UserId: ", s.UserId, ", ", "UserId: ", s.UserId, ", ",
"CreatedAt: ", s.CreatedAt, ", ", "CreatedAt: ", s.CreatedAt, ", ",
"UpdatedAt: ", s.UpdatedAt, ", ", "UpdatedAt: ", s.UpdatedAt, ", ",

View File

@ -309,10 +309,6 @@ func (r *UserRepository) jsonToString(jsonData any) string {
return string(bytes) return string(bytes)
} }
func (r *UserRepository) FirstLetterToUpper(name string) string {
return strings.ToUpper(name[:1]) + name[1:]
}
func (u *UserRepository) DoesUserEmailExist(email string) (bool, error) { func (u *UserRepository) DoesUserEmailExist(email string) (bool, error) {
sql, args, _ := u.dialect.From("user"). sql, args, _ := u.dialect.From("user").
Prepared(true). Prepared(true).

View File

@ -16,7 +16,7 @@ type User struct {
UpdatedAt time.Time `db:"updated_at"` UpdatedAt time.Time `db:"updated_at"`
} }
func (s *User) String() string { func (s User) String() string {
return fmt.Sprint("User{ ", return fmt.Sprint("User{ ",
"Id: ", s.Id, ", ", "Id: ", s.Id, ", ",
"Email: ", s.Email, ", ", "Email: ", s.Email, ", ",