8c6250f0/todo/todo-rest-crud_gen.go
2025-02-20 16:48:07 +01:00

401 lines
9.8 KiB
Go

package todo
import (
"errors"
"fmt"
"github.com/google/uuid"
. "github.com/labstack/echo/v4"
"todo/crud"
. "todo/html_components"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
const TodoPath = "/todo"
type TodoCrud struct {
e *Echo
repo *TodoRepository
html *GoHtmlHandler
}
func NewTodoCrud(e *Echo, repo *TodoRepository, html *GoHtmlHandler) *TodoCrud {
return &TodoCrud{e: e, repo: repo, html: html}
}
func (i *TodoCrud) AddRoutes() {
crud.Debug("Adding Todo crud routes")
g := i.e.Group(TodoPath)
g.GET("", i.GetItems)
g.GET("/:id", i.GetItem)
g.GET("/create", i.CreateNewItemInputs)
g.POST("", i.CreateItem)
g.GET("/:id/edit", i.EditItem)
g.PUT("/:id", i.UpdateItem)
g.DELETE("/:id", i.DeleteItem)
}
func (i *TodoCrud) readItem(c Context) (Todo, error) {
userId := c.Get("userId").(int)
id := ParseIntWithDefault(c.Param("id"), 1)
return i.repo.Read(userId, id)
}
func (i *TodoCrud) GetItem(c Context) error {
item, err := i.readItem(c)
if err != nil {
return err
}
queryString := GetCurrentUrlQueryParams(c)
itemDisplay := ItemDisplay{
Columns: []ItemDisplayColumn{
{Label: "Name", Value: item.Name, Type: ItemDisplayTypeText},
{Label: "Completed", Value: fmt.Sprint(item.Completed), Type: ItemDisplayTypeText},
},
SubItems: i.getSubItemDisplays(item, c),
EditItemUrl: fmt.Sprint(i.GetEntityUrl(c), "/", item.Id, "/edit", queryString),
DeleteItemUrl: fmt.Sprint(i.GetEntityUrl(c), "/", item.Id, queryString),
DeletePushUrl: fmt.Sprint(i.GetEntityUrl(c), queryString),
HasImages: false,
BackUrl: fmt.Sprint(i.GetEntityUrl(c), queryString),
ItemId: item.Id,
}
return i.html.RenderPage(c, "todo", TodoDisplay{
IsDisplay: true,
ItemDisplay: itemDisplay,
})
}
func (i *TodoCrud) getSubItemDisplays(item Todo, c Context) []ItemDisplaySubItem {
var items []ItemDisplaySubItem
return items
}
type TodoDisplay struct {
IsTable bool
Table GohtmlTable
IsDisplay bool
ItemDisplay ItemDisplay
IsEdit bool
EditItem EditItem
}
func (i *TodoCrud) GetItems(c Context) error {
page, count, err := i.getPage(c)
if err != nil {
return err
}
table := i.itemsToTable(c, page, count)
return i.html.RenderPage(c, "todo", TodoDisplay{
IsTable: true,
Table: table,
})
}
func (i *TodoCrud) GetEntityUrl(c Context) string {
return TodoPath
}
func (i *TodoCrud) GetParentEntityUrl(c Context) string {
return ""
}
func (i *TodoCrud) CreateNewItemInputs(c Context) error {
inputs := []EditItemInputs{
{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())
s := EditItem{
Id: "",
Title: "Create todo",
Url: url,
CancelUrl: url,
IsCreate: true,
SubmitButtonLabel: "Create",
Inputs: inputs,
HasFileUpload: false,
}
return i.html.RenderPage(c, "todo", TodoDisplay{
IsEdit: true,
EditItem: s,
})
}
func (i *TodoCrud) CreateItem(c Context) error {
userId := c.Get("userId").(int)
item := Todo{
UserId: userId,
Name: c.FormValue("Name"),
Completed: ParseCheckboxWithDefault(c.FormValue("Completed"), false),
}
_, err := i.repo.Create(item)
if err != nil {
return err
}
return i.GetItems(c)
}
func (i *TodoCrud) EditItem(c Context) error {
userId := c.Get("userId").(int)
id := ParseIntWithDefault(c.Param("id"), 1)
item, err := i.repo.Read(userId, id)
if err != nil {
return err
}
inputs := []EditItemInputs{
{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)
queryString := GetCurrentUrlQueryParams(c)
url := fmt.Sprint(path, queryString)
cancelUrl := url
if HasFromTableHeader(c) {
cancelUrl = fmt.Sprint(i.GetEntityUrl(c), queryString)
}
s := EditItem{
Id: fmt.Sprint(id),
Title: fmt.Sprint("Update todo ", id),
Url: url,
CancelUrl: cancelUrl,
IsCreate: false,
SubmitButtonLabel: "Update",
Inputs: inputs,
HasFileUpload: false,
FromTable: HasFromTableHeader(c),
}
return i.html.RenderPage(c, "todo", TodoDisplay{
IsEdit: true,
EditItem: s,
})
}
func (i *TodoCrud) UpdateItem(c Context) error {
userId := c.Get("userId").(int)
id := ParseIntWithDefault(c.Param("id"), 1)
item, err := i.repo.Read(userId, id)
if err != nil {
return err
}
item.Name = c.FormValue("Name")
item.Completed = ParseCheckboxWithDefault(c.FormValue("Completed"), false)
err = i.repo.Update(userId, item)
if err != nil {
return err
}
if HasFromTableHeader(c) {
return i.GetItems(c)
}
return i.GetItem(c)
}
func (i *TodoCrud) DeleteItem(c Context) error {
userId := c.Get("userId").(int)
id := ParseIntWithDefault(c.Param("id"), 1)
err := i.repo.Delete(userId, id)
if err != nil {
return err
}
return i.GetItems(c)
}
func (i *TodoCrud) parseDateTime(since string) time.Time {
t, err := time.Parse("2006-01-02T15:04", since)
if err != nil {
return time.Now()
}
return t
}
func (i *TodoCrud) renderPage(c Context, repo *TodoRepository) error {
page, count, err := i.getPage(c)
if err != nil {
return err
}
table := i.itemsToTable(c, page, count)
return i.html.RenderPage(c, "todo", table)
}
func (i *TodoCrud) returnRenderTable(c Context, items []Todo, count int) error {
table := i.itemsToTable(c, items, count)
return i.html.RenderComponent(c, "table", table)
}
func (i *TodoCrud) itemsToTable(c Context, items []Todo, count int) GohtmlTable {
filter := c.FormValue("filter")
page := ParseIntWithDefault(c.FormValue("pageNumber"), 1)
index := (page - 1) * 5
itemEnd := index + 5
if itemEnd > count {
itemEnd = count
}
return GohtmlTable{
Headers: []string{
"Name",
"Completed",
},
Rows: i.structsToTableRows(c, items),
EntityUrl: i.GetEntityUrl(c),
CreateItemUrl: fmt.Sprint(i.GetEntityUrl(c), "/create?", c.QueryString()),
OrderBy: string(i.getOrderBy(c)),
OrderDirection: string(i.getOrderDirection(c)),
FilterValue: c.FormValue("filterValue"),
FilterSelect: SelectInput{
Label: "Filter by",
Name: "filter",
HideLabel: true,
Options: []SelectInputOption{
{Label: "Name filter", Value: "Name", Selected: filter == "Name"},
{Label: "Completed filter", Value: "Completed", Selected: filter == "Completed"},
},
},
Pagination: Pagination{
CurrenItemStart: index + 1,
CurrentItemEnd: index + 10,
TotalNumberOfItems: count,
PreviousDisabled: index == 0,
NextDisabled: index+10 >= count,
Page: page,
PreviousPage: page - 1,
NextPage: page + 1,
},
ShowBack: false,
BackUrl: i.GetParentEntityUrl(c),
}
}
func (i *TodoCrud) structsToTableRows(c Context, items []Todo) []TableRow {
var rows []TableRow
for _, item := range items {
rows = append(rows, i.structToRow(c, item))
}
return rows
}
func (i *TodoCrud) structToRow(c Context, item Todo) TableRow {
return TableRow{
Id: fmt.Sprint(item.Id),
Columns: []TableColumn{
{Value: item.Name, Type: TableColumnTypeText},
{Value: fmt.Sprint(item.Completed), Type: TableColumnTypeText},
},
EntityUrl: i.GetEntityUrl(c),
EditItemUrl: fmt.Sprint(i.GetEntityUrl(c), "/", item.Id, "/edit"),
DeleteItemUrl: fmt.Sprint(i.GetEntityUrl(c), "/", item.Id),
}
}
func (i *TodoCrud) formatDateRangeInputTimeStamp(time time.Time) string {
return time.Format("2006-01-02T15:04")
}
func (i *TodoCrud) dateDisplay(time time.Time) string {
return time.Format("2006-01-02 15:04:05")
}
func (i *TodoCrud) getPage(c Context) ([]Todo, int, error) {
userId := c.Get("userId").(int)
filter := c.FormValue("filter")
filterValue := c.FormValue("filterValue")
page := ParseIntWithDefault(c.FormValue("pageNumber"), 1)
return i.repo.GetPage(TodoPaginationParams{
RowId: (page - 1) * 10,
PageSize: 10,
OrderBy: i.getOrderBy(c),
OrderDirection: i.getOrderDirection(c),
NameFilter: TodoNameFilter{
Active: filter == "Name",
Value: filterValue,
},
CompletedFilter: TodoCompletedFilter{
Active: filter == "Completed",
Value: ParseBoolWithDefault(filterValue, false),
},
References: TodoReferences{
UserId: userId,
},
})
}
func (i *TodoCrud) getOrderBy(c Context) TodoField {
orderBy := c.QueryParam("orderBy")
if orderBy == "" {
return TodoFieldName
}
return TodoField(strings.ToLower(orderBy))
}
func (i *TodoCrud) getOrderDirection(c Context) TodoOrderDirection {
orderDirection := c.QueryParam("orderDirection")
if orderDirection == "" {
return TodoOrderDirectionAsc
}
return TodoOrderDirection(orderDirection)
}
func (i *TodoCrud) saveFileAndReturnFileName(c Context, name string, currentFilename string) string {
file, err := c.FormFile(name)
if errors.Is(err, http.ErrMissingFile) {
return currentFilename
}
if err != nil {
crud.Error("Failed to save file from input", name, "with error", err)
return currentFilename
}
src, err := file.Open()
if err != nil {
crud.Error("Failed to save file from input", name, "with error", err)
return currentFilename
}
defer src.Close()
err = os.MkdirAll("files", os.ModePerm)
if err != nil {
crud.Error("Failed to save file from input", name, "with error", err)
return currentFilename
}
extension := filepath.Ext(file.Filename)
filename := fmt.Sprint(uuid.New().String(), extension)
err = CreateFile("files/"+filename, src)
if err != nil {
crud.Error("Failed to save file from input", name, "with error", err)
return currentFilename
}
return filename
}