[Add form generation]

This commit is contained in:
xoy 2025-04-11 17:49:54 +02:00
parent b33dc14db9
commit 215cfcb7ea
6 changed files with 513 additions and 6 deletions

88
list.go Normal file
View file

@ -0,0 +1,88 @@
package main
import "fmt"
type List[T any] struct {
bufferSize uint
bufferStart uint
index uint
values []*T
}
func NewList[T any](bufferSize uint, values ...*T) (*List[T], error) {
if bufferSize < 1 {
return nil, fmt.Errorf("parameter bufferSize must be greater than 0")
}
list := new(List[T])
list.bufferSize = bufferSize
if len(values) > 0 {
list.values = values
list.bufferStart = uint(len(values))
list.values = append(list.values, make([]*T, bufferSize)...)
} else {
list.bufferStart = 0
list.values = make([]*T, bufferSize)
}
list.index = 0
return list, nil
}
func (l *List[T]) Add(value *T) {
if l.bufferStart < uint(len(l.values)) {
bufferSize := uint(len(l.values)) - (l.bufferStart - 1)
if 1 >= bufferSize {
l.values = append(l.values, make([]*T, 1-bufferSize+l.bufferSize)...)
}
}
l.values[l.bufferStart] = value
l.bufferStart++
}
func (l *List[T]) Get(index uint) (*T, error) {
if index >= l.bufferStart {
return nil, fmt.Errorf("parameter index (%v) must be less than bufferStart (%v)", index, l.bufferStart)
}
return l.values[index], nil
}
func (l *List[T]) Set(index uint, value *T) error {
if index >= l.bufferStart {
return fmt.Errorf("parameter index (%v) must be less than bufferStart (%v)", index, l.bufferStart)
}
l.values[index] = value
return nil
}
func (l *List[T]) Index() uint {
if l.index >= l.bufferStart {
l.index = 0
}
return l.index
}
func (l *List[T]) Next() (out *T) {
out = l.values[l.Index()]
l.index++
return out
}
func (l *List[T]) Remove(index uint) error {
if index >= l.bufferStart {
return fmt.Errorf("parameter index (%v) must be less than bufferStart (%v)", index, l.bufferStart)
}
for i := index; i < l.bufferStart; i++ {
next := l.values[index+1]
err := l.Set(i, next)
if err != nil {
return err
}
}
return nil
}
func (l *List[T]) ToArray() []*T {
return l.values[0:l.bufferStart]
}

97
main.go
View file

@ -3,6 +3,7 @@ package main
import (
"log"
"math/rand/v2"
"strconv"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/template/html/v2"
@ -85,6 +86,13 @@ func main() {
"Behälter",
})
resultCount = len(table.Rows)
table, err = table.FilterColumns(1, 3, 4)
if err != nil {
notification = "Interner fehler: reihe/n nicht gefunden!"
resultCount = -1
}
}
return c.Render("search", fiber.Map{
@ -143,6 +151,37 @@ func main() {
})
})
app.Post("/admin/locations/edit", func(c *fiber.Ctx) error {
figch := rand.IntN(100) <= figchProbability
id, err := strconv.ParseInt(c.FormValue("id", "."), 10, 64)
if err != nil {
return err
}
location, err := conn.QueryLocation(id)
if err != nil {
return err
}
f, err := location.ToForm()
if err != nil {
return err
}
return c.Render("admin/edit", fiber.Map{
"Figch": figch,
"Title": "Verwaltung",
"Stylenames": NewStyleItemList("colors", "main", "overview"),
"NavItems": navItems,
"ActivePage": "/admin",
"Table": f.Table,
"FormElements": f.Elements,
"RowId": f.RowId,
})
})
app.Get("/admin/containers/overview", func(c *fiber.Ctx) error {
figch := rand.IntN(100) <= figchProbability
@ -161,12 +200,39 @@ func main() {
"Stylenames": NewStyleItemList("colors", "main", "overview"),
"NavItems": navItems,
"ActivePage": "/admin",
"Table": "locations",
"Table": "containers",
"Columns": table.Collumns,
"Rows": table.Rows,
})
})
app.Post("/admin/containers/edit", func(c *fiber.Ctx) error {
figch := rand.IntN(100) <= figchProbability
id, err := strconv.ParseInt(c.FormValue("id", "."), 10, 64)
if err != nil {
return err
}
container, err := conn.QueryContainer(id)
if err != nil {
return err
}
f := container.ToForm()
return c.Render("admin/edit", fiber.Map{
"Figch": figch,
"Title": "Verwaltung",
"Stylenames": NewStyleItemList("colors", "main", "overview"),
"NavItems": navItems,
"ActivePage": "/admin",
"Table": f.Table,
"FormElements": f.Elements,
"RowId": f.RowId,
})
})
app.Get("/admin/parts/overview", func(c *fiber.Ctx) error {
figch := rand.IntN(100) <= figchProbability
@ -188,11 +254,38 @@ func main() {
"Stylenames": NewStyleItemList("colors", "main", "overview"),
"NavItems": navItems,
"ActivePage": "/admin",
"Table": "locations",
"Table": "parts",
"Columns": table.Collumns,
"Rows": table.Rows,
})
})
app.Post("/admin/parts/edit", func(c *fiber.Ctx) error {
figch := rand.IntN(100) <= figchProbability
id, err := strconv.ParseInt(c.FormValue("id", "."), 10, 64)
if err != nil {
return err
}
part, err := conn.QueryPart(id)
if err != nil {
return err
}
f := part.ToForm()
return c.Render("admin/edit", fiber.Map{
"Figch": figch,
"Title": "Verwaltung",
"Stylenames": NewStyleItemList("colors", "main", "overview"),
"NavItems": navItems,
"ActivePage": "/admin",
"Table": f.Table,
"FormElements": f.Elements,
"RowId": f.RowId,
})
})
log.Fatal(app.Listen(":3000"))
}

