mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 02:25:30 +00:00
Saving success messages betteR
This commit is contained in:
@@ -81,7 +81,7 @@ func (p *AlmanachContentsEditPage) GET(engine *templating.Engine, app core.App)
|
|||||||
data["pagination_values"] = paginationValuesSorted()
|
data["pagination_values"] = paginationValuesSorted()
|
||||||
data["agent_relations"] = dbmodels.AGENT_RELATIONS
|
data["agent_relations"] = dbmodels.AGENT_RELATIONS
|
||||||
|
|
||||||
if msg := e.Request.URL.Query().Get("saved_message"); msg != "" {
|
if msg := popFlashSuccess(e); msg != "" {
|
||||||
data["success"] = msg
|
data["success"] = msg
|
||||||
}
|
}
|
||||||
data["edit_content_id"] = strings.TrimSpace(e.Request.URL.Query().Get("edit_content"))
|
data["edit_content_id"] = strings.TrimSpace(e.Request.URL.Query().Get("edit_content"))
|
||||||
@@ -165,7 +165,7 @@ func (p *AlmanachContentsEditPage) GETItemEdit(engine *templating.Engine, app co
|
|||||||
data["content_index"] = contentIndex
|
data["content_index"] = contentIndex
|
||||||
data["content_total"] = contentTotal
|
data["content_total"] = contentTotal
|
||||||
|
|
||||||
if msg := e.Request.URL.Query().Get("saved_message"); msg != "" {
|
if msg := popFlashSuccess(e); msg != "" {
|
||||||
data["success"] = msg
|
data["success"] = msg
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,7 +588,8 @@ func (p *AlmanachContentsEditPage) POSTSave(engine *templating.Engine, app core.
|
|||||||
go updateContentsFTS5(app, entry, touched)
|
go updateContentsFTS5(app, entry, touched)
|
||||||
}
|
}
|
||||||
|
|
||||||
savedMessage := url.QueryEscape("Änderungen gespeichert.")
|
saveAction := strings.TrimSpace(e.Request.FormValue("save_action"))
|
||||||
|
savedMessage := "Änderungen gespeichert."
|
||||||
if contentID != "" {
|
if contentID != "" {
|
||||||
effectiveContentID := contentID
|
effectiveContentID := contentID
|
||||||
if mappedID, ok := tempToCreated[effectiveContentID]; ok {
|
if mappedID, ok := tempToCreated[effectiveContentID]; ok {
|
||||||
@@ -596,12 +597,18 @@ func (p *AlmanachContentsEditPage) POSTSave(engine *templating.Engine, app core.
|
|||||||
}
|
}
|
||||||
if effectiveContentID != "" {
|
if effectiveContentID != "" {
|
||||||
if resolved, err := dbmodels.Contents_IDs(app, []any{effectiveContentID}); err == nil && len(resolved) > 0 {
|
if resolved, err := dbmodels.Contents_IDs(app, []any{effectiveContentID}); err == nil && len(resolved) > 0 {
|
||||||
redirect := fmt.Sprintf("/almanach/%s/contents/%d/edit?saved_message=%s", id, resolved[0].MusenalmID(), savedMessage)
|
if saveAction == "view" {
|
||||||
|
redirect := fmt.Sprintf("/beitrag/%d", resolved[0].MusenalmID())
|
||||||
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
|
}
|
||||||
|
setFlashSuccess(e, savedMessage)
|
||||||
|
redirect := fmt.Sprintf("/almanach/%s/contents/%d/edit", id, resolved[0].MusenalmID())
|
||||||
return e.Redirect(http.StatusSeeOther, redirect)
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
redirect := fmt.Sprintf("/almanach/%s/contents/edit?saved_message=%s", id, savedMessage)
|
setFlashSuccess(e, savedMessage)
|
||||||
|
redirect := fmt.Sprintf("/almanach/%s/contents/edit", id)
|
||||||
return e.Redirect(http.StatusSeeOther, redirect)
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -641,7 +648,8 @@ func (p *AlmanachContentsEditPage) POSTUpdateExtent(engine *templating.Engine, a
|
|||||||
}
|
}
|
||||||
}(app, entry)
|
}(app, entry)
|
||||||
|
|
||||||
redirect := fmt.Sprintf("/almanach/%s/contents/edit?saved_message=%s", id, url.QueryEscape("Struktur/Umfang gespeichert."))
|
setFlashSuccess(e, "Struktur/Umfang gespeichert.")
|
||||||
|
redirect := fmt.Sprintf("/almanach/%s/contents/edit", id)
|
||||||
return e.Redirect(http.StatusSeeOther, redirect)
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ func (p *AlmanachEditPage) GET(engine *templating.Engine, app core.App) HandleFu
|
|||||||
data["agent_relations"] = dbmodels.AGENT_RELATIONS
|
data["agent_relations"] = dbmodels.AGENT_RELATIONS
|
||||||
data["series_relations"] = dbmodels.SERIES_RELATIONS
|
data["series_relations"] = dbmodels.SERIES_RELATIONS
|
||||||
|
|
||||||
if msg := e.Request.URL.Query().Get("saved_message"); msg != "" {
|
if msg := popFlashSuccess(e); msg != "" {
|
||||||
data["success"] = msg
|
data["success"] = msg
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +225,7 @@ func (p *AlmanachEditPage) POSTSave(engine *templating.Engine, app core.App) Han
|
|||||||
updatedInfo["user"] = user.Name
|
updatedInfo["user"] = user.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFlashSuccess(e, "Änderungen gespeichert.")
|
||||||
return e.JSON(http.StatusOK, map[string]any{
|
return e.JSON(http.StatusOK, map[string]any{
|
||||||
"success": true,
|
"success": true,
|
||||||
"message": "Änderungen gespeichert.",
|
"message": "Änderungen gespeichert.",
|
||||||
|
|||||||
47
controllers/flash.go
Normal file
47
controllers/flash.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
const flashSuccessCookieName = "flash_success"
|
||||||
|
|
||||||
|
func setFlashSuccess(e *core.RequestEvent, message string) {
|
||||||
|
if e == nil || message == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.SetCookie(&http.Cookie{
|
||||||
|
Name: flashSuccessCookieName,
|
||||||
|
Value: url.QueryEscape(message),
|
||||||
|
Path: "/",
|
||||||
|
MaxAge: 60,
|
||||||
|
HttpOnly: true,
|
||||||
|
SameSite: http.SameSiteLaxMode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func popFlashSuccess(e *core.RequestEvent) string {
|
||||||
|
if e == nil || e.Request == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
cookie, err := e.Request.Cookie(flashSuccessCookieName)
|
||||||
|
if err != nil || cookie == nil || cookie.Value == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
e.SetCookie(&http.Cookie{
|
||||||
|
Name: flashSuccessCookieName,
|
||||||
|
Value: "",
|
||||||
|
Path: "/",
|
||||||
|
MaxAge: -1,
|
||||||
|
HttpOnly: true,
|
||||||
|
SameSite: http.SameSiteLaxMode,
|
||||||
|
})
|
||||||
|
value, err := url.QueryUnescape(cookie.Value)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -110,7 +109,7 @@ func (p *OrtEditPage) GET(engine *templating.Engine, app core.App) HandleFunc {
|
|||||||
req := templating.NewRequest(e)
|
req := templating.NewRequest(e)
|
||||||
data["csrf_token"] = req.Session().Token
|
data["csrf_token"] = req.Session().Token
|
||||||
|
|
||||||
if msg := e.Request.URL.Query().Get("saved_message"); msg != "" {
|
if msg := popFlashSuccess(e); msg != "" {
|
||||||
data["success"] = msg
|
data["success"] = msg
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +141,7 @@ func (p *OrtEditPage) renderError(engine *templating.Engine, app core.App, e *co
|
|||||||
type ortEditForm struct {
|
type ortEditForm struct {
|
||||||
CSRFToken string `form:"csrf_token"`
|
CSRFToken string `form:"csrf_token"`
|
||||||
LastEdited string `form:"last_edited"`
|
LastEdited string `form:"last_edited"`
|
||||||
|
SaveAction string `form:"save_action"`
|
||||||
Name string `form:"name"`
|
Name string `form:"name"`
|
||||||
Pseudonyms string `form:"pseudonyms"`
|
Pseudonyms string `form:"pseudonyms"`
|
||||||
Annotation string `form:"annotation"`
|
Annotation string `form:"annotation"`
|
||||||
@@ -237,7 +237,16 @@ func (p *OrtEditPage) POST(engine *templating.Engine, app core.App) HandleFunc {
|
|||||||
}
|
}
|
||||||
}(app, place.Id, nameChanged)
|
}(app, place.Id, nameChanged)
|
||||||
|
|
||||||
redirect := fmt.Sprintf("/ort/%s/edit?saved_message=%s", id, url.QueryEscape("Änderungen gespeichert."))
|
if strings.TrimSpace(formdata.SaveAction) == "view" {
|
||||||
|
redirect := fmt.Sprintf("/reihen/?place=%s", id)
|
||||||
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(formdata.SaveAction) == "view" {
|
||||||
|
redirect := fmt.Sprintf("/ort/%s", id)
|
||||||
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
|
}
|
||||||
|
setFlashSuccess(e, "Änderungen gespeichert.")
|
||||||
|
redirect := fmt.Sprintf("/ort/%s/edit", id)
|
||||||
return e.Redirect(http.StatusSeeOther, redirect)
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -143,7 +142,8 @@ func (p *OrtNewPage) POST(engine *templating.Engine, app core.App) HandleFunc {
|
|||||||
}
|
}
|
||||||
}(app, createdPlace.Id)
|
}(app, createdPlace.Id)
|
||||||
|
|
||||||
redirect := fmt.Sprintf("/ort/%s/edit?saved_message=%s", createdPlace.Id, url.QueryEscape("Änderungen gespeichert."))
|
setFlashSuccess(e, "Änderungen gespeichert.")
|
||||||
|
redirect := fmt.Sprintf("/ort/%s/edit", createdPlace.Id)
|
||||||
return e.Redirect(http.StatusSeeOther, redirect)
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ func (p *PersonEditPage) GET(engine *templating.Engine, app core.App) HandleFunc
|
|||||||
req := templating.NewRequest(e)
|
req := templating.NewRequest(e)
|
||||||
data["csrf_token"] = req.Session().Token
|
data["csrf_token"] = req.Session().Token
|
||||||
|
|
||||||
if msg := e.Request.URL.Query().Get("saved_message"); msg != "" {
|
if msg := popFlashSuccess(e); msg != "" {
|
||||||
data["success"] = msg
|
data["success"] = msg
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +240,7 @@ func agentContentsDetails(app core.App, agentID string) ([]*dbmodels.Content, ma
|
|||||||
type personEditForm struct {
|
type personEditForm struct {
|
||||||
CSRFToken string `form:"csrf_token"`
|
CSRFToken string `form:"csrf_token"`
|
||||||
LastEdited string `form:"last_edited"`
|
LastEdited string `form:"last_edited"`
|
||||||
|
SaveAction string `form:"save_action"`
|
||||||
Name string `form:"name"`
|
Name string `form:"name"`
|
||||||
Pseudonyms string `form:"pseudonyms"`
|
Pseudonyms string `form:"pseudonyms"`
|
||||||
BiographicalData string `form:"biographical_data"`
|
BiographicalData string `form:"biographical_data"`
|
||||||
@@ -348,9 +349,14 @@ func (p *PersonEditPage) POST(engine *templating.Engine, app core.App) HandleFun
|
|||||||
}
|
}
|
||||||
}(app, agent.Id, nameChanged)
|
}(app, agent.Id, nameChanged)
|
||||||
|
|
||||||
|
if strings.TrimSpace(formdata.SaveAction) == "view" {
|
||||||
redirect := fmt.Sprintf("/person/%s", id)
|
redirect := fmt.Sprintf("/person/%s", id)
|
||||||
return e.Redirect(http.StatusSeeOther, redirect)
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
}
|
}
|
||||||
|
setFlashSuccess(e, "Änderungen gespeichert.")
|
||||||
|
redirect := fmt.Sprintf("/person/%s/edit", id)
|
||||||
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PersonEditPage) POSTDelete(engine *templating.Engine, app core.App) HandleFunc {
|
func (p *PersonEditPage) POSTDelete(engine *templating.Engine, app core.App) HandleFunc {
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ func (p *ReiheEditPage) GET(engine *templating.Engine, app core.App) HandleFunc
|
|||||||
req := templating.NewRequest(e)
|
req := templating.NewRequest(e)
|
||||||
data["csrf_token"] = req.Session().Token
|
data["csrf_token"] = req.Session().Token
|
||||||
|
|
||||||
if msg := e.Request.URL.Query().Get("saved_message"); msg != "" {
|
if msg := popFlashSuccess(e); msg != "" {
|
||||||
data["success"] = msg
|
data["success"] = msg
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,6 +353,7 @@ func preferredSeriesEntries(app core.App, seriesID string) ([]*dbmodels.Entry, e
|
|||||||
type reiheEditForm struct {
|
type reiheEditForm struct {
|
||||||
CSRFToken string `form:"csrf_token"`
|
CSRFToken string `form:"csrf_token"`
|
||||||
LastEdited string `form:"last_edited"`
|
LastEdited string `form:"last_edited"`
|
||||||
|
SaveAction string `form:"save_action"`
|
||||||
Title string `form:"title"`
|
Title string `form:"title"`
|
||||||
Pseudonyms string `form:"pseudonyms"`
|
Pseudonyms string `form:"pseudonyms"`
|
||||||
Annotation string `form:"annotation"`
|
Annotation string `form:"annotation"`
|
||||||
@@ -448,7 +449,12 @@ func (p *ReiheEditPage) POST(engine *templating.Engine, app core.App) HandleFunc
|
|||||||
}
|
}
|
||||||
}(app, series.Id, titleChanged)
|
}(app, series.Id, titleChanged)
|
||||||
|
|
||||||
|
if strings.TrimSpace(formdata.SaveAction) == "view" {
|
||||||
redirect := fmt.Sprintf("/reihe/%s/", id)
|
redirect := fmt.Sprintf("/reihe/%s/", id)
|
||||||
return e.Redirect(http.StatusSeeOther, redirect)
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
}
|
}
|
||||||
|
setFlashSuccess(e, "Änderungen gespeichert.")
|
||||||
|
redirect := fmt.Sprintf("/reihe/%s/edit", id)
|
||||||
|
return e.Redirect(http.StatusSeeOther, redirect)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -16,19 +16,13 @@
|
|||||||
<i class="ri-eye-line"></i> Anschauen
|
<i class="ri-eye-line"></i> Anschauen
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
·
|
|
||||||
<div>
|
|
||||||
<a href="/abkuerzungen/" class="text-gray-700 hover:text-slate-950 block no-underline">
|
|
||||||
<i class="ri-loop-left-line"></i> Reset
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-normal mx-auto mt-4 !px-0">
|
<div class="container-normal mx-auto mt-4 !px-0">
|
||||||
{{ template "_usermessage" $model }}
|
{{/* usermessage moved into action bar */}}
|
||||||
|
|
||||||
{{- if (IsAdminOrEditor $model.request.user) -}}
|
{{- if (IsAdminOrEditor $model.request.user) -}}
|
||||||
{{/* Editable form for admin/editor */}}
|
{{/* Editable form for admin/editor */}}
|
||||||
@@ -94,16 +88,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex items-end justify-between gap-4 mt-6 flex-wrap">
|
<div class="w-full flex items-end justify-between gap-4 mt-6 flex-wrap">
|
||||||
<p id="abk-save-feedback" class="text-sm text-gray-600" aria-live="polite"></p>
|
<p
|
||||||
|
id="abk-save-feedback"
|
||||||
|
class="form-action-bar-message save-feedback {{ if $model.error }}save-feedback-error text-red-700{{ else if $model.success }}save-feedback-success text-green-700{{ else }}hidden{{ end }}"
|
||||||
|
aria-live="polite">
|
||||||
|
{{- if $model.error -}}
|
||||||
|
{{ $model.error }}
|
||||||
|
{{- else if $model.success -}}
|
||||||
|
{{ $model.success }}
|
||||||
|
{{- end -}}
|
||||||
|
</p>
|
||||||
<div class="flex items-center gap-3 self-end flex-wrap">
|
<div class="flex items-center gap-3 self-end flex-wrap">
|
||||||
<a href="/abkuerzungen/" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
<a href="/abkuerzungen/" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
||||||
<i class="ri-close-line"></i>
|
<i class="ri-close-line"></i>
|
||||||
<span>Abbrechen</span>
|
<span>Abbrechen</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="/abkuerzungen/" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
|
||||||
<i class="ri-loop-left-line"></i>
|
|
||||||
<span>Reset</span>
|
|
||||||
</a>
|
|
||||||
<button type="submit" class="submitbutton w-48 flex items-center gap-2 justify-center">
|
<button type="submit" class="submitbutton w-48 flex items-center gap-2 justify-center">
|
||||||
<i class="ri-save-line"></i>
|
<i class="ri-save-line"></i>
|
||||||
<span>Alle speichern</span>
|
<span>Alle speichern</span>
|
||||||
|
|||||||
@@ -68,12 +68,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</div>
|
</div>
|
||||||
·
|
|
||||||
<div>
|
|
||||||
<a href="/almanach/{{- $model.result.Entry.MusenalmID -}}/contents/{{ $model.content.MusenalmID }}/edit" class="text-gray-700 no-underline hover:text-slate-950 block">
|
|
||||||
<i class="ri-loop-left-line"></i> Reset
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</div>
|
</div>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
@@ -170,10 +164,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-normal mx-auto mt-4 !px-0">
|
<div class="container-normal mx-auto mt-4 !px-0">
|
||||||
{{ template "_usermessage" $model }}
|
{{/* usermessage moved into action bar */}}
|
||||||
<form
|
<form
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="w-full dbform"
|
class="w-full dbform form-with-action-bar"
|
||||||
method="POST"
|
method="POST"
|
||||||
enctype="multipart/form-data"
|
enctype="multipart/form-data"
|
||||||
hx-boost="false"
|
hx-boost="false"
|
||||||
@@ -258,9 +252,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex items-end justify-between gap-4 mt-6 flex-wrap">
|
<div class="form-action-bar">
|
||||||
<p class="text-sm text-gray-600" aria-live="polite"></p>
|
<div class="form-action-bar-inner">
|
||||||
<div class="flex items-center gap-3 self-end flex-wrap">
|
<p
|
||||||
|
id="user-message"
|
||||||
|
class="form-action-bar-message save-feedback {{ if $model.error }}save-feedback-error text-red-700{{ else if $model.success }}save-feedback-success text-green-700{{ else }}hidden{{ end }}"
|
||||||
|
aria-live="polite">
|
||||||
|
{{- if $model.error -}}
|
||||||
|
{{ $model.error }}
|
||||||
|
{{- else if $model.success -}}
|
||||||
|
{{ $model.success }}
|
||||||
|
{{- end -}}
|
||||||
|
</p>
|
||||||
|
<div class="form-action-bar-actions">
|
||||||
<a href="/almanach/{{ $model.result.Entry.MusenalmID }}/contents/edit" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
<a href="/almanach/{{ $model.result.Entry.MusenalmID }}/contents/edit" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
||||||
<i class="ri-arrow-left-line"></i>
|
<i class="ri-arrow-left-line"></i>
|
||||||
<span>Liste</span>
|
<span>Liste</span>
|
||||||
@@ -275,10 +279,28 @@
|
|||||||
<span>Eintrag loeschen</span>
|
<span>Eintrag loeschen</span>
|
||||||
</button>
|
</button>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
<button type="submit" class="submitbutton w-40 flex items-center gap-2 justify-center">
|
{{- if $model.content.MusenalmID -}}
|
||||||
|
<button type="submit" name="save_action" value="stay" class="submitbutton flex items-center gap-2 justify-center">
|
||||||
<i class="ri-save-line"></i>
|
<i class="ri-save-line"></i>
|
||||||
<span>Speichern</span>
|
<span>Speichern</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="save_action"
|
||||||
|
value="view"
|
||||||
|
class="submitbutton flex items-center gap-2 justify-center"
|
||||||
|
>
|
||||||
|
<i class="ri-eye-line"></i>
|
||||||
|
<span>Speichern & Anzeigen</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
{{- if not $model.content.MusenalmID -}}
|
||||||
|
<button type="submit" name="save_action" value="stay" class="submitbutton flex items-center gap-2 justify-center">
|
||||||
|
<i class="ri-save-line"></i>
|
||||||
|
<span>Speichern</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<dialog data-role="content-delete-dialog" class="dbform fixed inset-0 m-auto rounded-md border border-slate-200 p-0 shadow-xl backdrop:bg-black/40">
|
<dialog data-role="content-delete-dialog" class="dbform fixed inset-0 m-auto rounded-md border border-slate-200 p-0 shadow-xl backdrop:bg-black/40">
|
||||||
@@ -388,28 +410,43 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showUserMessage = (message, type) => {
|
||||||
|
const userMessage = document.getElementById("user-message");
|
||||||
|
if (!userMessage) return;
|
||||||
|
userMessage.textContent = message;
|
||||||
|
userMessage.classList.remove("hidden", "save-feedback-error", "save-feedback-success", "text-red-700", "text-green-700");
|
||||||
|
if (type === "error") {
|
||||||
|
userMessage.classList.add("save-feedback-error", "text-red-700");
|
||||||
|
} else if (type === "success") {
|
||||||
|
userMessage.classList.add("save-feedback-success", "text-green-700");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const attachFormHandlers = () => {
|
||||||
const form = document.querySelector("form.dbform");
|
const form = document.querySelector("form.dbform");
|
||||||
const uploadInput = document.querySelector("[data-role='content-images-upload-input']");
|
const uploadInput = document.querySelector("[data-role='content-images-upload-input']");
|
||||||
const userMessage = document.getElementById("user-message");
|
const userMessage = document.getElementById("user-message");
|
||||||
if (form && uploadInput && userMessage) {
|
if (!form || !uploadInput || !userMessage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
form.addEventListener("submit", async (event) => {
|
form.addEventListener("submit", async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
|
const submitter = event.submitter;
|
||||||
const files = Array.from(uploadInput.files || []);
|
const files = Array.from(uploadInput.files || []);
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
const hasInvalid = files.some((file) => !file.type || !file.type.startsWith("image/"));
|
const hasInvalid = files.some((file) => !file.type || !file.type.startsWith("image/"));
|
||||||
if (hasInvalid) {
|
if (hasInvalid) {
|
||||||
userMessage.innerHTML = `
|
showUserMessage("Bitte nur Bilddateien auswählen.", "error");
|
||||||
<div class="text-red-800 text-sm mt-2 rounded-xs bg-red-200 p-2 font-bold border-red-700 shadow border mb-3">
|
|
||||||
<i class="ri-error-warning-fill"></i> Bitte nur Bilddateien auswählen.
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = new FormData(form);
|
const payload = new FormData(form);
|
||||||
|
if (submitter && submitter.name) {
|
||||||
|
payload.set(submitter.name, submitter.value || "");
|
||||||
|
}
|
||||||
if (payload.has("scans")) {
|
if (payload.has("scans")) {
|
||||||
payload.delete("scans");
|
payload.delete("scans");
|
||||||
}
|
}
|
||||||
@@ -440,26 +477,40 @@
|
|||||||
body: payload,
|
body: payload,
|
||||||
credentials: "same-origin",
|
credentials: "same-origin",
|
||||||
});
|
});
|
||||||
|
const html = await response.text();
|
||||||
if (response.redirected && response.url) {
|
if (response.redirected && response.url) {
|
||||||
|
if (response.url !== window.location.href) {
|
||||||
window.location.assign(response.url);
|
window.location.assign(response.url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const html = await response.text();
|
}
|
||||||
if (!html) {
|
if (!html) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const doc = new DOMParser().parseFromString(html, "text/html");
|
const doc = new DOMParser().parseFromString(html, "text/html");
|
||||||
|
const nextForm = doc.querySelector("form.dbform");
|
||||||
|
if (nextForm) {
|
||||||
|
form.replaceWith(nextForm);
|
||||||
|
initMultiSelects();
|
||||||
|
attachFormHandlers();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const nextMessage = doc.getElementById("user-message");
|
const nextMessage = doc.getElementById("user-message");
|
||||||
if (nextMessage) {
|
if (nextMessage) {
|
||||||
userMessage.innerHTML = nextMessage.innerHTML;
|
const liveMessage = document.getElementById("user-message");
|
||||||
|
if (liveMessage) {
|
||||||
|
liveMessage.className = nextMessage.className;
|
||||||
|
liveMessage.textContent = nextMessage.textContent || "";
|
||||||
|
if (!liveMessage.textContent.trim()) {
|
||||||
|
liveMessage.classList.add("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (!response.ok) {
|
} else if (!response.ok) {
|
||||||
userMessage.innerHTML = `
|
showUserMessage("Speichern fehlgeschlagen.", "error");
|
||||||
<div class="text-red-800 text-sm mt-2 rounded-xs bg-red-200 p-2 font-bold border-red-700 shadow border mb-3">
|
|
||||||
<i class="ri-error-warning-fill"></i> Speichern fehlgeschlagen.
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
attachFormHandlers();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -94,13 +94,6 @@ type AlmanachResult struct {
|
|||||||
</div>
|
</div>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</div>
|
</div>
|
||||||
·
|
|
||||||
<div>
|
|
||||||
<a href="/almanach/{{- $model.result.Entry.MusenalmID -}}/edit" class="text-gray-700
|
|
||||||
no-underline hover:text-slate-950 block ">
|
|
||||||
<i class="ri-loop-left-line"></i> Reset
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</div>
|
</div>
|
||||||
@@ -181,10 +174,10 @@ type AlmanachResult struct {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-normal mx-auto mt-4 !px-0">
|
<div class="container-normal mx-auto mt-4 !px-0">
|
||||||
{{ template "_usermessage" $model }}
|
{{/* usermessage moved into action bar */}}
|
||||||
<form
|
<form
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="w-full dbform"
|
class="w-full dbform form-with-action-bar"
|
||||||
id="changealmanachform"
|
id="changealmanachform"
|
||||||
x-target="changealmanachform user-message almanach-header-data"
|
x-target="changealmanachform user-message almanach-header-data"
|
||||||
hx-boost="false"
|
hx-boost="false"
|
||||||
@@ -1209,18 +1202,26 @@ type AlmanachResult struct {
|
|||||||
</div>
|
</div>
|
||||||
<!-- End Right Column -->
|
<!-- End Right Column -->
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full flex flex-col gap-3 mt-6 items-end">
|
<div class="form-action-bar">
|
||||||
<p id="almanach-save-feedback" class="save-feedback hidden text-right" aria-live="polite"></p>
|
<div class="form-action-bar-inner">
|
||||||
<div class="flex items-center gap-3 flex-wrap justify-end">
|
<div id="user-message" class="form-action-bar-message">
|
||||||
|
<p
|
||||||
|
id="almanach-save-feedback"
|
||||||
|
class="save-feedback {{ if $model.error }}save-feedback-error text-red-700{{ else if $model.success }}save-feedback-success text-green-700{{ else }}hidden{{ end }}"
|
||||||
|
aria-live="polite">
|
||||||
|
{{- if $model.error -}}
|
||||||
|
{{ $model.error }}
|
||||||
|
{{- else if $model.success -}}
|
||||||
|
{{ $model.success }}
|
||||||
|
{{- end -}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-action-bar-actions">
|
||||||
<a href="{{ if $model.is_new }}/suche/baende{{ else }}/almanach/{{ $model.result.Entry.MusenalmID }}{{ end }}" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
<a href="{{ if $model.is_new }}/suche/baende{{ else }}/almanach/{{ $model.result.Entry.MusenalmID }}{{ end }}" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
||||||
<i class="ri-close-line"></i>
|
<i class="ri-close-line"></i>
|
||||||
<span>Abbrechen</span>
|
<span>Abbrechen</span>
|
||||||
</a>
|
</a>
|
||||||
{{- if not $model.is_new -}}
|
{{- if not $model.is_new -}}
|
||||||
<button type="button" class="resetbutton w-40 flex items-center gap-2 justify-center" data-role="almanach-reset">
|
|
||||||
<i class="ri-loop-left-line"></i>
|
|
||||||
<span>Reset</span>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="resetbutton w-40 flex items-center gap-2 justify-center bg-red-50 text-red-800 hover:bg-red-100 hover:text-red-900"
|
class="resetbutton w-40 flex items-center gap-2 justify-center bg-red-50 text-red-800 hover:bg-red-100 hover:text-red-900"
|
||||||
@@ -1229,10 +1230,27 @@ type AlmanachResult struct {
|
|||||||
<span>Eintrag löschen</span>
|
<span>Eintrag löschen</span>
|
||||||
</button>
|
</button>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
{{- if not $model.is_new -}}
|
||||||
<button type="button" class="submitbutton w-40 flex items-center gap-2 justify-center" data-role="almanach-save">
|
<button type="button" class="submitbutton w-40 flex items-center gap-2 justify-center" data-role="almanach-save">
|
||||||
<i class="ri-save-line"></i>
|
<i class="ri-save-line"></i>
|
||||||
<span>Speichern</span>
|
<span>Speichern</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="submitbutton flex items-center gap-2 justify-center"
|
||||||
|
data-role="almanach-save-view"
|
||||||
|
data-redirect-url="/almanach/{{ $model.result.Entry.MusenalmID }}">
|
||||||
|
<i class="ri-eye-line"></i>
|
||||||
|
<span>Speichern & Anzeigen</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
{{- if $model.is_new -}}
|
||||||
|
<button type="button" class="submitbutton w-40 flex items-center gap-2 justify-center" data-role="almanach-save">
|
||||||
|
<i class="ri-save-line"></i>
|
||||||
|
<span>Speichern</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{- if not $model.is_new -}}
|
{{- if not $model.is_new -}}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<div data-role="content-edit" class="mt-2">
|
<div data-role="content-edit" class="mt-2">
|
||||||
<form
|
<form
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="w-full dbform"
|
class="w-full dbform form-with-action-bar"
|
||||||
method="POST"
|
method="POST"
|
||||||
action="/almanach/{{ $entry.MusenalmID }}/contents/edit">
|
action="/almanach/{{ $entry.MusenalmID }}/contents/edit">
|
||||||
<input type="hidden" name="csrf_token" value="{{ $csrf }}" />
|
<input type="hidden" name="csrf_token" value="{{ $csrf }}" />
|
||||||
@@ -33,7 +33,9 @@
|
|||||||
"content_agents" $contentAgents
|
"content_agents" $contentAgents
|
||||||
"agent_relations" $agentRelations
|
"agent_relations" $agentRelations
|
||||||
) -}}
|
) -}}
|
||||||
<div class="w-full flex items-center justify-end gap-3 mt-4 flex-wrap">
|
<div class="form-action-bar">
|
||||||
|
<div class="form-action-bar-inner">
|
||||||
|
<div class="form-action-bar-actions">
|
||||||
<a href="/almanach/{{ $entry.MusenalmID }}/contents/edit" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
<a href="/almanach/{{ $entry.MusenalmID }}/contents/edit" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
||||||
<i class="ri-close-line"></i>
|
<i class="ri-close-line"></i>
|
||||||
<span>Zurueck</span>
|
<span>Zurueck</span>
|
||||||
@@ -42,10 +44,29 @@
|
|||||||
<i class="ri-delete-bin-line"></i>
|
<i class="ri-delete-bin-line"></i>
|
||||||
<span>Eintrag loeschen</span>
|
<span>Eintrag loeschen</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="submitbutton w-40 flex items-center gap-2 justify-center">
|
{{- if $content.MusenalmID -}}
|
||||||
|
<button type="submit" name="save_action" value="stay" class="submitbutton flex items-center gap-2 justify-center">
|
||||||
<i class="ri-save-line"></i>
|
<i class="ri-save-line"></i>
|
||||||
<span>Speichern</span>
|
<span>Speichern</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="save_action"
|
||||||
|
value="view"
|
||||||
|
class="submitbutton flex items-center gap-2 justify-center"
|
||||||
|
>
|
||||||
|
<i class="ri-eye-line"></i>
|
||||||
|
<span>Speichern & Anzeigen</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
{{- if not $content.MusenalmID -}}
|
||||||
|
<button type="submit" name="save_action" value="stay" class="submitbutton flex items-center gap-2 justify-center">
|
||||||
|
<i class="ri-save-line"></i>
|
||||||
|
<span>Speichern</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<dialog data-role="content-delete-dialog" class="dbform fixed inset-0 m-auto rounded-md border border-slate-200 p-0 shadow-xl backdrop:bg-black/40">
|
<dialog data-role="content-delete-dialog" class="dbform fixed inset-0 m-auto rounded-md border border-slate-200 p-0 shadow-xl backdrop:bg-black/40">
|
||||||
|
|||||||
@@ -18,17 +18,18 @@
|
|||||||
</h1>
|
</h1>
|
||||||
{{- if not $model.is_new -}}
|
{{- if not $model.is_new -}}
|
||||||
<div class="flex flex-row gap-x-3">
|
<div class="flex flex-row gap-x-3">
|
||||||
|
<div>
|
||||||
|
<a href="/reihen/?place={{ $place.Id }}" class="text-gray-700 hover:text-slate-950 block no-underline">
|
||||||
|
<i class="ri-eye-line"></i> Anschauen
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
·
|
||||||
<div>
|
<div>
|
||||||
<a href="/orte/" class="text-gray-700 hover:text-slate-950 block no-underline">
|
<a href="/orte/" class="text-gray-700 hover:text-slate-950 block no-underline">
|
||||||
<i class="ri-eye-line"></i> Orte
|
<i class="ri-eye-line"></i> Orte
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
·
|
·
|
||||||
<div>
|
|
||||||
<a href="/ort/{{ $place.Id }}/edit" class="text-gray-700 no-underline hover:text-slate-950 block">
|
|
||||||
<i class="ri-loop-left-line"></i> Reset
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</div>
|
</div>
|
||||||
@@ -105,10 +106,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-normal mx-auto mt-4 !px-0">
|
<div class="container-normal mx-auto mt-4 !px-0">
|
||||||
{{ template "_usermessage" $model }}
|
{{/* usermessage moved into action bar */}}
|
||||||
<form
|
<form
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="w-full dbform"
|
class="w-full dbform form-with-action-bar"
|
||||||
id="changeplaceform"
|
id="changeplaceform"
|
||||||
method="POST"
|
method="POST"
|
||||||
action="{{ if $model.is_new }}/orte/new/{{ else }}/ort/{{ $place.Id }}/edit{{ end }}"
|
action="{{ if $model.is_new }}/orte/new/{{ else }}/ort/{{ $place.Id }}/edit{{ end }}"
|
||||||
@@ -191,18 +192,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex items-end justify-between gap-4 mt-6 flex-wrap">
|
<div class="form-action-bar">
|
||||||
<p id="place-save-feedback" class="text-sm text-gray-600" aria-live="polite"></p>
|
<div class="form-action-bar-inner">
|
||||||
<div class="flex items-center gap-3 self-end flex-wrap">
|
<p
|
||||||
|
id="place-save-feedback"
|
||||||
|
class="form-action-bar-message save-feedback {{ if $model.error }}save-feedback-error text-red-700{{ else if $model.success }}save-feedback-success text-green-700{{ else }}hidden{{ end }}"
|
||||||
|
aria-live="polite">
|
||||||
|
{{- if $model.error -}}
|
||||||
|
{{ $model.error }}
|
||||||
|
{{- else if $model.success -}}
|
||||||
|
{{ $model.success }}
|
||||||
|
{{- end -}}
|
||||||
|
</p>
|
||||||
|
<div class="form-action-bar-actions">
|
||||||
<a href="/orte/" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
<a href="/orte/" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
||||||
<i class="ri-close-line"></i>
|
<i class="ri-close-line"></i>
|
||||||
<span>Abbrechen</span>
|
<span>Abbrechen</span>
|
||||||
</a>
|
</a>
|
||||||
{{- if not $model.is_new -}}
|
{{- if not $model.is_new -}}
|
||||||
<a href="/ort/{{ $place.Id }}/edit" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
|
||||||
<i class="ri-loop-left-line"></i>
|
|
||||||
<span>Reset</span>
|
|
||||||
</a>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="resetbutton w-40 flex items-center gap-2 justify-center bg-red-50 text-red-800 hover:bg-red-100 hover:text-red-900"
|
class="resetbutton w-40 flex items-center gap-2 justify-center bg-red-50 text-red-800 hover:bg-red-100 hover:text-red-900"
|
||||||
@@ -211,10 +218,28 @@
|
|||||||
<span>Ort löschen</span>
|
<span>Ort löschen</span>
|
||||||
</button>
|
</button>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
<button type="submit" class="submitbutton w-40 flex items-center gap-2 justify-center">
|
{{- if not $model.is_new -}}
|
||||||
|
<button type="submit" name="save_action" value="stay" class="submitbutton flex items-center gap-2 justify-center">
|
||||||
<i class="ri-save-line"></i>
|
<i class="ri-save-line"></i>
|
||||||
<span>Speichern</span>
|
<span>Speichern</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="save_action"
|
||||||
|
value="view"
|
||||||
|
class="submitbutton flex items-center gap-2 justify-center"
|
||||||
|
>
|
||||||
|
<i class="ri-eye-line"></i>
|
||||||
|
<span>Speichern & Anzeigen</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
{{- if $model.is_new -}}
|
||||||
|
<button type="submit" name="save_action" value="stay" class="submitbutton flex items-center gap-2 justify-center">
|
||||||
|
<i class="ri-save-line"></i>
|
||||||
|
<span>Speichern</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{- if not $model.is_new -}}
|
{{- if not $model.is_new -}}
|
||||||
|
|||||||
@@ -26,11 +26,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
·
|
·
|
||||||
<div>
|
|
||||||
<a href="/person/{{ $agent.Id }}/edit" class="text-gray-700 no-underline hover:text-slate-950 block">
|
|
||||||
<i class="ri-loop-left-line"></i> Reset
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</div>
|
</div>
|
||||||
@@ -107,10 +102,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-normal mx-auto mt-4 !px-0">
|
<div class="container-normal mx-auto mt-4 !px-0">
|
||||||
{{ template "_usermessage" $model }}
|
{{/* usermessage moved into action bar */}}
|
||||||
<form
|
<form
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="w-full dbform"
|
class="w-full dbform form-with-action-bar"
|
||||||
id="changepersonform"
|
id="changepersonform"
|
||||||
method="POST"
|
method="POST"
|
||||||
action="{{ if $model.is_new }}/personen/new/{{ else }}/person/{{ $agent.Id }}/edit{{ end }}"
|
action="{{ if $model.is_new }}/personen/new/{{ else }}/person/{{ $agent.Id }}/edit{{ end }}"
|
||||||
@@ -248,18 +243,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex items-end justify-between gap-4 mt-6 flex-wrap">
|
<div class="form-action-bar">
|
||||||
<p id="person-save-feedback" class="text-sm text-gray-600" aria-live="polite"></p>
|
<div class="form-action-bar-inner">
|
||||||
<div class="flex items-center gap-3 self-end flex-wrap">
|
<p
|
||||||
|
id="person-save-feedback"
|
||||||
|
class="form-action-bar-message save-feedback {{ if $model.error }}save-feedback-error text-red-700{{ else if $model.success }}save-feedback-success text-green-700{{ else }}hidden{{ end }}"
|
||||||
|
aria-live="polite">
|
||||||
|
{{- if $model.error -}}
|
||||||
|
{{ $model.error }}
|
||||||
|
{{- else if $model.success -}}
|
||||||
|
{{ $model.success }}
|
||||||
|
{{- end -}}
|
||||||
|
</p>
|
||||||
|
<div class="form-action-bar-actions">
|
||||||
<a href="{{ if $model.is_new }}/personen/{{ else }}/person/{{ $agent.Id }}{{ end }}" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
<a href="{{ if $model.is_new }}/personen/{{ else }}/person/{{ $agent.Id }}{{ end }}" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
||||||
<i class="ri-close-line"></i>
|
<i class="ri-close-line"></i>
|
||||||
<span>Abbrechen</span>
|
<span>Abbrechen</span>
|
||||||
</a>
|
</a>
|
||||||
{{- if not $model.is_new -}}
|
{{- if not $model.is_new -}}
|
||||||
<a href="/person/{{ $agent.Id }}/edit" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
|
||||||
<i class="ri-loop-left-line"></i>
|
|
||||||
<span>Reset</span>
|
|
||||||
</a>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="resetbutton w-40 flex items-center gap-2 justify-center bg-red-50 text-red-800 hover:bg-red-100 hover:text-red-900"
|
class="resetbutton w-40 flex items-center gap-2 justify-center bg-red-50 text-red-800 hover:bg-red-100 hover:text-red-900"
|
||||||
@@ -268,10 +269,28 @@
|
|||||||
<span>Person löschen</span>
|
<span>Person löschen</span>
|
||||||
</button>
|
</button>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
<button type="submit" class="submitbutton w-40 flex items-center gap-2 justify-center">
|
{{- if not $model.is_new -}}
|
||||||
|
<button type="submit" name="save_action" value="stay" class="submitbutton flex items-center gap-2 justify-center">
|
||||||
<i class="ri-save-line"></i>
|
<i class="ri-save-line"></i>
|
||||||
<span>Speichern</span>
|
<span>Speichern</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="save_action"
|
||||||
|
value="view"
|
||||||
|
class="submitbutton flex items-center gap-2 justify-center"
|
||||||
|
>
|
||||||
|
<i class="ri-eye-line"></i>
|
||||||
|
<span>Speichern & Anzeigen</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
{{- if $model.is_new -}}
|
||||||
|
<button type="submit" name="save_action" value="stay" class="submitbutton flex items-center gap-2 justify-center">
|
||||||
|
<i class="ri-save-line"></i>
|
||||||
|
<span>Speichern</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{- if not $model.is_new -}}
|
{{- if not $model.is_new -}}
|
||||||
|
|||||||
@@ -26,11 +26,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
·
|
·
|
||||||
<div>
|
|
||||||
<a href="/reihe/{{ $series.MusenalmID }}/edit" class="text-gray-700 no-underline hover:text-slate-950 block">
|
|
||||||
<i class="ri-loop-left-line"></i> Reset
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</div>
|
</div>
|
||||||
@@ -107,10 +102,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-normal mx-auto mt-4 !px-0">
|
<div class="container-normal mx-auto mt-4 !px-0">
|
||||||
{{ template "_usermessage" $model }}
|
{{/* usermessage moved into action bar */}}
|
||||||
<form
|
<form
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="w-full dbform"
|
class="w-full dbform form-with-action-bar"
|
||||||
id="changeseriesform"
|
id="changeseriesform"
|
||||||
method="POST"
|
method="POST"
|
||||||
action="{{ if $model.is_new }}/reihen/new/{{ else }}/reihe/{{ $series.MusenalmID }}/edit{{ end }}"
|
action="{{ if $model.is_new }}/reihen/new/{{ else }}/reihe/{{ $series.MusenalmID }}/edit{{ end }}"
|
||||||
@@ -183,18 +178,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex items-end justify-between gap-4 mt-6 flex-wrap">
|
<div class="form-action-bar">
|
||||||
<p id="series-save-feedback" class="text-sm text-gray-600" aria-live="polite"></p>
|
<div class="form-action-bar-inner">
|
||||||
<div class="flex items-center gap-3 self-end flex-wrap">
|
<p
|
||||||
|
id="series-save-feedback"
|
||||||
|
class="form-action-bar-message save-feedback {{ if $model.error }}save-feedback-error text-red-700{{ else if $model.success }}save-feedback-success text-green-700{{ else }}hidden{{ end }}"
|
||||||
|
aria-live="polite">
|
||||||
|
{{- if $model.error -}}
|
||||||
|
{{ $model.error }}
|
||||||
|
{{- else if $model.success -}}
|
||||||
|
{{ $model.success }}
|
||||||
|
{{- end -}}
|
||||||
|
</p>
|
||||||
|
<div class="form-action-bar-actions">
|
||||||
<a href="{{ if $model.is_new }}/reihen/{{ else }}/reihe/{{ $series.MusenalmID }}{{ end }}" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
<a href="{{ if $model.is_new }}/reihen/{{ else }}/reihe/{{ $series.MusenalmID }}{{ end }}" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
||||||
<i class="ri-close-line"></i>
|
<i class="ri-close-line"></i>
|
||||||
<span>Abbrechen</span>
|
<span>Abbrechen</span>
|
||||||
</a>
|
</a>
|
||||||
{{- if not $model.is_new -}}
|
{{- if not $model.is_new -}}
|
||||||
<a href="/reihe/{{ $series.MusenalmID }}/edit" class="resetbutton w-40 flex items-center gap-2 justify-center">
|
|
||||||
<i class="ri-loop-left-line"></i>
|
|
||||||
<span>Reset</span>
|
|
||||||
</a>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="resetbutton w-40 flex items-center gap-2 justify-center bg-red-50 text-red-800 hover:bg-red-100 hover:text-red-900"
|
class="resetbutton w-40 flex items-center gap-2 justify-center bg-red-50 text-red-800 hover:bg-red-100 hover:text-red-900"
|
||||||
@@ -203,10 +204,28 @@
|
|||||||
<span>Reihe löschen</span>
|
<span>Reihe löschen</span>
|
||||||
</button>
|
</button>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
<button type="submit" class="submitbutton w-40 flex items-center gap-2 justify-center">
|
{{- if not $model.is_new -}}
|
||||||
|
<button type="submit" name="save_action" value="stay" class="submitbutton flex items-center gap-2 justify-center">
|
||||||
<i class="ri-save-line"></i>
|
<i class="ri-save-line"></i>
|
||||||
<span>Speichern</span>
|
<span>Speichern</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="save_action"
|
||||||
|
value="view"
|
||||||
|
class="submitbutton flex items-center gap-2 justify-center"
|
||||||
|
>
|
||||||
|
<i class="ri-eye-line"></i>
|
||||||
|
<span>Speichern & Anzeigen</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
{{- if $model.is_new -}}
|
||||||
|
<button type="submit" name="save_action" value="stay" class="submitbutton flex items-center gap-2 justify-center">
|
||||||
|
<i class="ri-save-line"></i>
|
||||||
|
<span>Speichern</span>
|
||||||
|
</button>
|
||||||
|
{{- end -}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{- if not $model.is_new -}}
|
{{- if not $model.is_new -}}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export class AlmanachEditPage extends HTMLElement {
|
|||||||
this._preferredSeriesRelationId = "";
|
this._preferredSeriesRelationId = "";
|
||||||
this._preferredSeriesSeriesId = "";
|
this._preferredSeriesSeriesId = "";
|
||||||
this._handleSaveClick = this._handleSaveClick.bind(this);
|
this._handleSaveClick = this._handleSaveClick.bind(this);
|
||||||
|
this._handleSaveViewClick = this._handleSaveViewClick.bind(this);
|
||||||
this._handleResetClick = this._handleResetClick.bind(this);
|
this._handleResetClick = this._handleResetClick.bind(this);
|
||||||
this._handleDeleteClick = this._handleDeleteClick.bind(this);
|
this._handleDeleteClick = this._handleDeleteClick.bind(this);
|
||||||
this._handleDeleteConfirmClick = this._handleDeleteConfirmClick.bind(this);
|
this._handleDeleteConfirmClick = this._handleDeleteConfirmClick.bind(this);
|
||||||
@@ -145,6 +146,7 @@ export class AlmanachEditPage extends HTMLElement {
|
|||||||
this._teardownSaveHandling();
|
this._teardownSaveHandling();
|
||||||
this._form = this.querySelector("#changealmanachform");
|
this._form = this.querySelector("#changealmanachform");
|
||||||
this._saveButton = this.querySelector("[data-role='almanach-save']");
|
this._saveButton = this.querySelector("[data-role='almanach-save']");
|
||||||
|
this._saveViewButton = this.querySelector("[data-role='almanach-save-view']");
|
||||||
this._resetButton = this.querySelector("[data-role='almanach-reset']");
|
this._resetButton = this.querySelector("[data-role='almanach-reset']");
|
||||||
this._deleteButton = this.querySelector("[data-role='almanach-delete']");
|
this._deleteButton = this.querySelector("[data-role='almanach-delete']");
|
||||||
this._deleteDialog = this.querySelector("[data-role='almanach-delete-dialog']");
|
this._deleteDialog = this.querySelector("[data-role='almanach-delete-dialog']");
|
||||||
@@ -157,6 +159,9 @@ export class AlmanachEditPage extends HTMLElement {
|
|||||||
this._saveEndpoint = this._form.getAttribute("data-save-endpoint") || this._deriveSaveEndpoint();
|
this._saveEndpoint = this._form.getAttribute("data-save-endpoint") || this._deriveSaveEndpoint();
|
||||||
this._deleteEndpoint = this._form.getAttribute("data-delete-endpoint") || "";
|
this._deleteEndpoint = this._form.getAttribute("data-delete-endpoint") || "";
|
||||||
this._saveButton.addEventListener("click", this._handleSaveClick);
|
this._saveButton.addEventListener("click", this._handleSaveClick);
|
||||||
|
if (this._saveViewButton) {
|
||||||
|
this._saveViewButton.addEventListener("click", this._handleSaveViewClick);
|
||||||
|
}
|
||||||
if (this._resetButton) {
|
if (this._resetButton) {
|
||||||
this._resetButton.addEventListener("click", this._handleResetClick);
|
this._resetButton.addEventListener("click", this._handleResetClick);
|
||||||
}
|
}
|
||||||
@@ -188,6 +193,9 @@ export class AlmanachEditPage extends HTMLElement {
|
|||||||
if (this._saveButton) {
|
if (this._saveButton) {
|
||||||
this._saveButton.removeEventListener("click", this._handleSaveClick);
|
this._saveButton.removeEventListener("click", this._handleSaveClick);
|
||||||
}
|
}
|
||||||
|
if (this._saveViewButton) {
|
||||||
|
this._saveViewButton.removeEventListener("click", this._handleSaveViewClick);
|
||||||
|
}
|
||||||
if (this._resetButton) {
|
if (this._resetButton) {
|
||||||
this._resetButton.removeEventListener("click", this._handleResetClick);
|
this._resetButton.removeEventListener("click", this._handleResetClick);
|
||||||
}
|
}
|
||||||
@@ -204,6 +212,7 @@ export class AlmanachEditPage extends HTMLElement {
|
|||||||
this._deleteDialog.removeEventListener("cancel", this._handleDeleteCancelClick);
|
this._deleteDialog.removeEventListener("cancel", this._handleDeleteCancelClick);
|
||||||
}
|
}
|
||||||
this._saveButton = null;
|
this._saveButton = null;
|
||||||
|
this._saveViewButton = null;
|
||||||
this._resetButton = null;
|
this._resetButton = null;
|
||||||
this._deleteButton = null;
|
this._deleteButton = null;
|
||||||
this._deleteDialog = null;
|
this._deleteDialog = null;
|
||||||
@@ -258,13 +267,54 @@ export class AlmanachEditPage extends HTMLElement {
|
|||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data?.redirect) {
|
await this._reloadForm(data?.message || "Änderungen gespeichert.");
|
||||||
window.location.assign(data.redirect);
|
} catch (error) {
|
||||||
return;
|
this._showStatus(error instanceof Error ? error.message : "Speichern fehlgeschlagen.", "error");
|
||||||
|
} finally {
|
||||||
|
this._setSavingState(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._reloadForm(data?.message || "Änderungen gespeichert.");
|
async _handleSaveViewClick(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (this._isSaving) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const redirectUrl = this._saveViewButton?.getAttribute("data-redirect-url");
|
||||||
|
if (!redirectUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this._clearStatus();
|
this._clearStatus();
|
||||||
|
let payload;
|
||||||
|
try {
|
||||||
|
payload = this._buildPayload();
|
||||||
|
} catch (error) {
|
||||||
|
this._showStatus(error instanceof Error ? error.message : String(error), "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._setSavingState(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch(this._saveEndpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
let message = `Speichern fehlgeschlagen (${response.status}).`;
|
||||||
|
try {
|
||||||
|
const data = await response.clone().json();
|
||||||
|
message = data?.error || message;
|
||||||
|
} catch {
|
||||||
|
// ignore parsing error
|
||||||
|
}
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.assign(redirectUrl);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._showStatus(error instanceof Error ? error.message : "Speichern fehlgeschlagen.", "error");
|
this._showStatus(error instanceof Error ? error.message : "Speichern fehlgeschlagen.", "error");
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -134,6 +134,44 @@
|
|||||||
@apply w-full inline-flex justify-center py-2 px-4 border border-transparent rounded-md text-sm font-medium text-gray-800 bg-stone-200 hover:bg-stone-300 cursor-pointer focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-500 no-underline;
|
@apply w-full inline-flex justify-center py-2 px-4 border border-transparent rounded-md text-sm font-medium text-gray-800 bg-stone-200 hover:bg-stone-300 cursor-pointer focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-500 no-underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-with-action-bar {
|
||||||
|
@apply pb-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-action-bar {
|
||||||
|
@apply sticky bottom-0 z-30 border-t border-transparent bg-stone-50;
|
||||||
|
box-shadow: none;
|
||||||
|
width: calc(100% + 120px);
|
||||||
|
margin-left: -60px;
|
||||||
|
margin-right: -60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-action-bar-inner {
|
||||||
|
@apply w-full max-w-(--breakpoint-xl) mx-auto px-8 py-3 flex items-center justify-between gap-4 flex-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-action-bar-actions {
|
||||||
|
@apply ml-auto flex items-center gap-3 flex-wrap justify-end w-full md:w-auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-action-bar-message {
|
||||||
|
@apply text-left mr-auto w-full md:w-auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-action-bar .submitbutton,
|
||||||
|
.form-action-bar .resetbutton {
|
||||||
|
@apply !w-auto px-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-action-bar.is-stuck {
|
||||||
|
@apply border-t border-l border-r border-transparent;
|
||||||
|
background-color: rgb(250 250 249);
|
||||||
|
border-color: rgba(15, 23, 42, 0.06);
|
||||||
|
box-shadow: 0 -1px 6px rgba(15, 23, 42, 0.06);
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
border-top-right-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.content-action-button {
|
.content-action-button {
|
||||||
@apply inline-flex items-center justify-center gap-2 rounded-xs border border-slate-300 bg-white px-3 py-1.5 text-base font-semibold text-gray-800 shadow-sm transition-all duration-75 hover:bg-stone-50 focus:outline-none focus:ring-2 focus:ring-slate-400/30;
|
@apply inline-flex items-center justify-center gap-2 rounded-xs border border-slate-300 bg-white px-3 py-1.5 text-base font-semibold text-gray-800 shadow-sm transition-all duration-75 hover:bg-stone-50 focus:outline-none focus:ring-2 focus:ring-slate-400/30;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -616,6 +616,32 @@ function FormLoad(form) {
|
|||||||
// Update on change
|
// Update on change
|
||||||
checkbox.addEventListener("change", updateHiddenInput);
|
checkbox.addEventListener("change", updateHiddenInput);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function InitStickyActionBars() {
|
||||||
|
if (InitStickyActionBars._initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InitStickyActionBars._initialized = true;
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
const bars = document.querySelectorAll(".form-action-bar");
|
||||||
|
if (!bars.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const viewportBottom = window.innerHeight || document.documentElement.clientHeight;
|
||||||
|
bars.forEach((bar) => {
|
||||||
|
const rect = bar.getBoundingClientRect();
|
||||||
|
const stuck = rect.bottom >= viewportBottom - 1;
|
||||||
|
bar.classList.toggle("is-stuck", stuck);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
update();
|
||||||
|
window.addEventListener("scroll", update, { passive: true });
|
||||||
|
window.addEventListener("resize", update);
|
||||||
|
document.addEventListener("htmx:afterSwap", update);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("keydown", (event) => {
|
document.addEventListener("keydown", (event) => {
|
||||||
@@ -639,5 +665,6 @@ window.HookupRBChange = HookupRBChange;
|
|||||||
window.FormLoad = FormLoad;
|
window.FormLoad = FormLoad;
|
||||||
window.TextareaAutoResize = TextareaAutoResize;
|
window.TextareaAutoResize = TextareaAutoResize;
|
||||||
InitGlobalHtmxNotice();
|
InitGlobalHtmxNotice();
|
||||||
|
InitStickyActionBars();
|
||||||
|
|
||||||
export { FilterList, ScrollButton, AbbreviationTooltips, MultiSelectSimple, MultiSelectRole, ToolTip, PopupImage, TabList, FilterPill, ImageReel, IntLink, ItemsEditor, SingleSelectRemote, AlmanachEditPage, RelationsEditor, EditPage, FabMenu, LookupField };
|
export { FilterList, ScrollButton, AbbreviationTooltips, MultiSelectSimple, MultiSelectRole, ToolTip, PopupImage, TabList, FilterPill, ImageReel, IntLink, ItemsEditor, SingleSelectRemote, AlmanachEditPage, RelationsEditor, EditPage, FabMenu, LookupField };
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class ScrollButton extends HTMLElement {
|
|||||||
<button
|
<button
|
||||||
class="
|
class="
|
||||||
scroll-to-top
|
scroll-to-top
|
||||||
fixed bottom-5 right-5
|
fixed bottom-12 right-8 z-50
|
||||||
hidden
|
hidden
|
||||||
bg-gray-800 text-white
|
bg-gray-800 text-white
|
||||||
p-2
|
p-2
|
||||||
|
|||||||
Reference in New Issue
Block a user