@alstar/studio 0.0.0-beta.5 → 0.0.0-beta.7
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/api/api-key.ts +74 -0
- package/api/block.ts +82 -42
- package/api/index.ts +9 -1
- package/api/mcp.ts +53 -0
- package/components/AdminPanel.ts +74 -0
- package/components/BlockFieldRenderer.ts +121 -0
- package/components/BlockRenderer.ts +22 -0
- package/components/Entries.ts +5 -4
- package/components/Entry.ts +13 -21
- package/components/FieldRenderer.ts +35 -0
- package/components/Render.ts +46 -0
- package/components/Settings.ts +98 -0
- package/components/{layout.ts → SiteLayout.ts} +9 -13
- package/components/fields/Markdown.ts +44 -0
- package/components/fields/Text.ts +42 -0
- package/components/fields/index.ts +4 -0
- package/components/icons.ts +59 -0
- package/index.ts +52 -30
- package/package.json +3 -2
- package/pages/entry/[id].ts +17 -0
- package/{components → pages}/index.ts +7 -4
- package/pages/settings.ts +10 -0
- package/public/studio/admin-panel.css +103 -0
- package/public/studio/blocks.css +53 -0
- package/public/studio/main.css +162 -0
- package/public/studio/main.js +10 -0
- package/public/studio/markdown-editor.js +34 -0
- package/public/studio/settings.css +24 -0
- package/public/studio/sortable-list.js +40 -0
- package/queries/block-with-children.ts +74 -0
- package/queries/block.ts +31 -40
- package/queries/db-types.ts +15 -0
- package/queries/getBlockTrees-2.ts +0 -0
- package/queries/getBlockTrees.ts +316 -0
- package/queries/getBlocks.ts +214 -0
- package/queries/index.ts +1 -0
- package/queries/structure-types.ts +97 -0
- package/schemas.ts +14 -3
- package/types.ts +123 -6
- package/utils/buildBlocksTree.ts +4 -4
- package/utils/define.ts +31 -5
- package/utils/file-based-router.ts +1 -0
- package/utils/get-or-create-row.ts +41 -0
- package/utils/startup-log.ts +9 -0
- package/components/AdminPanel/AdminPanel.css +0 -78
- package/components/AdminPanel/AdminPanel.ts +0 -57
- package/components/Block.ts +0 -116
- package/components/Field.ts +0 -168
- package/components/Fields.ts +0 -43
- package/public/main.css +0 -95
- package/public/main.js +0 -44
- /package/{components/Entry.css → public/studio/entry.css} +0 -0
- /package/public/{favicon.svg → studio/favicon.svg} +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.blocks-field {
|
|
2
|
+
> header {
|
|
3
|
+
display: flex;
|
|
4
|
+
justify-content: space-between;
|
|
5
|
+
align-items: center;
|
|
6
|
+
|
|
7
|
+
> form fieldset {
|
|
8
|
+
button {
|
|
9
|
+
padding-inline: 1rem;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
details {
|
|
14
|
+
form {
|
|
15
|
+
display: flex;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
button {
|
|
19
|
+
width: 100%;
|
|
20
|
+
margin-bottom: 0;
|
|
21
|
+
text-align: left;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
summary + ul {
|
|
25
|
+
right: 0;
|
|
26
|
+
left: auto;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
article {
|
|
32
|
+
> header {
|
|
33
|
+
display: flex;
|
|
34
|
+
justify-content: space-between;
|
|
35
|
+
align-items: center;
|
|
36
|
+
|
|
37
|
+
aside {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: 5px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
button {
|
|
44
|
+
margin: 0;
|
|
45
|
+
padding: 0;
|
|
46
|
+
|
|
47
|
+
svg {
|
|
48
|
+
padding: 5px;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/* @import './../node_modules/@alstar/ui/red.css'; */
|
|
2
|
+
@import 'https://esm.sh/@alstar/ui/red.css';
|
|
3
|
+
@import './admin-panel.css';
|
|
4
|
+
@import './entry.css';
|
|
5
|
+
@import './blocks.css';
|
|
6
|
+
@import './settings.css';
|
|
7
|
+
|
|
8
|
+
body {
|
|
9
|
+
padding: 0;
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
|
|
12
|
+
display: grid;
|
|
13
|
+
grid-template-columns: auto 1fr;
|
|
14
|
+
align-content: baseline;
|
|
15
|
+
|
|
16
|
+
> main {
|
|
17
|
+
padding: 40px;
|
|
18
|
+
height: 100vh;
|
|
19
|
+
overflow: auto;
|
|
20
|
+
|
|
21
|
+
section {
|
|
22
|
+
max-width: 900px;
|
|
23
|
+
margin: 0 auto;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.entry > .fields > .block {
|
|
29
|
+
padding-block: var(--pico-spacing);
|
|
30
|
+
|
|
31
|
+
> header {
|
|
32
|
+
margin-block: var(--pico-block-spacing-vertical);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.block {
|
|
37
|
+
> header {
|
|
38
|
+
margin-bottom: var(--pico-spacing);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
header {
|
|
42
|
+
display: flex;
|
|
43
|
+
justify-content: space-between;
|
|
44
|
+
align-items: center;
|
|
45
|
+
|
|
46
|
+
background-color: inherit;
|
|
47
|
+
|
|
48
|
+
h6,
|
|
49
|
+
h5,
|
|
50
|
+
fieldset {
|
|
51
|
+
margin-bottom: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
button {
|
|
55
|
+
padding: 0;
|
|
56
|
+
background-color: white;
|
|
57
|
+
|
|
58
|
+
svg {
|
|
59
|
+
padding: 8px;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
[data-sortable] {
|
|
66
|
+
> article {
|
|
67
|
+
transition: opacity 200ms;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.sortable-ghost {
|
|
71
|
+
opacity: 0.3;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
div.ink-mde {
|
|
76
|
+
border: var(--pico-border-width) solid #2a3140;
|
|
77
|
+
border-radius: var(--pico-border-radius);
|
|
78
|
+
outline: 0;
|
|
79
|
+
background-color: var(--pico-background-color);
|
|
80
|
+
box-shadow: var(--pico-box-shadow);
|
|
81
|
+
color: var(--pico-color);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.ͼ1 .cm-content.ink-mde-editor-content {
|
|
85
|
+
min-height: 10lh;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
div.ink-mde {
|
|
89
|
+
--pico-border-color: var(--pico-form-element-border-color);
|
|
90
|
+
|
|
91
|
+
border: var(--pico-border-width) solid var(--pico-border-color);
|
|
92
|
+
border-radius: var(--pico-border-radius);
|
|
93
|
+
|
|
94
|
+
padding: var(--pico-form-element-spacing-vertical)
|
|
95
|
+
var(--pico-form-element-spacing-horizontal);
|
|
96
|
+
margin-bottom: var(--pico-spacing);
|
|
97
|
+
|
|
98
|
+
--pico-background-color: var(--pico-form-element-background-color);
|
|
99
|
+
|
|
100
|
+
--pico-color: var(--pico-form-element-color);
|
|
101
|
+
--pico-box-shadow: none;
|
|
102
|
+
|
|
103
|
+
outline: 0;
|
|
104
|
+
background-color: var(--pico-background-color);
|
|
105
|
+
box-shadow: var(--pico-box-shadow);
|
|
106
|
+
color: var(--pico-color);
|
|
107
|
+
font-weight: var(--pico-font-weight);
|
|
108
|
+
transition:
|
|
109
|
+
background-color var(--pico-transition),
|
|
110
|
+
border-color var(--pico-transition),
|
|
111
|
+
color var(--pico-transition),
|
|
112
|
+
box-shadow var(--pico-transition);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.ink-mde-textarea {
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
div.ink-mde:has(.cm-focused) {
|
|
119
|
+
--pico-outline-width: 0.1rem;
|
|
120
|
+
--pico-box-shadow: 0 0 0 var(--pico-outline-width)
|
|
121
|
+
var(--pico-form-element-focus-color);
|
|
122
|
+
|
|
123
|
+
box-shadow: var(--pico-box-shadow);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.ͼ1.cm-focused {
|
|
127
|
+
outline: none;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.ink-mde-textarea {
|
|
131
|
+
width: 100%;
|
|
132
|
+
|
|
133
|
+
--pico-background-color: var(--pico-form-element-background-color);
|
|
134
|
+
|
|
135
|
+
--pico-color: var(--pico-form-element-color);
|
|
136
|
+
--pico-box-shadow: none;
|
|
137
|
+
|
|
138
|
+
outline: 0;
|
|
139
|
+
background-color: var(--pico-background-color);
|
|
140
|
+
box-shadow: var(--pico-box-shadow);
|
|
141
|
+
color: var(--pico-color);
|
|
142
|
+
font-weight: var(--pico-font-weight);
|
|
143
|
+
transition:
|
|
144
|
+
background-color var(--pico-transition),
|
|
145
|
+
border-color var(--pico-transition),
|
|
146
|
+
color var(--pico-transition),
|
|
147
|
+
box-shadow var(--pico-transition);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.disclamer {
|
|
151
|
+
display: flex;
|
|
152
|
+
justify-content: center;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.text-secondary {
|
|
156
|
+
color: var(--pico-secondary);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.markdown {
|
|
160
|
+
width: 100%;
|
|
161
|
+
height: 10lh;
|
|
162
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ink } from 'ink-mde'
|
|
2
|
+
|
|
3
|
+
class MarkdownEditor extends HTMLElement {
|
|
4
|
+
instance = null
|
|
5
|
+
|
|
6
|
+
connectedCallback() {
|
|
7
|
+
this.style.width = '100%'
|
|
8
|
+
|
|
9
|
+
this.instance = ink(this, {
|
|
10
|
+
hooks: {
|
|
11
|
+
afterUpdate: async (e) => {
|
|
12
|
+
await fetch('/admin/api/value', {
|
|
13
|
+
method: 'PATCH',
|
|
14
|
+
body: JSON.stringify({
|
|
15
|
+
value: e,
|
|
16
|
+
id: this.dataset.id,
|
|
17
|
+
}),
|
|
18
|
+
})
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
interface: {
|
|
22
|
+
attribution: false,
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
this.instance.update(this.dataset.content)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
disconnectedCallback() {
|
|
30
|
+
// this.instance.destroy()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
customElements.define('markdown-editor', MarkdownEditor)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#settings {
|
|
2
|
+
ul {
|
|
3
|
+
padding: 0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
li {
|
|
7
|
+
width: 100%;
|
|
8
|
+
display: grid;
|
|
9
|
+
align-items: center;
|
|
10
|
+
grid-template-columns: 15% 1fr auto;
|
|
11
|
+
gap: 1rem;
|
|
12
|
+
border-bottom: 1px solid var(--pico-form-element-border-color);
|
|
13
|
+
margin-bottom: 0;
|
|
14
|
+
padding: 1rem 0;
|
|
15
|
+
|
|
16
|
+
input {
|
|
17
|
+
font-family: var(--pico-font-family-monospace);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
* {
|
|
21
|
+
margin-bottom: 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import Sortable from 'sortablejs'
|
|
2
|
+
|
|
3
|
+
class SortableList extends HTMLElement {
|
|
4
|
+
instance = null
|
|
5
|
+
|
|
6
|
+
connectedCallback() {
|
|
7
|
+
if (!this.children.length) return
|
|
8
|
+
|
|
9
|
+
const { id } = this.dataset
|
|
10
|
+
|
|
11
|
+
this.instance = Sortable.create(this, {
|
|
12
|
+
animation: 250,
|
|
13
|
+
handle: `[data-handle-for="${id}"]`,
|
|
14
|
+
onEnd: (e) => {
|
|
15
|
+
const items = [...e.target.children]
|
|
16
|
+
|
|
17
|
+
items.forEach(async (child, idx) => {
|
|
18
|
+
const searchParams = new URLSearchParams()
|
|
19
|
+
|
|
20
|
+
searchParams.set('id', child.dataset.id)
|
|
21
|
+
searchParams.set('sort-order', idx)
|
|
22
|
+
|
|
23
|
+
await fetch(`/admin/api/sort-order?${searchParams.toString()}`, {
|
|
24
|
+
method: 'post',
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
this.querySelectorAll('[name="sort_order"]').forEach((input, idx) => {
|
|
29
|
+
input.value = idx.toString()
|
|
30
|
+
})
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
disconnectedCallback() {
|
|
36
|
+
this.instance?.destroy()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
customElements.define('sortable-list', SortableList)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { sql } from '../utils/sql.ts'
|
|
2
|
+
|
|
3
|
+
export const blockWithChildren = sql`
|
|
4
|
+
with recursive
|
|
5
|
+
block_tree as (
|
|
6
|
+
-- Start from the root block you want
|
|
7
|
+
select
|
|
8
|
+
id,
|
|
9
|
+
name,
|
|
10
|
+
label,
|
|
11
|
+
type,
|
|
12
|
+
sort_order,
|
|
13
|
+
value,
|
|
14
|
+
options,
|
|
15
|
+
status,
|
|
16
|
+
parent_id,
|
|
17
|
+
0 as depth
|
|
18
|
+
from
|
|
19
|
+
blocks
|
|
20
|
+
where
|
|
21
|
+
id = ? -- <-- put your starting block id here
|
|
22
|
+
union all
|
|
23
|
+
-- Recursively select children
|
|
24
|
+
select
|
|
25
|
+
b.id,
|
|
26
|
+
b.name,
|
|
27
|
+
b.label,
|
|
28
|
+
b.type,
|
|
29
|
+
b.sort_order,
|
|
30
|
+
b.value,
|
|
31
|
+
b.options,
|
|
32
|
+
b.status,
|
|
33
|
+
b.parent_id,
|
|
34
|
+
bt.depth + 1
|
|
35
|
+
from
|
|
36
|
+
blocks b
|
|
37
|
+
inner join block_tree bt on b.parent_id = bt.id
|
|
38
|
+
)
|
|
39
|
+
select
|
|
40
|
+
*
|
|
41
|
+
from
|
|
42
|
+
block_tree
|
|
43
|
+
order by
|
|
44
|
+
depth,
|
|
45
|
+
sort_order;
|
|
46
|
+
`
|
|
47
|
+
|
|
48
|
+
export const deleteBlockWithChildren = sql`
|
|
49
|
+
with recursive
|
|
50
|
+
block_tree as (
|
|
51
|
+
-- start from the root block you want to delete
|
|
52
|
+
select
|
|
53
|
+
id
|
|
54
|
+
from
|
|
55
|
+
blocks
|
|
56
|
+
where
|
|
57
|
+
id = ? -- <-- put your root block id here
|
|
58
|
+
union all
|
|
59
|
+
-- recursively select children
|
|
60
|
+
select
|
|
61
|
+
b.id
|
|
62
|
+
from
|
|
63
|
+
blocks b
|
|
64
|
+
inner join block_tree bt on b.parent_id = bt.id
|
|
65
|
+
)
|
|
66
|
+
delete from blocks
|
|
67
|
+
where
|
|
68
|
+
id in (
|
|
69
|
+
select
|
|
70
|
+
id
|
|
71
|
+
from
|
|
72
|
+
block_tree
|
|
73
|
+
);
|
|
74
|
+
`
|
package/queries/block.ts
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
import { db } from '@alstar/db'
|
|
2
2
|
import { sql } from '../utils/sql.ts'
|
|
3
|
-
|
|
4
|
-
type DBBlockResult = {
|
|
5
|
-
id: number
|
|
6
|
-
created_at: string
|
|
7
|
-
updated_at: string
|
|
8
|
-
name: string
|
|
9
|
-
label: string
|
|
10
|
-
type: string
|
|
11
|
-
sort_order: number
|
|
12
|
-
value: string | null
|
|
13
|
-
options: any
|
|
14
|
-
status: string
|
|
15
|
-
parent_block_id: number | null
|
|
16
|
-
depth: number
|
|
17
|
-
children: DBBlockResult[]
|
|
18
|
-
fields: Record<string, DBBlockResult>
|
|
19
|
-
}
|
|
3
|
+
import { type DBBlockResult } from '../types.ts'
|
|
20
4
|
|
|
21
5
|
function buildForest(blocks: DBBlockResult[]): DBBlockResult[] {
|
|
22
6
|
const map = new Map<number, DBBlockResult>()
|
|
@@ -28,10 +12,10 @@ function buildForest(blocks: DBBlockResult[]): DBBlockResult[] {
|
|
|
28
12
|
}
|
|
29
13
|
|
|
30
14
|
for (const block of blocks) {
|
|
31
|
-
if (block.
|
|
15
|
+
if (block.parent_id === null) {
|
|
32
16
|
roots.push(block)
|
|
33
17
|
} else {
|
|
34
|
-
const parent = map.get(block.
|
|
18
|
+
const parent = map.get(block.parent_id)
|
|
35
19
|
if (parent) parent.children!.push(block)
|
|
36
20
|
}
|
|
37
21
|
}
|
|
@@ -56,10 +40,10 @@ function buildTree(blocks: DBBlockResult[]): DBBlockResult {
|
|
|
56
40
|
}
|
|
57
41
|
|
|
58
42
|
for (const block of blocks) {
|
|
59
|
-
if (block.
|
|
43
|
+
if (block.parent_id === null) {
|
|
60
44
|
roots.push(block)
|
|
61
45
|
} else {
|
|
62
|
-
const parent = map.get(block.
|
|
46
|
+
const parent = map.get(block.parent_id)
|
|
63
47
|
if (parent) parent.children!.push(block)
|
|
64
48
|
}
|
|
65
49
|
}
|
|
@@ -74,35 +58,40 @@ function buildTree(blocks: DBBlockResult[]): DBBlockResult {
|
|
|
74
58
|
return roots[0]
|
|
75
59
|
}
|
|
76
60
|
|
|
77
|
-
function
|
|
61
|
+
function transformBlocksTree(
|
|
78
62
|
block: DBBlockResult,
|
|
79
63
|
isBlocksChild?: boolean,
|
|
80
64
|
): DBBlockResult {
|
|
81
65
|
const fields: Record<string, DBBlockResult> = {}
|
|
66
|
+
let hasFields = false
|
|
82
67
|
|
|
83
68
|
for (const child of block.children ?? []) {
|
|
84
|
-
const transformedChild =
|
|
69
|
+
const transformedChild = transformBlocksTree(
|
|
85
70
|
child,
|
|
86
71
|
child.type === 'blocks',
|
|
87
72
|
)
|
|
88
73
|
|
|
89
|
-
if (isBlocksChild) {
|
|
90
|
-
|
|
74
|
+
if (!isBlocksChild) {
|
|
75
|
+
hasFields = true
|
|
91
76
|
fields[transformedChild.name] = transformedChild
|
|
92
77
|
}
|
|
93
78
|
}
|
|
94
79
|
|
|
95
|
-
|
|
96
|
-
|
|
80
|
+
if(hasFields) {
|
|
81
|
+
block.fields = fields
|
|
82
|
+
}
|
|
83
|
+
|
|
97
84
|
if (!isBlocksChild) {
|
|
98
|
-
block.children
|
|
85
|
+
delete block.children
|
|
86
|
+
} else {
|
|
87
|
+
delete block.fields
|
|
99
88
|
}
|
|
100
89
|
|
|
101
90
|
return block
|
|
102
91
|
}
|
|
103
92
|
|
|
104
93
|
function transformForest(blocks: DBBlockResult[]): DBBlockResult[] {
|
|
105
|
-
return blocks.map((block) =>
|
|
94
|
+
return blocks.map((block) => transformBlocksTree(block))
|
|
106
95
|
}
|
|
107
96
|
|
|
108
97
|
function rootQuery(filterSql: string, depthLimit?: number) {
|
|
@@ -123,7 +112,7 @@ function rootQuery(filterSql: string, depthLimit?: number) {
|
|
|
123
112
|
value,
|
|
124
113
|
options,
|
|
125
114
|
status,
|
|
126
|
-
|
|
115
|
+
parent_id,
|
|
127
116
|
0 as depth
|
|
128
117
|
from
|
|
129
118
|
blocks
|
|
@@ -141,11 +130,11 @@ function rootQuery(filterSql: string, depthLimit?: number) {
|
|
|
141
130
|
b.value,
|
|
142
131
|
b.options,
|
|
143
132
|
b.status,
|
|
144
|
-
b.
|
|
133
|
+
b.parent_id,
|
|
145
134
|
a.depth + 1
|
|
146
135
|
from
|
|
147
136
|
blocks b
|
|
148
|
-
inner join ancestors a on b.id = a.
|
|
137
|
+
inner join ancestors a on b.id = a.parent_id
|
|
149
138
|
),
|
|
150
139
|
roots as (
|
|
151
140
|
select
|
|
@@ -159,12 +148,12 @@ function rootQuery(filterSql: string, depthLimit?: number) {
|
|
|
159
148
|
value,
|
|
160
149
|
options,
|
|
161
150
|
status,
|
|
162
|
-
|
|
151
|
+
parent_id,
|
|
163
152
|
0 as depth
|
|
164
153
|
from
|
|
165
154
|
ancestors
|
|
166
155
|
where
|
|
167
|
-
|
|
156
|
+
parent_id is null
|
|
168
157
|
),
|
|
169
158
|
descendants as (
|
|
170
159
|
select
|
|
@@ -178,7 +167,7 @@ function rootQuery(filterSql: string, depthLimit?: number) {
|
|
|
178
167
|
value,
|
|
179
168
|
options,
|
|
180
169
|
status,
|
|
181
|
-
|
|
170
|
+
parent_id,
|
|
182
171
|
depth
|
|
183
172
|
from
|
|
184
173
|
roots
|
|
@@ -194,19 +183,19 @@ function rootQuery(filterSql: string, depthLimit?: number) {
|
|
|
194
183
|
b.value,
|
|
195
184
|
b.options,
|
|
196
185
|
b.status,
|
|
197
|
-
b.
|
|
186
|
+
b.parent_id,
|
|
198
187
|
d.depth + 1
|
|
199
188
|
from
|
|
200
189
|
blocks b
|
|
201
|
-
inner join descendants d on b.
|
|
190
|
+
inner join descendants d on b.parent_id = d.id ${depthLimitClause}
|
|
202
191
|
)
|
|
203
192
|
select
|
|
204
193
|
*
|
|
205
194
|
from
|
|
206
195
|
descendants
|
|
207
196
|
order by
|
|
208
|
-
|
|
209
|
-
|
|
197
|
+
sort_order,
|
|
198
|
+
id;
|
|
210
199
|
`
|
|
211
200
|
}
|
|
212
201
|
|
|
@@ -264,7 +253,7 @@ export function root(
|
|
|
264
253
|
|
|
265
254
|
const tree = buildTree(rows)
|
|
266
255
|
|
|
267
|
-
return
|
|
256
|
+
return transformBlocksTree(tree)
|
|
268
257
|
}
|
|
269
258
|
|
|
270
259
|
export function block(params: Record<string, any>) {
|
|
@@ -292,6 +281,8 @@ export function blocks(params: Record<string, any>) {
|
|
|
292
281
|
blocks
|
|
293
282
|
where
|
|
294
283
|
${filterSql}
|
|
284
|
+
order by
|
|
285
|
+
sort_order
|
|
295
286
|
`
|
|
296
287
|
|
|
297
288
|
return db.database.prepare(query).all(sqlParams) as unknown as DBBlockResult[]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// db-types.ts
|
|
2
|
+
export type DBBase = {
|
|
3
|
+
id: number;
|
|
4
|
+
created_at: string;
|
|
5
|
+
updated_at: string;
|
|
6
|
+
name: string;
|
|
7
|
+
label: string;
|
|
8
|
+
type: string;
|
|
9
|
+
sort_order: number;
|
|
10
|
+
value: string | null;
|
|
11
|
+
options: any | null;
|
|
12
|
+
status: string;
|
|
13
|
+
parent_id: number | null;
|
|
14
|
+
depth: number;
|
|
15
|
+
};
|
|
File without changes
|