49
misc.go
View file

@ -1,6 +1,7 @@
package main
import (
"fmt"
"unicode"
)
@ -37,3 +38,51 @@ func IsEmpty(s string) bool {
}
return true
}
func LocationsToOption(locations []*Location, selected *Location) ([]*Option, error) {
options, err := NewList[Option](uint(len(locations)))
if err != nil {
return nil, err
}
for _, l := range locations {
fullPath, err := l.FullPath()
if err != nil {
fmt.Println(err)
continue
}
if l.Id.Valid && l.Id.Int64 != selected.Id.Int64 {
option := Option{
Caption: fullPath,
Value: fmt.Sprintf("%v", int(l.Id.Int64)),
Selected: selected.Parent.Valid && int(l.Id.Int64) == int(selected.Parent.Int64),
}
options.Add(&option)
}
}
return options.ToArray(), nil
}
func ContainersToOption(containers []*Container, selected *Container) ([]*Option, error) {
options, err := NewList[Option](uint(len(containers)))
if err != nil {
return nil, err
}
for _, c := range containers {
if c.Id.Valid {
option := Option{
Caption: "",
Value: fmt.Sprintf("%v", int(c.Id.Int64)),
Selected: selected.Id.Valid && int(c.Id.Int64) == int(selected.Id.Int64),
}
if c.Name.Valid {
option.Caption = c.Name.String
}
options.Add(&option)
}
}
return options.ToArray(), nil
}

266
types.go
View file

