Completed contest/question/answer MVC
This commit is contained in:
parent
71997176b7
commit
4263fcd035
17 changed files with 331 additions and 27 deletions
4
Makefile
4
Makefile
|
@ -1,10 +1,10 @@
|
|||
PHONY: all
|
||||
|
||||
run_with_docker:
|
||||
dockerized:
|
||||
docker-compose -f compose/docker-compose.yml down
|
||||
docker-compose -f compose/docker-compose.yml up --build -d
|
||||
|
||||
run_outside_docker:
|
||||
dev:
|
||||
killall main || echo "Process was not running."
|
||||
docker-compose -f compose/docker-compose_outside_docker.yml down
|
||||
docker-compose -f compose/docker-compose_outside_docker.yml up -d db
|
||||
|
|
1
main.go
1
main.go
|
@ -28,6 +28,7 @@ var (
|
|||
models = []interface{}{
|
||||
&orm.Question{},
|
||||
&orm.Answer{},
|
||||
&orm.Contest{},
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ func (a *Answer) Read(args map[string]string, r *http.Request) (interface{}, err
|
|||
|
||||
func (a *Answer) ReadAll(args map[string]string, r *http.Request) (interface{}, error) {
|
||||
var answers []*Answer
|
||||
if err := DB().Order("created_at").Find(&answers).Error; err != nil {
|
||||
if err := DB().Preload("Question").Order("created_at").Find(&answers).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return answers, nil
|
||||
|
@ -90,6 +90,10 @@ func (a *Answer) Update(args map[string]string, r *http.Request) (interface{}, e
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// FIXME: Should not be hard set.
|
||||
answer.(*Answer).Correct = false
|
||||
|
||||
err = renderer.Decode(answer, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
136
orm/contest.go
Normal file
136
orm/contest.go
Normal file
|
@ -0,0 +1,136 @@
|
|||
package orm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.andreafazzi.eu/andrea/oef/renderer"
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
type Contest struct {
|
||||
gorm.Model
|
||||
|
||||
Name string
|
||||
Category string
|
||||
|
||||
Date *time.Time
|
||||
StartTime *time.Time
|
||||
EndTime *time.Time
|
||||
|
||||
Questions []*Question
|
||||
}
|
||||
|
||||
func (c *Contest) GetID() uint { return c.ID }
|
||||
|
||||
func (c *Contest) String() string {
|
||||
return c.Name
|
||||
}
|
||||
|
||||
func (c *Contest) Create(args map[string]string, r *http.Request) (interface{}, error) {
|
||||
if r.Method == "GET" {
|
||||
return nil, nil
|
||||
} else {
|
||||
contest := new(Contest)
|
||||
err := renderer.Decode(contest, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contest, err = CreateContest(contest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return contest, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Contest) Read(args map[string]string, r *http.Request) (interface{}, error) {
|
||||
var contest Contest
|
||||
|
||||
id := args["id"]
|
||||
|
||||
if err := DB().Preload("Questions").First(&contest, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &contest, nil
|
||||
}
|
||||
|
||||
func (c *Contest) ReadAll(args map[string]string, r *http.Request) (interface{}, error) {
|
||||
var contests []*Contest
|
||||
if err := DB().Order("created_at").Find(&contests).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return contests, nil
|
||||
}
|
||||
|
||||
func (c *Contest) Update(args map[string]string, r *http.Request) (interface{}, error) {
|
||||
if r.Method == "GET" {
|
||||
result, err := c.Read(args, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contest := result.(*Contest)
|
||||
|
||||
return contest, nil
|
||||
} else {
|
||||
contest, err := c.Read(args, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = r.ParseForm()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
date := r.FormValue("Date")
|
||||
startTime := r.FormValue("StartTime")
|
||||
endTime := r.FormValue("EndTime")
|
||||
|
||||
r.PostForm.Set("Date", fmt.Sprintf("%sT%s:00+00:00", date, startTime))
|
||||
r.PostForm.Set("StartTime", fmt.Sprintf("%sT%s:00+00:00", date, startTime))
|
||||
r.PostForm.Set("EndTime", fmt.Sprintf("%sT%s:00+00:00", date, endTime))
|
||||
|
||||
err = r.ParseForm()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.Form["Date"][0] = fmt.Sprintf("%sT%s+00:00", date, startTime)
|
||||
|
||||
err = renderer.Decode(contest, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = SaveContest(contest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contest, err = c.Read(args, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return contest.(*Contest), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Contest) Delete(args map[string]string, r *http.Request) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func CreateContest(contest *Contest) (*Contest, error) {
|
||||
if err := DB().Create(contest).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return contest, nil
|
||||
}
|
||||
|
||||
func SaveContest(contest interface{}) (interface{}, error) {
|
||||
if err := DB().Omit("Contests").Save(contest).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return contest, nil
|
||||
}
|
|
@ -10,9 +10,14 @@ import (
|
|||
type Question struct {
|
||||
gorm.Model
|
||||
|
||||
Text string
|
||||
Text string
|
||||
Contest *Contest
|
||||
ContestID uint `schema:"contest_id"`
|
||||
|
||||
Answers []*Answer
|
||||
|
||||
SelectedContest map[uint]string `gorm:"-"`
|
||||
AllContests []*Contest `gorm:"-"`
|
||||
}
|
||||
|
||||
func (q *Question) GetID() uint { return q.ID }
|
||||
|
@ -23,7 +28,11 @@ func (q *Question) String() string {
|
|||
|
||||
func (q *Question) Create(args map[string]string, r *http.Request) (interface{}, error) {
|
||||
if r.Method == "GET" {
|
||||
return nil, nil
|
||||
question := new(Question)
|
||||
if err := DB().Find(&question.AllContests).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return question, nil
|
||||
} else {
|
||||
question := new(Question)
|
||||
err := renderer.Decode(question, r)
|
||||
|
@ -43,7 +52,7 @@ func (q *Question) Read(args map[string]string, r *http.Request) (interface{}, e
|
|||
|
||||
id := args["id"]
|
||||
|
||||
if err := DB().Preload("Answers").Where("id = ?", id).Find(&question).Error; err != nil {
|
||||
if err := DB().Preload("Answers").Preload("Contest").First(&question, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -52,7 +61,7 @@ func (q *Question) Read(args map[string]string, r *http.Request) (interface{}, e
|
|||
|
||||
func (q *Question) ReadAll(args map[string]string, r *http.Request) (interface{}, error) {
|
||||
var questions []*Question
|
||||
if err := DB().Order("created_at").Find(&questions).Error; err != nil {
|
||||
if err := DB().Preload("Contest").Order("created_at").Find(&questions).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return questions, nil
|
||||
|
@ -60,7 +69,21 @@ func (q *Question) ReadAll(args map[string]string, r *http.Request) (interface{}
|
|||
|
||||
func (q *Question) Update(args map[string]string, r *http.Request) (interface{}, error) {
|
||||
if r.Method == "GET" {
|
||||
return q.Read(args, r)
|
||||
result, err := q.Read(args, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
question := result.(*Question)
|
||||
|
||||
if err := DB().Find(&question.AllContests).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
question.SelectedContest = make(map[uint]string)
|
||||
question.SelectedContest[question.ContestID] = "selected"
|
||||
|
||||
return question, nil
|
||||
} else {
|
||||
question, err := q.Read(args, nil)
|
||||
if err != nil {
|
||||
|
@ -94,7 +117,7 @@ func CreateQuestion(question *Question) (*Question, error) {
|
|||
}
|
||||
|
||||
func SaveQuestion(question interface{}) (interface{}, error) {
|
||||
if err := DB().Omit("Answers").Save(question).Error; err != nil {
|
||||
if err := DB().Omit("Answers", "Contest").Save(question).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return question, nil
|
||||
|
|
|
@ -21,6 +21,8 @@ var (
|
|||
funcMap = template.FuncMap{
|
||||
"query": query,
|
||||
"convertDate": convertDate,
|
||||
"convertTime": convertTime,
|
||||
"prettyDate": prettyDate,
|
||||
"modelPath": modelPath,
|
||||
"dict": dict,
|
||||
"yaml": yaml,
|
||||
|
@ -113,6 +115,9 @@ func incr(value int) int {
|
|||
|
||||
func callString(value interface{}) string {
|
||||
if value != nil {
|
||||
if reflect.ValueOf(value).Kind() == reflect.String {
|
||||
return value.(string)
|
||||
}
|
||||
return reflect.ValueOf(value).MethodByName("String").Interface().(func() string)()
|
||||
} else {
|
||||
return ""
|
||||
|
@ -185,13 +190,29 @@ func query(values ...string) template.URL {
|
|||
}
|
||||
|
||||
func convertDate(value interface{}) string {
|
||||
t, ok := value.(time.Time)
|
||||
t, ok := value.(*time.Time)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d-%02d-%02d", t.Year(), t.Month(), t.Day())
|
||||
}
|
||||
|
||||
func prettyDate(value interface{}) string {
|
||||
t, ok := value.(*time.Time)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%02d/%02d/%d", t.Day(), t.Month(), t.Year())
|
||||
}
|
||||
|
||||
func convertTime(value interface{}) string {
|
||||
t, ok := value.(*time.Time)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%02d:%02d", t.Hour(), t.Minute())
|
||||
}
|
||||
|
||||
func modelPath(model string, action string, id uint) string {
|
||||
var q template.URL
|
||||
|
||||
|
|
|
@ -280,9 +280,13 @@ func Decode(dst interface{}, r *http.Request) error {
|
|||
if value == "" {
|
||||
return reflect.ValueOf(time.Time{})
|
||||
}
|
||||
if v, err := time.Parse("2006-01-02", value); err == nil {
|
||||
|
||||
if v, err := time.Parse(time.RFC3339, value); err == nil {
|
||||
return reflect.ValueOf(v)
|
||||
} else {
|
||||
log.Println(value, err)
|
||||
}
|
||||
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
role="form"
|
||||
{{" id={{$form}}>"}}
|
||||
|
||||
{{" {{$options := ` { cancelTitle: \"Annulla\", saveTitle: \"Salva\", model: "}}"{{.Model}}"{{" `}} "}}
|
||||
{{" {{$options := ` { cancelTitle: \"Annulla\", saveTitle: \"Salva\", model: "}}"{{.Model}}"{{" } `}} "}}
|
||||
{{" {{template \"submit_cancel_buttons\" dict \"options\" ($options|yaml) \"id\" (.Data|field \"ID\") \"update\" $update}} "}}
|
||||
|
||||
</form>
|
||||
|
|
|
@ -19,8 +19,11 @@
|
|||
<span class="fa fa-reply"></span>
|
||||
{{$element|string}}
|
||||
<div class="text-right">
|
||||
{{$options := `noElements: "no subelements"`}}
|
||||
{{/*template "small" dict "options" ($options | yaml) "data" $element.SubElements*/}}
|
||||
{{$options := `noElements: "nessuna domanda"`}}
|
||||
{{template "small" dict "options" ($options | yaml) "data" $element.Question}}
|
||||
{{if $element.Correct}}
|
||||
{{template "small" dict "options" ($options | yaml) "data" "corretta"}}
|
||||
{{end}}
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
{{if $update}}
|
||||
|
||||
{{template "breadcrumb" toSlice "Risposte" (all "Answer") (.Data|string) (.Data.ID|show "Answer") "Aggiorna" "current"}}
|
||||
{{template "breadcrumb" toSlice "Risposte" (all "Answer") (.Data|string|trim) (.Data.ID|show "Answer") "Aggiorna" "current"}}
|
||||
{{else}}
|
||||
{{template "breadcrumb" toSlice "Risposte" (all "Answer") "Aggiungi" "current"}}
|
||||
{{end}}
|
||||
|
@ -25,6 +25,9 @@
|
|||
{{$options := ` { name: "question_id", id: "question_id", label: "Domanda relativa a questa risposta", title: "Seleziona la domanda"}`}}
|
||||
{{template "select" dict "options" ($options|yaml) "data" (.Data|field "AllQuestions") "selected" (.Data|field "Selected") "update" $update "form" $form}}
|
||||
|
||||
{{$options := ` { name: "Correct",id: "answer_correct",label: "La risposta è corretta",placeholder: "",type: "checkbox",formClass: "form-check form-check-inline"} `}}
|
||||
{{template "checkbox" dict "options" ($options|yaml) "value" (.Data|field "Correct") "update" $update}}
|
||||
|
||||
{{$options := ` { cancelTitle: "Annulla", saveTitle: "Salva", model: "Answer"} `}}
|
||||
{{template "submit_cancel_buttons" dict "options" ($options|yaml) "id" (.Data|field "ID") "update" $update}}
|
||||
|
||||
|
|
33
templates/contests.html.tpl
Normal file
33
templates/contests.html.tpl
Normal file
|
@ -0,0 +1,33 @@
|
|||
{{ define "content" }}
|
||||
|
||||
|
||||
<div class="container">
|
||||
{{$options := `
|
||||
title: "Gare"
|
||||
buttonTitle: "Crea nuova gara"
|
||||
`}}
|
||||
|
||||
{{template "read_all_header" dict "options" ($options | yaml) "lengthData" (len .Data) "modelPath" (create "Contest")}}
|
||||
{{template "search_input"}}
|
||||
|
||||
{{if not .}}
|
||||
{{template "display_no_elements"}}
|
||||
{{else}}
|
||||
<div class="list-group" id="myUL">
|
||||
{{range $element := .Data}}
|
||||
<a class="list-group-item list-group-item-action" href={{$element.ID|show "Contest"}}>
|
||||
<span class="fa fa-user"></span>
|
||||
{{$element|string}}
|
||||
<div class="text-right">
|
||||
{{$options := `noElements: "nessuna data"`}}
|
||||
{{template "small" dict "options" ($options | yaml) "data" ($element.Date|prettyDate)}}
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{{ end }}
|
46
templates/contests_add_update.html.tpl
Normal file
46
templates/contests_add_update.html.tpl
Normal file
|
@ -0,0 +1,46 @@
|
|||
{{ define "content" }}
|
||||
<div class="container">
|
||||
|
||||
{{$update := .Options.Get "update"}}
|
||||
|
||||
{{if $update}}
|
||||
|
||||
{{template "breadcrumb" toSlice "Contests" (all "Contest") (.Data|string) (.Data.ID|show "Contest") "Aggiorna" "current"}}
|
||||
{{else}}
|
||||
{{template "breadcrumb" toSlice "Contests" (all "Contest") "Aggiungi" "current"}}
|
||||
{{end}}
|
||||
|
||||
{{template "add_update_header" dict "update" $update "addTitle" "Crea nuovo ELEMENTO" "updateTitle" (printf "Aggiorna gara %s" (.Data|string))}}
|
||||
{{$form := "form_add_update"}}
|
||||
<form
|
||||
class="needs-validation"
|
||||
action="{{if $update}}{{.Data.ID|update "Contest"}}{{else}}{{create "Contest"}}{{end}}"
|
||||
method="POST"
|
||||
role="form"
|
||||
id={{$form}}>
|
||||
|
||||
{{$options := ` { name: "Name",id: "contest_name",label: "Nome della gara",placeholder: "Inserire il nome della gara",type: "text",required: "true"} `}}
|
||||
{{template "input" dict "options" ($options|yaml) "value" (.Data|field "Name") "update" $update}}
|
||||
|
||||
<div class="form-row">
|
||||
<div class="col">
|
||||
{{$options := ` { name: "Date",id: "contest_date",label: "La gara si svolgerà il giorno",type: "date" } `}}
|
||||
{{template "input" dict "options" ($options|yaml) "value" (.Data|field "Date"|convertDate) "update" $update}}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{$options := ` { name: "StartTime",id: "contest_start_time",label: "Dalle ore",type: "time" } `}}
|
||||
{{template "input" dict "options" ($options|yaml) "value" (.Data|field "StartTime"|convertTime) "update" $update}}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{$options := ` { name: "EndTime",id: "contest_end_time",label: "Alle ore",type: "time" } `}}
|
||||
{{template "input" dict "options" ($options|yaml) "value" (.Data|field "EndTime"|convertTime) "update" $update}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{$options := ` { cancelTitle: "Annulla", saveTitle: "Salva", model: "Contest" }`}}
|
||||
{{template "submit_cancel_buttons" dict "options" ($options|yaml) "id" (.Data|field "ID") "update" $update}}
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
{{ end }}
|
27
templates/contests_show.html.tpl
Normal file
27
templates/contests_show.html.tpl
Normal file
|
@ -0,0 +1,27 @@
|
|||
{{ define "content" }}
|
||||
|
||||
<div class="container">
|
||||
|
||||
{{template "breadcrumb" toSlice "Gare" (all "Contest") (.Data|string) "current"}}
|
||||
{{template "show_header" dict "title" (.Data|string) "updatePath" (.Data.ID|update "Contest") "deletePath" (.Data.ID|delete "Contest")}}
|
||||
|
||||
<h2 class="karmen-relation-header">Informazioni generali sulla gara</h2>
|
||||
<p>La gara si svolgerà il giorno <strong>{{.Data.Date|prettyDate}}</strong> dalle ore <strong>{{.Data.StartTime|convertTime}}</strong> alle ore <strong>{{.Data.EndTime|convertTime}}</strong>.</p>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
{{$options := `
|
||||
title: "Domande"
|
||||
model: "Question"
|
||||
icon: "fa fa-question-circle"
|
||||
`}}
|
||||
|
||||
{{$noElements := "nessuna domanda associata alla gara"}}
|
||||
{{template "relation_list" dict "options" ($options|yaml) "data" .Data.Questions "noElements" $noElements}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{ end }}
|
|
@ -12,7 +12,7 @@
|
|||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-top navbar-dark bg-primary">
|
||||
<a class="navbar-brand" href="/teachers?{{query "tpl_layout" "base" "tpl_content" "teachers"}}">
|
||||
<a class="navbar-brand" href="{{all "Contest"}}">
|
||||
<span class="fa fa-landmark"></span>
|
||||
OEF 2020
|
||||
</a>
|
||||
|
@ -21,14 +21,15 @@
|
|||
</button>
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<a class="nav-item nav-link {{.Options|active "Question"}}" href="{{all "Question"}}">Domande</a>
|
||||
<a class="nav-item nav-link {{.Options|active "Answer"}}" href="{{all "Answers"}}">Risposte</a>
|
||||
</ul>
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<a class="nav-item nav-link {{.Options|active "Contest"}}" href="{{all "Contest"}}">Gare</a>
|
||||
<a class="nav-item nav-link {{.Options|active "Question"}}" href="{{all "Question"}}">Domande</a>
|
||||
<a class="nav-item nav-link {{.Options|active "Answer"}}" href="{{all "Answers"}}">Risposte</a>
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a class="nav-link" href="/logout">Esci</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a class="nav-link" href="/logout">Esci</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</nav>
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-top navbar-dark bg-primary">
|
||||
<a class="navbar-brand" href="/teachers?{{query "tpl_layout" "base" "tpl_content" "teachers"}}">
|
||||
<a class="navbar-brand" href="{{all "Contest"}}">
|
||||
<span class="fa fa-landmark"></span>
|
||||
OEF 2020
|
||||
</a>
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
<span class="fa fa-question-circle"></span>
|
||||
{{$element|string}}
|
||||
<div class="text-right">
|
||||
{{$options := `noElements: "no subelements"`}}
|
||||
{{/*template "small" dict "options" ($options | yaml) "data" $element.SubElements*/}}
|
||||
{{$options := `noElements: "nessuna gara"`}}
|
||||
{{template "small" dict "options" ($options | yaml) "data" $element.Contest}}
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
|
|
|
@ -22,9 +22,11 @@
|
|||
{{$options := ` { name: "Text",id: "question_text",label: "Testo della domanda",placeholder: "Inserire il testo della domanda",type: "text",required: "true"} `}}
|
||||
{{template "input" dict "options" ($options|yaml) "value" (.Data|field "Text") "update" $update}}
|
||||
|
||||
{{$options := ` { name: "contest_id", id: "contest_id", label: "Gara relativa a questa domanda", title: "Seleziona la gara"}`}}
|
||||
{{template "select" dict "options" ($options|yaml) "data" (.Data|field "AllContests") "selected" (.Data|field "SelectedContest") "update" $update "form" $form}}
|
||||
|
||||
{{$options := ` { cancelTitle: "Annulla", saveTitle: "Salva", model: "Question"} `}}
|
||||
{{template "submit_cancel_buttons" dict "options" ($options|yaml) "id" (.Data|field "ID") "update" $update}}
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue