feat: PageHeader/Filter/Paginierung auf Register, Nutzerverwaltung, Abteilungen, Posteingang, Betreiber-Dashboard ausgerollt
Register und Fälle-Posteingang bekommen .page-header, Freitext-Filter
und Paginierung; Register zusätzlich .table-responsive statt
.table-scroll. Nutzerverwaltung bekommt Filter+Paginierung (bleibt
Karten-Liste). Abteilungen nur .page-header (bewusst ohne Paginierung,
typischerweise kleine Listen). Betreiber-Dashboard von .admin-kacheln
auf .stat-cards umgestellt.
Neuer gemeinsamer Helfer matchesQuery() in pagination.go für die
Freitext-Filter aller Listen-Seiten.
Dabei einen bereits live gewesenen CSS-Bug gefunden und behoben:
.filter-bar direkt auf einem <form> verlor gegen "form {
flex-direction: column }" (gleiche Spezifität, nur form setzte die
Property), wodurch die Filterleiste senkrecht statt waagerecht
stapelte — betraf die im letzten Schritt ausgelieferte
Mandanten-Werkzeugkatalog-Seite. Jetzt robust via explizitem
flex-direction: row in .filter-bar selbst.
This commit is contained in:
39
CLAUDE.md
39
CLAUDE.md
@@ -628,9 +628,42 @@ außerhalb des Arbeitsverzeichnisses liegt):
|
|||||||
`@ant-design/icons`, das ist React-spezifisch) — bewusste
|
`@ant-design/icons`, das ist React-spezifisch) — bewusste
|
||||||
Vereinfachung. Angewendet auf die Startseite (`index.html`).
|
Vereinfachung. Angewendet auf die Startseite (`index.html`).
|
||||||
|
|
||||||
Noch nicht auf weitere Tabellen/Seiten übertragen (Register,
|
**Ausgerollt (2026-08-31):** Register, Nutzerverwaltung, Abteilungen,
|
||||||
Nutzerliste, Abteilungen, Fälle-Posteingang, Betreiber-Dashboard) —
|
Fälle-Posteingang, Betreiber-Dashboard.
|
||||||
nächste Kandidaten, falls das Muster fortgesetzt werden soll.
|
|
||||||
|
- **Register** (`register_liste.html`) und **Fälle-Posteingang**
|
||||||
|
(`faelle_liste.html`) haben jetzt `.page-header` + Freitext-Filter
|
||||||
|
(`q`) + Paginierung. Register zusätzlich `.table-responsive` (war
|
||||||
|
vorher `.table-scroll`) mit `data-label` je `<td>`.
|
||||||
|
- **Nutzerverwaltung** (`nutzer_liste.html`) hat `.page-header` +
|
||||||
|
Freitext-Filter (E-Mail/Rolle) + Paginierung — blieb `.beitraege-liste`
|
||||||
|
(schon Karten-artig), keine `.table-responsive`-Umstellung nötig.
|
||||||
|
- **Abteilungen** (`abteilung_liste.html`) bekam nur `.page-header` —
|
||||||
|
bewusst ohne Paginierung, Abteilungslisten sind typischerweise klein
|
||||||
|
(Handvoll Stammdaten pro Mandant).
|
||||||
|
- **Betreiber-Dashboard** (`betreiber_dashboard.html`) von
|
||||||
|
`.admin-kacheln` auf `.stat-cards` umgestellt (analog zu `index.html`).
|
||||||
|
|
||||||
|
Neue gemeinsame Helfer in `pagination.go`: `matchesQuery(q string,
|
||||||
|
felder ...string) bool` — eine einzige Freitext-Filter-Basis für alle
|
||||||
|
Listen-Seiten, damit nicht jede ihre eigene Such-Logik erfindet.
|
||||||
|
|
||||||
|
**Beim Ausrollen gefundener, bereits live gewesener Bug:** `.filter-bar`
|
||||||
|
direkt auf einem `<form>`-Element (statt auf einem `<div>` innerhalb
|
||||||
|
des Formulars) verlor gegen die allgemeine `form { flex-direction:
|
||||||
|
column }`-Regel — beide Selektoren haben für die Property
|
||||||
|
`flex-direction` dieselbe Spezifität, aber nur `form` setzte sie
|
||||||
|
explizit, `.filter-bar` überließ sie dem Initialwert. Ergebnis: die
|
||||||
|
Filterleiste stapelte senkrecht statt in einer Zeile. Betraf bereits
|
||||||
|
die im vorigen Schritt ausgelieferte Mandanten-Werkzeugkatalog-Seite.
|
||||||
|
Behoben durch explizites `flex-direction: row` in `.filter-bar` selbst
|
||||||
|
— jetzt unabhängig davon robust, ob die Klasse auf einem `<form>` oder
|
||||||
|
einem `<div>` sitzt.
|
||||||
|
|
||||||
|
Noch nicht übertragen: Betreiber-Accounts, Betreiber-Audit-Log,
|
||||||
|
Wiedervorlage, "Meine Anträge" — nächste Kandidaten, falls das Muster
|
||||||
|
fortgesetzt werden soll (Audit-Log wird langfristig am ehesten groß
|
||||||
|
genug für Paginierung).
|
||||||
|
|
||||||
**Verifikationsmethode für CSS-Änderungen:** Live-Cookie-Auth per
|
**Verifikationsmethode für CSS-Änderungen:** Live-Cookie-Auth per
|
||||||
Chromium-Headless/CDP ist im Sandbox-Environment nicht möglich (kein
|
Chromium-Headless/CDP ist im Sandbox-Environment nicht möglich (kein
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ package web
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/netcell-it/deklarix/internal/auth"
|
"github.com/netcell-it/deklarix/internal/auth"
|
||||||
|
"github.com/netcell-it/deklarix/internal/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// mandantenRollen sind die Rollen, die ein Admin innerhalb des eigenen
|
// mandantenRollen sind die Rollen, die ein Admin innerhalb des eigenen
|
||||||
@@ -38,6 +40,8 @@ type nutzerListeData struct {
|
|||||||
Title string
|
Title string
|
||||||
Nav navData
|
Nav navData
|
||||||
Nutzer []nutzerListItem
|
Nutzer []nutzerListItem
|
||||||
|
Suche string
|
||||||
|
Pagination pagination
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleNutzerListe zeigt alle Logins des eigenen Mandanten.
|
// handleNutzerListe zeigt alle Logins des eigenen Mandanten.
|
||||||
@@ -48,8 +52,16 @@ func (s *Server) handleNutzerListe(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Nutzer konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
http.Error(w, "Nutzer konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := nutzerListeData{Title: "Nutzerverwaltung", Nav: navFor(r)}
|
q := strings.TrimSpace(r.URL.Query().Get("q"))
|
||||||
|
var gefiltert []store.User
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
|
if matchesQuery(q, u.Email, u.Role) {
|
||||||
|
gefiltert = append(gefiltert, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pag := paginationFromRequest(r, len(gefiltert))
|
||||||
|
data := nutzerListeData{Title: "Nutzerverwaltung", Nav: navFor(r), Suche: q, Pagination: pag}
|
||||||
|
for _, u := range paginate(gefiltert, pag) {
|
||||||
data.Nutzer = append(data.Nutzer, nutzerListItem{
|
data.Nutzer = append(data.Nutzer, nutzerListItem{
|
||||||
ID: u.ID, Email: u.Email, Role: u.Role, Active: u.Active, IstSelbst: u.ID == self.ID,
|
ID: u.ID, Email: u.Email, Role: u.Role, Active: u.Active, IstSelbst: u.ID == self.ID,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package web
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/netcell-it/deklarix/internal/store"
|
"github.com/netcell-it/deklarix/internal/store"
|
||||||
@@ -23,6 +24,8 @@ type fallListData struct {
|
|||||||
Title string
|
Title string
|
||||||
Nav navData
|
Nav navData
|
||||||
Faelle []fallListItem
|
Faelle []fallListItem
|
||||||
|
Suche string
|
||||||
|
Pagination pagination
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleFaelleListe zeigt den Posteingang: alle noch offenen (Status
|
// handleFaelleListe zeigt den Posteingang: alle noch offenen (Status
|
||||||
@@ -34,16 +37,25 @@ func (s *Server) handleFaelleListe(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Anträge konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
http.Error(w, "Anträge konnten nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := fallListData{Title: "Posteingang", Nav: navFor(r)}
|
q := strings.TrimSpace(r.URL.Query().Get("q"))
|
||||||
|
var offene []fallListItem
|
||||||
for _, a := range antraege {
|
for _, a := range antraege {
|
||||||
if a.Status != "eingereicht" {
|
if a.Status != "eingereicht" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
data.Faelle = append(data.Faelle, fallListItem{
|
if !matchesQuery(q, a.Titel, a.Haeufigkeit) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
offene = append(offene, fallListItem{
|
||||||
ID: a.ID, Titel: a.Titel, Haeufigkeit: a.Haeufigkeit,
|
ID: a.ID, Titel: a.Titel, Haeufigkeit: a.Haeufigkeit,
|
||||||
CreatedAt: a.CreatedAt.Format("02.01.2006 15:04"),
|
CreatedAt: a.CreatedAt.Format("02.01.2006 15:04"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pag := paginationFromRequest(r, len(offene))
|
||||||
|
data := fallListData{
|
||||||
|
Title: "Posteingang", Nav: navFor(r),
|
||||||
|
Faelle: paginate(offene, pag), Suche: q, Pagination: pag,
|
||||||
|
}
|
||||||
if err := s.templates.ExecuteTemplate(w, "faelle-liste", data); err != nil {
|
if err := s.templates.ExecuteTemplate(w, "faelle-liste", data); err != nil {
|
||||||
http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError)
|
http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package web
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultPerPage = 20
|
const defaultPerPage = 20
|
||||||
@@ -65,6 +66,25 @@ func (p pagination) bounds() (start, end int) {
|
|||||||
return start, end
|
return start, end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// matchesQuery prüft, ob q (Freitext-Suche, ohne Beachtung von
|
||||||
|
// Groß-/Kleinschreibung) in mindestens einem der übergebenen Felder
|
||||||
|
// vorkommt — leeres q trifft immer zu. Gemeinsame Basis für die
|
||||||
|
// Freitext-Filter mehrerer Listen-Seiten (Register, Nutzerverwaltung,
|
||||||
|
// Posteingang, ...), damit nicht jede Liste ihre eigene Such-Logik
|
||||||
|
// erfindet.
|
||||||
|
func matchesQuery(q string, felder ...string) bool {
|
||||||
|
if q == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
q = strings.ToLower(q)
|
||||||
|
for _, f := range felder {
|
||||||
|
if strings.Contains(strings.ToLower(f), q) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// paginate schneidet den Ausschnitt für die aktuelle Seite aus einer
|
// paginate schneidet den Ausschnitt für die aktuelle Seite aus einer
|
||||||
// bereits gefilterten/sortierten Liste heraus — generisch, damit jede
|
// bereits gefilterten/sortierten Liste heraus — generisch, damit jede
|
||||||
// Listen-Seite (Werkzeuge, Register, ...) dieselbe Funktion nutzt statt
|
// Listen-Seite (Werkzeuge, Register, ...) dieselbe Funktion nutzt statt
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ type registerListeData struct {
|
|||||||
Title string
|
Title string
|
||||||
Nav navData
|
Nav navData
|
||||||
Eintraege []registerEintragView
|
Eintraege []registerEintragView
|
||||||
|
Suche string
|
||||||
|
Pagination pagination
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) registerEintraegeFuerAccount(r *http.Request) ([]registerEintragView, error) {
|
func (s *Server) registerEintraegeFuerAccount(r *http.Request) ([]registerEintragView, error) {
|
||||||
@@ -61,7 +63,18 @@ func (s *Server) handleRegisterListe(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Register konnte nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
http.Error(w, "Register konnte nicht geladen werden: "+err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := registerListeData{Title: "Register", Nav: navFor(r), Eintraege: views}
|
q := strings.TrimSpace(r.URL.Query().Get("q"))
|
||||||
|
var gefiltert []registerEintragView
|
||||||
|
for _, e := range views {
|
||||||
|
if matchesQuery(q, e.Zweck, e.Abteilung, e.Werkzeug, e.Verantwortlicher) {
|
||||||
|
gefiltert = append(gefiltert, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pag := paginationFromRequest(r, len(gefiltert))
|
||||||
|
data := registerListeData{
|
||||||
|
Title: "Register", Nav: navFor(r), Eintraege: paginate(gefiltert, pag),
|
||||||
|
Suche: q, Pagination: pag,
|
||||||
|
}
|
||||||
if err := s.templates.ExecuteTemplate(w, "register-liste", data); err != nil {
|
if err := s.templates.ExecuteTemplate(w, "register-liste", data); err != nil {
|
||||||
http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError)
|
http.Error(w, "Seite konnte nicht gerendert werden", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -755,6 +755,12 @@ tbody tr:hover {
|
|||||||
pagination.go. */
|
pagination.go. */
|
||||||
.filter-bar {
|
.filter-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
/* explizit setzen, nicht dem initial-value überlassen: .filter-bar
|
||||||
|
landet manchmal direkt auf einem <form>, und "form { flex-direction:
|
||||||
|
column }" (oben) hat dieselbe Spezifität für DIESE Property, wenn
|
||||||
|
.filter-bar sie nicht selbst deklariert — das Formular würde dann
|
||||||
|
senkrecht stapeln statt in einer Zeile. */
|
||||||
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|||||||
@@ -4,8 +4,12 @@
|
|||||||
<body>
|
<body>
|
||||||
{{template "nav" .Nav}}
|
{{template "nav" .Nav}}
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<h1>Abteilungen</h1>
|
<div class="page-header">
|
||||||
<p class="hinweis">Stammdaten für den Fragebogen (Feld "Abteilung").</p>
|
<div>
|
||||||
|
<h1>Abteilungen</h1>
|
||||||
|
<p class="hinweis">Stammdaten für den Fragebogen (Feld "Abteilung").</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{if .Error}}<p class="fehler">{{.Error}}</p>{{end}}
|
{{if .Error}}<p class="fehler">{{.Error}}</p>{{end}}
|
||||||
{{if not .Abteilungen}}
|
{{if not .Abteilungen}}
|
||||||
<p class="hinweis">Noch keine Abteilungen.</p>
|
<p class="hinweis">Noch keine Abteilungen.</p>
|
||||||
|
|||||||
@@ -5,10 +5,25 @@
|
|||||||
{{template "nav" .Nav}}
|
{{template "nav" .Nav}}
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<h1>Plattform</h1>
|
<h1>Plattform</h1>
|
||||||
<ul class="admin-kacheln">
|
<ul class="stat-cards">
|
||||||
<li><a href="/betreiber/accounts">Accounts <span class="status">{{.AccountCount}}</span></a></li>
|
<li>
|
||||||
<li><a href="/betreiber/werkzeuge">Werkzeugkatalog <span class="status">{{.WerkzeugCount}}</span></a></li>
|
<a class="stat-card" href="/betreiber/accounts">
|
||||||
<li><a href="/betreiber/audit-log">Audit-Log <span class="status">{{.RecentAuditCount}} neueste</span></a></li>
|
<span class="stat-card-value">{{.AccountCount}}</span>
|
||||||
|
<span class="stat-card-label">Accounts</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="stat-card" href="/betreiber/werkzeuge">
|
||||||
|
<span class="stat-card-value">{{.WerkzeugCount}}</span>
|
||||||
|
<span class="stat-card-label">Werkzeugkatalog</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="stat-card" href="/betreiber/audit-log">
|
||||||
|
<span class="stat-card-value">{{.RecentAuditCount}}</span>
|
||||||
|
<span class="stat-card-label">Audit-Log (neueste)</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -4,11 +4,23 @@
|
|||||||
<body>
|
<body>
|
||||||
{{template "nav" .Nav}}
|
{{template "nav" .Nav}}
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<h1>Posteingang</h1>
|
<div class="page-header">
|
||||||
{{if not .Faelle}}
|
<div>
|
||||||
<p class="keine-findings">Keine offenen Anträge — alles bearbeitet.</p>
|
<h1>Posteingang</h1>
|
||||||
{{else}}
|
<p class="hinweis">Alle noch offenen Anträge deines Mandanten.</p>
|
||||||
<ul class="beitraege-liste">
|
</div>
|
||||||
|
</div>
|
||||||
|
<form method="get">
|
||||||
|
<div class="filter-bar">
|
||||||
|
<input type="search" name="q" value="{{.Suche}}" placeholder="Suche (Titel, Häufigkeit)">
|
||||||
|
<button type="submit">Filtern</button>
|
||||||
|
{{if .Suche}}<a href="/faelle">Zurücksetzen</a>{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{if not .Faelle}}
|
||||||
|
<p class="keine-findings">{{if .Suche}}Keine Treffer für diese Suche.{{else}}Keine offenen Anträge — alles bearbeitet.{{end}}</p>
|
||||||
|
{{else}}
|
||||||
|
<ul class="beitraege-liste">
|
||||||
{{range .Faelle}}
|
{{range .Faelle}}
|
||||||
<li>
|
<li>
|
||||||
<a href="/faelle/{{.ID}}">
|
<a href="/faelle/{{.ID}}">
|
||||||
@@ -16,8 +28,16 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
{{end}}
|
<div class="pagination">
|
||||||
|
<span class="hinweis">Seite {{.Pagination.Page}} von {{.Pagination.TotalPages}} ({{.Pagination.Total}} Einträge)</span>
|
||||||
|
<span>
|
||||||
|
{{if .Pagination.HasPrev}}<button type="submit" name="page" value="{{.Pagination.PrevPage}}">← Zurück</button>{{end}}
|
||||||
|
{{if .Pagination.HasNext}}<button type="submit" name="page" value="{{.Pagination.NextPage}}">Weiter →</button>{{end}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -4,7 +4,21 @@
|
|||||||
<body>
|
<body>
|
||||||
{{template "nav" .Nav}}
|
{{template "nav" .Nav}}
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<h1>Nutzerverwaltung</h1>
|
<div class="page-header">
|
||||||
|
<div>
|
||||||
|
<h1>Nutzerverwaltung</h1>
|
||||||
|
<p class="hinweis">Logins deines Mandanten — ein Login wird deaktiviert statt gelöscht.</p>
|
||||||
|
</div>
|
||||||
|
<a class="page-header-action" href="/verwaltung/nutzer/neu">Nutzer anlegen</a>
|
||||||
|
</div>
|
||||||
|
<form method="get" class="filter-bar">
|
||||||
|
<input type="search" name="q" value="{{.Suche}}" placeholder="Suche (E-Mail, Rolle)">
|
||||||
|
<button type="submit">Filtern</button>
|
||||||
|
{{if .Suche}}<a href="/verwaltung/nutzer">Zurücksetzen</a>{{end}}
|
||||||
|
</form>
|
||||||
|
{{if not .Nutzer}}
|
||||||
|
<p class="hinweis">{{if .Suche}}Keine Treffer für diese Suche.{{else}}Noch keine weiteren Nutzer.{{end}}</p>
|
||||||
|
{{else}}
|
||||||
<ul class="beitraege-liste">
|
<ul class="beitraege-liste">
|
||||||
{{range .Nutzer}}
|
{{range .Nutzer}}
|
||||||
<li>
|
<li>
|
||||||
@@ -25,7 +39,27 @@
|
|||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
<p><a href="/verwaltung/nutzer/neu">Nutzer anlegen</a> · <a href="/verwaltung/nutzer/import">Per CSV importieren</a></p>
|
<div class="pagination">
|
||||||
|
<span class="hinweis">Seite {{.Pagination.Page}} von {{.Pagination.TotalPages}} ({{.Pagination.Total}} Einträge)</span>
|
||||||
|
<span>
|
||||||
|
{{if .Pagination.HasPrev}}
|
||||||
|
<form method="get" style="display:inline">
|
||||||
|
<input type="hidden" name="q" value="{{.Suche}}">
|
||||||
|
<input type="hidden" name="page" value="{{.Pagination.PrevPage}}">
|
||||||
|
<button type="submit">← Zurück</button>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
|
{{if .Pagination.HasNext}}
|
||||||
|
<form method="get" style="display:inline">
|
||||||
|
<input type="hidden" name="q" value="{{.Suche}}">
|
||||||
|
<input type="hidden" name="page" value="{{.Pagination.NextPage}}">
|
||||||
|
<button type="submit">Weiter →</button>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
<p><a href="/verwaltung/nutzer/import">Per CSV importieren</a></p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -4,17 +4,28 @@
|
|||||||
<body>
|
<body>
|
||||||
{{template "nav" .Nav}}
|
{{template "nav" .Nav}}
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<h1>Register</h1>
|
<div class="page-header">
|
||||||
<p class="hinweis">
|
<div>
|
||||||
|
<h1>Register</h1>
|
||||||
|
<p class="hinweis">
|
||||||
Jede Genehmigung erzeugt automatisch einen Registereintrag —
|
Jede Genehmigung erzeugt automatisch einen Registereintrag —
|
||||||
abgelehnte Anträge und Rückfragen stehen nicht im Register.
|
abgelehnte Anträge und Rückfragen stehen nicht im Register.
|
||||||
</p>
|
</p>
|
||||||
<p><a href="/registereintraege/export.csv">CSV-Export</a> · <a href="/registereintraege/export.pdf">PDF-Export</a></p>
|
</div>
|
||||||
{{if not .Eintraege}}
|
<span class="hinweis"><a href="/registereintraege/export.csv">CSV-Export</a> · <a href="/registereintraege/export.pdf">PDF-Export</a></span>
|
||||||
<p class="hinweis">Noch keine Genehmigungen.</p>
|
</div>
|
||||||
{{else}}
|
<form method="get">
|
||||||
<div class="table-scroll">
|
<div class="filter-bar">
|
||||||
<table>
|
<input type="search" name="q" value="{{.Suche}}" placeholder="Suche (Zweck, Abteilung, Werkzeug, Verantwortliche/r)">
|
||||||
|
<button type="submit">Filtern</button>
|
||||||
|
{{if .Suche}}<a href="/registereintraege">Zurücksetzen</a>{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{if not .Eintraege}}
|
||||||
|
<p class="hinweis">{{if .Suche}}Keine Treffer für diese Suche.{{else}}Noch keine Genehmigungen.{{end}}</p>
|
||||||
|
{{else}}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Zweck</th><th>Abteilung</th><th>Werkzeug</th><th>Datenklasse</th>
|
<th>Zweck</th><th>Abteilung</th><th>Werkzeug</th><th>Datenklasse</th>
|
||||||
@@ -25,15 +36,29 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{{range .Eintraege}}
|
{{range .Eintraege}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{.Zweck}}</td><td>{{.Abteilung}}</td><td>{{.Werkzeug}}</td><td>{{.Datenklasse}}</td>
|
<td data-label="Zweck">{{.Zweck}}</td>
|
||||||
<td>{{.Einstufung}}</td><td>{{.Auflagen}}</td><td>{{.Verantwortlicher}}</td>
|
<td data-label="Abteilung">{{.Abteilung}}</td>
|
||||||
<td>{{.EntschiedenAm}}</td><td>{{.GueltigBis}}</td>
|
<td data-label="Werkzeug">{{.Werkzeug}}</td>
|
||||||
|
<td data-label="Datenklasse">{{.Datenklasse}}</td>
|
||||||
|
<td data-label="Einstufung">{{.Einstufung}}</td>
|
||||||
|
<td data-label="Auflagen">{{.Auflagen}}</td>
|
||||||
|
<td data-label="Verantwortliche/r">{{.Verantwortlicher}}</td>
|
||||||
|
<td data-label="Entschieden am">{{.EntschiedenAm}}</td>
|
||||||
|
<td data-label="Gültig bis">{{.GueltigBis}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
<div class="pagination">
|
||||||
|
<span class="hinweis">Seite {{.Pagination.Page}} von {{.Pagination.TotalPages}} ({{.Pagination.Total}} Einträge)</span>
|
||||||
|
<span>
|
||||||
|
{{if .Pagination.HasPrev}}<button type="submit" name="page" value="{{.Pagination.PrevPage}}">← Zurück</button>{{end}}
|
||||||
|
{{if .Pagination.HasNext}}<button type="submit" name="page" value="{{.Pagination.NextPage}}">Weiter →</button>{{end}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user