[Add form generation]
This commit is contained in:
parent
b33dc14db9
commit
215cfcb7ea
6 changed files with 513 additions and 6 deletions
88
list.go
Normal file
88
list.go
Normal 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
97
main.go
|
@ -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
49
misc.go
|
@ -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
266
types.go
|
@ -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 {
|
||||
|
|
|
@ -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 }}">
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue