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:
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { Plus, SearchX } from '@lucide/vue'
|
||||
import { ArrowUpDown, Plus, SearchX } from '@lucide/vue'
|
||||
import { api } from '../api/client'
|
||||
import type { AdminRecord, BulkActionResult, ListMeta, ListQuery, ListSchema } from '../api/types'
|
||||
import { controllerIdFromParams, controllerPath } from '../app/controllerRoutes'
|
||||
@@ -79,6 +79,18 @@ const subtitle = computed(() => (meta.value ? message(messages.value?.recordCoun
|
||||
|
||||
const buttons = computed(() => schema.value?.toolbarButtons ?? [])
|
||||
const headingButtons = computed(() => buttons.value.filter((button) => button === 'create'))
|
||||
// The tree reorder page (D-09): offered when the list declares it; a list
|
||||
// filtered on the reorder partition column opens that partition.
|
||||
const reorderLink = computed(() => {
|
||||
const reorder = schema.value?.reorder
|
||||
if (!reorder) {
|
||||
return null
|
||||
}
|
||||
const column = reorder.partition?.column
|
||||
const filter = column ? schema.value?.filters.find((item) => item.column === column || item.name === column) : undefined
|
||||
const value = filter ? query.value.filter?.[filter.name] : undefined
|
||||
return { path: `${basePath}/reorder`, query: value !== undefined && value !== '' ? { partition: String(value) } : {} }
|
||||
})
|
||||
// Delete and the registered actions the admin may run, in declared order
|
||||
// (D-12). An action missing from toolbarActions is not rendered at all.
|
||||
const toolbarActions = computed(() => schema.value?.toolbarActions ?? [])
|
||||
@@ -408,7 +420,10 @@ async function onAction(name: string): Promise<void> {
|
||||
<h1 class="text-[26px] font-bold tracking-[-0.02em]">{{ title }}</h1>
|
||||
<p v-if="subtitle" data-record-count class="text-muted">{{ subtitle }}</p>
|
||||
</div>
|
||||
<div v-if="headingButtons.length > 0" class="flex items-center gap-2.5">
|
||||
<div v-if="headingButtons.length > 0 || reorderLink" class="flex items-center gap-2.5">
|
||||
<Button v-if="reorderLink" data-action="reorder" variant="outline" :icon="ArrowUpDown" :to="reorderLink">
|
||||
{{ t('backend::lang.messages.list.reorder') }}
|
||||
</Button>
|
||||
<Button
|
||||
v-for="button in headingButtons"
|
||||
:key="button"
|
||||
|
||||
Reference in New Issue
Block a user