@edc-motor/admin-kit 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edc-motor/admin-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "EdC Motor — kit del panel de administración: layout, gestores CRUD, imágenes, PDF, páginas, configuración, usuarios y copias (paquete fuente: lo compila el consumidor con Vite)",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@edc-motor/ui": "^0.
|
|
36
|
+
"@edc-motor/ui": "^0.4.0",
|
|
37
37
|
"axios": "^1.7.0",
|
|
38
38
|
"vue-draggable-plus": "^0.6.1"
|
|
39
39
|
},
|
|
@@ -120,7 +120,20 @@
|
|
|
120
120
|
|
|
121
121
|
display: grid;
|
|
122
122
|
gap: $space-3;
|
|
123
|
+
|
|
124
|
+
// display:grid no gobierna el contenido desplegado de un <details>: el
|
|
125
|
+
// aire de los checkboxes (bajo el selector de color) y del select del
|
|
126
|
+
// padre va a mano, y cada checkbox en su propia línea.
|
|
127
|
+
> .checkbox {
|
|
128
|
+
display: flex;
|
|
129
|
+
margin-top: $space-2;
|
|
130
|
+
}
|
|
131
|
+
> .checkbox:first-of-type { margin-top: $space-4; }
|
|
132
|
+
> .form-field { margin-top: $space-3; }
|
|
123
133
|
}
|
|
134
|
+
|
|
135
|
+
// Bloques hijos (parent_id): tarjeta sangrada bajo su padre.
|
|
136
|
+
&__item.is-child { margin-left: $space-7; }
|
|
124
137
|
}
|
|
125
138
|
|
|
126
139
|
.schema-fields {
|
|
@@ -6,6 +6,7 @@ import { VueDraggable } from 'vue-draggable-plus'
|
|
|
6
6
|
import {
|
|
7
7
|
BaseButton,
|
|
8
8
|
BaseCheckbox,
|
|
9
|
+
BaseSelect,
|
|
9
10
|
EditModal,
|
|
10
11
|
useConfirm,
|
|
11
12
|
useToast,
|
|
@@ -35,6 +36,8 @@ export interface PageBlocksLabels {
|
|
|
35
36
|
panelTitle: string
|
|
36
37
|
panelEmpty: string
|
|
37
38
|
panelContent: string
|
|
39
|
+
parent: string
|
|
40
|
+
parentNone: string
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
const defaultLabels: PageBlocksLabels = {
|
|
@@ -52,6 +55,8 @@ const defaultLabels: PageBlocksLabels = {
|
|
|
52
55
|
panelTitle: 'Bloque',
|
|
53
56
|
panelEmpty: 'Selecciona un bloque para ver sus acciones.',
|
|
54
57
|
panelContent: 'Contenido',
|
|
58
|
+
parent: 'Bloque padre (índices indentados)',
|
|
59
|
+
parentNone: '— Ninguno —',
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
const props = withDefaults(
|
|
@@ -90,6 +95,7 @@ interface BlockRow {
|
|
|
90
95
|
id: number
|
|
91
96
|
type: string
|
|
92
97
|
order: number
|
|
98
|
+
parent_id: number | null
|
|
93
99
|
settings: Record<string, unknown>
|
|
94
100
|
is_printable: boolean
|
|
95
101
|
is_indexable: boolean
|
|
@@ -128,6 +134,16 @@ const modalType = ref<BlockTypeSchema | null>(null)
|
|
|
128
134
|
const form = ref<Record<string, unknown>>({})
|
|
129
135
|
const formPrintable = ref(true)
|
|
130
136
|
const formIndexable = ref(true)
|
|
137
|
+
const formParent = ref<number | null>(null)
|
|
138
|
+
|
|
139
|
+
// Padres elegibles: bloques raíz de la página (un solo nivel de anidado),
|
|
140
|
+
// nunca uno mismo.
|
|
141
|
+
const parentOptions = computed(() => {
|
|
142
|
+
const self = editing.value?.id
|
|
143
|
+
return blocks.value
|
|
144
|
+
.filter((b) => !b.parent_id && b.id !== self)
|
|
145
|
+
.map((b) => ({ value: String(b.id), label: `${typeName(b.type)} — ${summary(b) || b.id}` }))
|
|
146
|
+
})
|
|
131
147
|
|
|
132
148
|
function typeName(key: string): string {
|
|
133
149
|
const type = types.value.find((t) => t.key === key)
|
|
@@ -205,6 +221,18 @@ const selectedFields = computed(() => {
|
|
|
205
221
|
.filter((entry) => entry.value)
|
|
206
222
|
})
|
|
207
223
|
|
|
224
|
+
/** Hijos justo debajo de su padre (en su orden relativo): anidado visible. */
|
|
225
|
+
function arrange(list: BlockRow[]): BlockRow[] {
|
|
226
|
+
const parents = list.filter((b) => !b.parent_id)
|
|
227
|
+
const out: BlockRow[] = []
|
|
228
|
+
for (const parent of parents) {
|
|
229
|
+
out.push(parent, ...list.filter((b) => b.parent_id === parent.id))
|
|
230
|
+
}
|
|
231
|
+
// Huérfanos (padre borrado en otra sesión): al final, sin perderse.
|
|
232
|
+
out.push(...list.filter((b) => !out.includes(b)))
|
|
233
|
+
return out
|
|
234
|
+
}
|
|
235
|
+
|
|
208
236
|
async function load() {
|
|
209
237
|
try {
|
|
210
238
|
const [palette, list] = await Promise.all([
|
|
@@ -212,7 +240,7 @@ async function load() {
|
|
|
212
240
|
props.api.get(`/admin/pages/${props.pageId}/blocks`),
|
|
213
241
|
])
|
|
214
242
|
types.value = palette.data.data
|
|
215
|
-
blocks.value = list.data.data
|
|
243
|
+
blocks.value = arrange(list.data.data)
|
|
216
244
|
} catch {
|
|
217
245
|
toast.danger(L.error)
|
|
218
246
|
}
|
|
@@ -229,6 +257,7 @@ function openCreate(type: BlockTypeSchema) {
|
|
|
229
257
|
form.value = defaults
|
|
230
258
|
formPrintable.value = true
|
|
231
259
|
formIndexable.value = true
|
|
260
|
+
formParent.value = null
|
|
232
261
|
modalOpen.value = true
|
|
233
262
|
}
|
|
234
263
|
|
|
@@ -238,6 +267,7 @@ function openEdit(block: BlockRow) {
|
|
|
238
267
|
form.value = { ...(block.settings ?? {}) }
|
|
239
268
|
formPrintable.value = block.is_printable
|
|
240
269
|
formIndexable.value = block.is_indexable
|
|
270
|
+
formParent.value = block.parent_id
|
|
241
271
|
modalOpen.value = true
|
|
242
272
|
}
|
|
243
273
|
|
|
@@ -250,6 +280,7 @@ async function save() {
|
|
|
250
280
|
settings: form.value,
|
|
251
281
|
is_printable: formPrintable.value,
|
|
252
282
|
is_indexable: formIndexable.value,
|
|
283
|
+
parent_id: formParent.value,
|
|
253
284
|
}
|
|
254
285
|
if (editing.value) {
|
|
255
286
|
await props.api.put(`/admin/blocks/${editing.value.id}`, payload)
|
|
@@ -282,8 +313,10 @@ async function remove(block: BlockRow) {
|
|
|
282
313
|
}
|
|
283
314
|
}
|
|
284
315
|
|
|
285
|
-
/** El drag reordena en cliente; se
|
|
316
|
+
/** El drag reordena en cliente; los hijos se recolocan bajo su padre y se
|
|
317
|
+
* persiste la lista de ids resultante. */
|
|
286
318
|
async function persistOrder() {
|
|
319
|
+
blocks.value = arrange(blocks.value)
|
|
287
320
|
try {
|
|
288
321
|
await props.api.post(`/admin/pages/${props.pageId}/blocks/reorder`, {
|
|
289
322
|
ids: blocks.value.map((b) => b.id),
|
|
@@ -337,7 +370,7 @@ defineExpose({ reload: load })
|
|
|
337
370
|
v-for="block in blocks"
|
|
338
371
|
:key="block.id"
|
|
339
372
|
class="page-blocks__item"
|
|
340
|
-
:class="{ 'is-active': selectedId === block.id }"
|
|
373
|
+
:class="{ 'is-active': selectedId === block.id, 'is-child': block.parent_id }"
|
|
341
374
|
@click="(e) => selectBlock(block, e)"
|
|
342
375
|
>
|
|
343
376
|
<span class="page-blocks__grip"><GripVertical :size="16" /></span>
|
|
@@ -381,6 +414,12 @@ defineExpose({ reload: load })
|
|
|
381
414
|
/>
|
|
382
415
|
<BaseCheckbox v-model="formPrintable" :label="L.printable" />
|
|
383
416
|
<BaseCheckbox v-model="formIndexable" :label="L.indexable" />
|
|
417
|
+
<BaseSelect
|
|
418
|
+
:model-value="formParent === null ? '' : String(formParent)"
|
|
419
|
+
:label="L.parent"
|
|
420
|
+
:options="[{ value: '', label: L.parentNone }, ...parentOptions]"
|
|
421
|
+
@update:model-value="(v: string) => (formParent = v ? Number(v) : null)"
|
|
422
|
+
/>
|
|
384
423
|
</details>
|
|
385
424
|
</template>
|
|
386
425
|
</EditModal>
|
|
@@ -388,7 +427,11 @@ defineExpose({ reload: load })
|
|
|
388
427
|
<!-- Acciones del bloque seleccionado, en el panel derecho (patrón kontuan) -->
|
|
389
428
|
<Teleport defer to="#right-sidebar-target">
|
|
390
429
|
<div class="manager-panel">
|
|
391
|
-
|
|
430
|
+
<!-- Sin bloque seleccionado, la vista puede poner su propio panel
|
|
431
|
+
(p. ej. las acciones de la página) -->
|
|
432
|
+
<slot v-if="!selected" name="panel-default">
|
|
433
|
+
<p class="manager-panel__empty">{{ L.panelEmpty }}</p>
|
|
434
|
+
</slot>
|
|
392
435
|
<template v-else>
|
|
393
436
|
<p class="manager-panel__kicker">{{ typeName(selected.type) }}</p>
|
|
394
437
|
|