@ -34,6 +34,88 @@ type Table struct {
Rows []TableRow
}
func (t Table) FilterColumns(indices ...int) (Table, error) {
table := Table{}
for _, i := range indices {
if i < 0 || i >= len(t.Collumns) {
return table, fmt.Errorf("Index out of bounds: expected: 0-%v: actual: %v", len(t.Collumns), i)
}
}
table.Collumns = make(TableColumns, len(indices))
for i, I := range indices {
table.Collumns[i] = t.Collumns[I]
}
table.Rows = make([]TableRow, len(t.Rows))
for i, r := range t.Rows {
table.Rows[i] = TableRow{}
table.Rows[i].Id = r.Id
table.Rows[i].Columns = make(TableColumns, len(indices))
for j, I := range indices {
table.Rows[i].Columns[j] = t.Rows[i].Columns[I]
}
}
return table, nil
}
type HtmlElement interface {
HtmlTag() string
}
type InputField struct {
Id string
Type string
Label string
Name string
Value string
IsRequired bool
}
func (i *InputField) HtmlTag() string {
return "input"
}
type Option struct {
Caption string
Value string
Selected bool
}
type Select struct {
Id string
Label string
Name string
Options []*Option
IsRequired bool
}
func (s *Select) HtmlTag() string {
return "select"
}
type TextArea struct {
Id string
Label string
Name string
Value string
IsRequired bool
}
func (t *TextArea) HtmlTag() string {
return "textarea"
}
type Form struct {
RowId int
Table string
Elements []HtmlElement
}
// Database
type Location struct {
@ -44,6 +126,70 @@ type Location struct {
Connection *Connection
}
func (l *Location) ToForm() (Form, error) {
form := Form{}
if l.Id.Valid {
form.RowId = int(l.Id.Int64)
}
form.Table = "locations"
form.Elements = make([]HtmlElement, 3)
lOptions := make([]*Option, 0)
locations, err := l.Connection.QueryLocations()
if err == nil {
lOptions, err = LocationsToOption(locations, l)
for i, o := range lOptions {
if o == nil {
fmt.Println(i)
}
}
}
if err != nil {
fmt.Println(err)
}
parentInput := &Select{
Id: "parent",
Label: "Übergeordneter Ort",
Name: "parent",
Options: lOptions,
}
nameInput := &InputField{
Id: "name",
Type: "text",
Label: "Name",
Name: "name",
}
descInput := &InputField{
Id: "description",
Type: "text",
Label: "Beschreibung",
Name: "description",
}
form.Elements[0] = parentInput
if l.Name.Valid {
nameInput.Value = fmt.Sprintf("%v", l.Name.String)
} else {
nameInput.Value = ""
}
form.Elements[1] = nameInput
if l.Description.Valid {
descInput.Value = fmt.Sprintf("%v", l.Description.String)
} else {
descInput.Value = ""
}
form.Elements[2] = descInput
return form, nil
}
func (l *Location) ToTableRow() TableRow {
columns := make(TableColumns, 3)
@ -75,11 +221,45 @@ func (l *Location) GetParent() *Location {
return nil
}
func (l *Location) FullPath() (string, error) {
return l.Connection.QueryLocationFullPath(l.Id.Int64)
}
type Container struct {
Id sql.NullInt64
Name sql.NullString
}
func (c *Container) ToForm() Form {
form := Form{}
if c.Id.Valid {
form.RowId = int(c.Id.Int64)
} else {
return form
}
form.Table = "containers"
form.Elements = make([]HtmlElement, 1)
nameInput := InputField{
"name",
"text",
"Name",
"name",
"",
true,
}
if c.Name.Valid {
nameInput.Value = c.Name.String
}
form.Elements[0] = &nameInput
return form
}
func (c *Container) ToTableRow() TableRow {
columns := make(TableColumns, 2)
@ -154,6 +334,92 @@ func (p *Part) ToTableRow() TableRow {
return tr
}
func (p *Part) ToForm() Form {
form := Form{}
if p.Id.Valid {
form.RowId = int(p.Id.Int64)
} else {
return form
}
form.Table = "parts"
form.Elements = make([]HtmlElement, 4)
nameInput := InputField{
"name",
"text",
"Name",
"name",
"",
true,
}
if p.Name.Valid {
nameInput.Value = p.Name.String
}
tagsInput := TextArea{
"tags",
"Tags",
"tags",
"",
true,
}
if p.Tags.Valid {
tagsInput.Value = p.Tags.String
}
lOptions := make([]*Option, 0)
locations, err := p.Connection.QueryLocations()
if err == nil {
lOptions, err = LocationsToOption(locations, &p.Location)
for i, o := range lOptions {
if o == nil {
fmt.Println(i)
}
}
}
if err != nil {
fmt.Println(err)
}
locationInput := Select{
Id: "location",
Label: "Ort",
Name: "location",
Options: lOptions,
}
cOptions := make([]*Option, 0)
containers, err := p.Connection.QueryContainers()
if err == nil {
cOptions, err = ContainersToOption(containers, &p.Container)
for i, o := range lOptions {
if o == nil {
fmt.Println(i)
}
}
}
containerInput := Select{
Id: "container",
Label: "Behälter",
Name: "container",
Options: cOptions,
}
form.Elements[0] = &nameInput
form.Elements[1] = &tagsInput
form.Elements[2] = &locationInput
form.Elements[3] = &containerInput
return form
}
// Interfaces
type DatabaseType interface {

View file

@ -1,9 +1,20 @@
{{template "partials/base-top" .}}
<form action="/admin/{{ .Table }}/edit" method="post">
{{ range .InputFields }}
{{ range .FormElements }}
{{ if eq .HtmlTag "input" }}
<label for="{{ .Id }}">{{ .Label }}</label>
<input type="{{ .Type }}" name="{{ .Name }}" id="{{ .Id }}">
<input type="{{ .Type }}" name="{{ .Name }}" id="{{ .Id }}" value="{{ .Value }}">
{{ else if eq .HtmlTag "select" }}
<select name="{{ .Name }}" id="{{ .Id }}">
<option value="-1">No Selection</option>
{{ range .Options }}
<option value="{{ .Value }}" {{ if .Selected }} selected {{ end }}>{{ .Caption }}</option>
{{ end }}
</select>
{{ else if eq .HtmlTag "textarea" }}
<textarea name="{{ .Name }}" id="{{ .Id }}">{{ .Value }}</textarea>
{{ end }}
{{ end }}
<input type="hidden" name="doEdit">
<input type="hidden" name="id" value="{{ .RowId }}">

View file

@ -21,11 +21,11 @@
{{ end }}
<td>
<div class="action-container">
<form action="/admin/{{ $Table }}/edit">
<form action="/admin/{{ $Table }}/edit" method="post">
<input type="hidden" name="id" value="{{ .Id }}">
<button type="submit"></button>
</form>
<form action="/admin/{{ $Table }}/delete">
<form action="/admin/{{ $Table }}/delete" method="post">
<input type="hidden" name="id" value="{{ .Id }}">
<button type="submit"></button>
</form>