feat(17-02): tree reorder page and singleton form in the admin

- ReorderView: indented tree with drag handle, move up/down, outdent and
  indent (buttons and arrow keys on the handle), pointer drag before, after
  or inside a row and never into its own subtree; one POST with the whole
  node set 400 ms after the last move, flushed on leave; a failed save
  restores the saved tree, a 409 reloads; loading, empty, load-failure and
  403 states; partition selector
- ListView: outline Reorder button before New record, passing the filtered
  partition
- SettingsIndexView opens a singleton controller's record through
  GET …/singleton; FormView in singleton mode has no Delete and returns to
  the settings index
- reorder.* and messages.list.reorder phrase keys (en, pl), smoke spec, docs
This commit is contained in:
2026-10-12 16:18:31 +02:00
parent 204149ccd2
commit 872fd6535f
15 changed files with 1092 additions and 19 deletions

View File

@@ -288,6 +288,9 @@
"backend::lang.form.error_title": {
"other": "Nie udało się zapisać."
},
"backend::lang.form.forbidden": {
"other": "Nie masz uprawnień do wprowadzenia tej zmiany. Nic nie zostało zapisane."
},
"backend::lang.form.load_failed": {
"other": "Nie udało się wczytać formularza."
},
@@ -468,6 +471,9 @@
"many": ":count rekordów",
"other": ":count rekordu"
},
"backend::lang.messages.list.reorder": {
"other": "Zmień kolejność rekordów"
},
"backend::lang.messages.list.selected": {
"other": "Zaznaczono :count"
},
@@ -630,6 +636,54 @@
"backend::lang.relation.unlink": {
"other": "Odłącz"
},
"backend::lang.reorder.empty": {
"other": "Nie ma jeszcze czego porządkować"
},
"backend::lang.reorder.empty_hint": {
"other": "Najpierw dodaj rekordy, a potem wróć tutaj, aby ustalić ich kolejność."
},
"backend::lang.reorder.failed": {
"other": "Nie udało się zapisać nowej kolejności. Przywrócono poprzednią."
},
"backend::lang.reorder.indent": {
"other": "Przenieś do poprzedniej pozycji: :name"
},
"backend::lang.reorder.load_failed": {
"other": "Nie udało się wczytać rekordów. Odśwież stronę, aby spróbować ponownie."
},
"backend::lang.reorder.move": {
"other": "Przenieś: :name"
},
"backend::lang.reorder.move_down": {
"other": "Przenieś niżej: :name"
},
"backend::lang.reorder.move_up": {
"other": "Przenieś wyżej: :name"
},
"backend::lang.reorder.moved": {
"other": "Przeniesiono :name na pozycję :position z :total."
},
"backend::lang.reorder.nested": {
"other": ":name jest teraz wewnątrz :parent."
},
"backend::lang.reorder.outdent": {
"other": "Przenieś poziom wyżej: :name"
},
"backend::lang.reorder.saved": {
"other": "Zapisano kolejność"
},
"backend::lang.reorder.stale": {
"other": "Rekordy zmieniły się w międzyczasie. Lista została wczytana ponownie."
},
"backend::lang.reorder.tree": {
"other": "Kolejność rekordów"
},
"backend::lang.reorder.unnested": {
"other": "Przeniesiono :name na poziom :level."
},
"backend::lang.reorder.untitled": {
"other": "Bez tytułu (#:id)"
},
"backend::lang.repeater.add": {
"other": "Dodaj pozycję"
},