@live-change/frontend-auto-form 0.9.61 → 0.9.63
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/front/locales/en.json +7 -0
- package/front/src/components/crud/AutoObjectIdentification.vue +46 -16
- package/front/src/components/crud/DefaultObjectIdentification.vue +85 -0
- package/front/src/components/crud/InjectedObjectIndentification.vue +1 -1
- package/front/src/components/crud/ModelEditor.vue +11 -10
- package/front/src/components/crud/ModelList.vue +43 -26
- package/front/src/components/crud/ModelSingle.vue +29 -11
- package/front/src/components/crud/ModelView.vue +22 -11
- package/front/src/components/crud/ObjectPath.vue +106 -0
- package/front/src/components/form/AutoField.vue +6 -3
- package/front/src/components/form/AutoInput.vue +2 -2
- package/front/src/components/form/GroupField.vue +2 -2
- package/front/src/components/form/ObjectInput.vue +72 -6
- package/front/src/components/form/ObjectPicker.vue +240 -2
- package/front/src/components/form/inputConfigInjection.js +5 -5
- package/front/src/components/form/provideAutoInputConfiguration.js +2 -0
- package/front/src/logic/editorData.js +25 -1
- package/front/src/logic/relations.js +71 -27
- package/index.js +4 -0
- package/package.json +13 -13
package/front/locales/en.json
CHANGED
|
@@ -12,5 +12,12 @@
|
|
|
12
12
|
},
|
|
13
13
|
"autoform": {
|
|
14
14
|
"addItem": "Add next item"
|
|
15
|
+
},
|
|
16
|
+
"objectPicker": {
|
|
17
|
+
"selectObjectType": "Select object type",
|
|
18
|
+
"currentType": "Select",
|
|
19
|
+
"fromService": "from",
|
|
20
|
+
"modelOption": "{model}",
|
|
21
|
+
"modelOptionWithService": "{service} - {model}"
|
|
15
22
|
}
|
|
16
23
|
}
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
</
|
|
5
|
-
<
|
|
6
|
-
<span>
|
|
7
|
-
<i class="pi pi-box mr-1"></i>
|
|
8
|
-
<strong>{{ objectType }}</strong>: {{ object ?? objectData.to ?? objectData.id }}
|
|
9
|
-
</span>
|
|
10
|
-
</template>
|
|
2
|
+
<router-link v-if="link" :to="viewRoute">
|
|
3
|
+
<ObjectIdentification :objectType="objectType" :object="object" :data="data" />
|
|
4
|
+
</router-link>
|
|
5
|
+
<ObjectIdentification v-else :objectType="objectType" :object="object" :data="data" />
|
|
11
6
|
</template>
|
|
12
7
|
|
|
13
8
|
<script setup>
|
|
14
9
|
|
|
15
|
-
import {
|
|
10
|
+
import { defineProps, computed, inject, toRefs } from 'vue'
|
|
11
|
+
import { injectComponent } from '@live-change/vue3-components'
|
|
16
12
|
|
|
17
13
|
const props = defineProps({
|
|
18
14
|
objectType: {
|
|
@@ -24,15 +20,14 @@
|
|
|
24
20
|
required: true
|
|
25
21
|
},
|
|
26
22
|
data: {
|
|
27
|
-
type: Object,
|
|
28
|
-
default: null
|
|
23
|
+
type: Object,
|
|
29
24
|
},
|
|
30
|
-
|
|
25
|
+
link: {
|
|
31
26
|
type: Boolean,
|
|
32
27
|
default: false
|
|
33
28
|
}
|
|
34
29
|
})
|
|
35
|
-
const { objectType, object, data,
|
|
30
|
+
const { objectType, object, data, link } = toRefs(props)
|
|
36
31
|
|
|
37
32
|
import { useApi, usePath, live } from '@live-change/vue3-ssr'
|
|
38
33
|
const api = useApi()
|
|
@@ -45,6 +40,16 @@
|
|
|
45
40
|
const service = computed(() => serviceAndModel.value.service)
|
|
46
41
|
const model = computed(() => serviceAndModel.value.model)
|
|
47
42
|
|
|
43
|
+
import DefaultObjectIdentification from './DefaultObjectIdentification.vue'
|
|
44
|
+
const ObjectIdentification = computed(() =>
|
|
45
|
+
injectComponent({
|
|
46
|
+
name: 'ObjectIdentification',
|
|
47
|
+
type: service.value + '_' + model.value,
|
|
48
|
+
service: service.value,
|
|
49
|
+
model: model.value
|
|
50
|
+
}, DefaultObjectIdentification)
|
|
51
|
+
)
|
|
52
|
+
|
|
48
53
|
const modelDefinition = computed(() => {
|
|
49
54
|
return api.services?.[service.value]?.models?.[model.value]
|
|
50
55
|
})
|
|
@@ -71,9 +76,34 @@
|
|
|
71
76
|
const loadedObjectData = await live(objectDataPath)
|
|
72
77
|
const objectData = computed(() => data.value || loadedObjectData.value)
|
|
73
78
|
|
|
79
|
+
function objectIdentifiers(object) {
|
|
80
|
+
const identifiers = {}
|
|
81
|
+
for(const identifierDefinition of modelDefinition.value.identifiers) {
|
|
82
|
+
if(typeof identifierDefinition === 'string') {
|
|
83
|
+
identifiers[identifierDefinition] = object[identifierDefinition]
|
|
84
|
+
} else {
|
|
85
|
+
if(identifierDefinition.field === 'id') {
|
|
86
|
+
identifiers[identifierDefinition.name] = object?.to ?? object.id
|
|
87
|
+
} else {
|
|
88
|
+
identifiers[identifierDefinition.name] = object[identifierDefinition.field]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return identifiers
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const viewRoute = computed(() => {
|
|
96
|
+
return {
|
|
97
|
+
name: 'auto-form:view',
|
|
98
|
+
params: {
|
|
99
|
+
serviceName: service.value,
|
|
100
|
+
modelName: model.value,
|
|
101
|
+
identifiers: Object.values(objectIdentifiers(objectData.value))
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
74
106
|
|
|
75
107
|
</script>
|
|
76
108
|
|
|
77
|
-
<style scoped>
|
|
78
109
|
|
|
79
|
-
</style>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template v-if="typeof identificationConfig === 'string'">
|
|
3
|
+
<span>
|
|
4
|
+
<i :class="[icon, 'mr-2']" style="font-size: 0.9em;"></i>{{ objectData[identificationConfig] }}
|
|
5
|
+
</span>
|
|
6
|
+
</template>
|
|
7
|
+
<template v-else>
|
|
8
|
+
<span>
|
|
9
|
+
<strong>{{ objectType }}</strong>: {{ object ?? objectData.to ?? objectData.id }}
|
|
10
|
+
</span>
|
|
11
|
+
</template>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
|
|
16
|
+
import { ref, computed, onMounted, defineProps, defineEmits, toRefs } from 'vue'
|
|
17
|
+
|
|
18
|
+
const props = defineProps({
|
|
19
|
+
objectType: {
|
|
20
|
+
type: String,
|
|
21
|
+
required: true
|
|
22
|
+
},
|
|
23
|
+
object: {
|
|
24
|
+
type: String,
|
|
25
|
+
required: true
|
|
26
|
+
},
|
|
27
|
+
data: {
|
|
28
|
+
type: Object,
|
|
29
|
+
default: null
|
|
30
|
+
},
|
|
31
|
+
inline: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
default: false
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
const { objectType, object, data, inline } = toRefs(props)
|
|
37
|
+
|
|
38
|
+
import { useApi, usePath, live } from '@live-change/vue3-ssr'
|
|
39
|
+
const api = useApi()
|
|
40
|
+
const path = usePath()
|
|
41
|
+
|
|
42
|
+
const serviceAndModel = computed(() => {
|
|
43
|
+
const [service, model] = objectType.value.split('_')
|
|
44
|
+
return { service, model }
|
|
45
|
+
})
|
|
46
|
+
const service = computed(() => serviceAndModel.value.service)
|
|
47
|
+
const model = computed(() => serviceAndModel.value.model)
|
|
48
|
+
|
|
49
|
+
const modelDefinition = computed(() => {
|
|
50
|
+
return api.services?.[service.value]?.models?.[model.value]
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const identificationViewName = computed(() => {
|
|
54
|
+
return modelDefinition.value?.crud?.identification || modelDefinition.value?.crud?.read
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const identificationConfig = computed(() => {
|
|
58
|
+
return modelDefinition.value?.identification
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const objectDataPath = computed(() => {
|
|
62
|
+
if(data.value) return null
|
|
63
|
+
if(!identificationConfig.value) return null
|
|
64
|
+
const viewName = identificationViewName.value
|
|
65
|
+
if(!viewName) return null
|
|
66
|
+
const modelName = model.value
|
|
67
|
+
return path[service.value][viewName]({
|
|
68
|
+
[modelName[0].toLowerCase() + modelName.slice(1)]: object.value
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const loadedObjectData = await live(objectDataPath)
|
|
73
|
+
const objectData = computed(() => data.value || loadedObjectData.value)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
const icon = computed(() => {
|
|
77
|
+
if(modelDefinition.value?.iconProperty) return objectData[modelDefinition.value?.iconProperty]
|
|
78
|
+
return modelDefinition.value?.icon || 'pi pi-box'
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<style scoped>
|
|
84
|
+
|
|
85
|
+
</style>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
})
|
|
46
46
|
const { type, object, data, link, inline } = toRefs(props)
|
|
47
47
|
|
|
48
|
-
import AutoObjectIdentification from './
|
|
48
|
+
import AutoObjectIdentification from './DefaultObjectIdentification.vue'
|
|
49
49
|
|
|
50
50
|
import { useApi, usePath, live } from '@live-change/vue3-ssr'
|
|
51
51
|
const api = useApi()
|
|
@@ -16,23 +16,24 @@
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
18
|
<form v-if="editor" @submit="handleSave" @reset="handleReset">
|
|
19
|
-
<div v-for="identifier in modelDefinition.identifiers">
|
|
20
|
-
<template v-if="identifier.slice(-4) !== 'Type'">
|
|
19
|
+
<div v-for="identifier in modelDefinition.identifiers">
|
|
20
|
+
<template v-if="(identifier.name ?? identifier).slice(-4) !== 'Type'">
|
|
21
21
|
<div v-if="identifiers[identifier]" class="flex flex-col mb-3">
|
|
22
|
-
<div class="min-w-[8rem] font-medium">{{ identifier }}</div>
|
|
22
|
+
<div class="min-w-[8rem] font-medium">{{ identifier.name ?? identifier }}</div>
|
|
23
23
|
<div class="">
|
|
24
24
|
<InjectedObjectIndentification
|
|
25
|
-
:type="identifiers[identifier+'Type']
|
|
26
|
-
|
|
25
|
+
:type="identifiers[(identifier.field ?? identifier)+'Type']
|
|
26
|
+
?? modelDefinition.properties[identifier.field ?? identifier].type"
|
|
27
|
+
:object="identifiers[identifier.field ?? identifier]"
|
|
27
28
|
/>
|
|
28
29
|
</div>
|
|
29
30
|
</div>
|
|
30
31
|
<div v-else class="flex flex-col mb-3">
|
|
31
32
|
<auto-field :key="identifier"
|
|
32
|
-
v-model="editor.value.value[identifier]"
|
|
33
|
-
:definition="modelDefinition.properties[identifier]"
|
|
34
|
-
:label="identifier"
|
|
35
|
-
:rootValue="props.rootValue" :propName="identifier"
|
|
33
|
+
v-model="editor.value.value[identifier.field ?? identifier]"
|
|
34
|
+
:definition="modelDefinition.properties[identifier.field ?? identifier]"
|
|
35
|
+
:label="identifier.name ?? identifier"
|
|
36
|
+
:rootValue="props.rootValue" :propName="identifier.field ?? identifier"
|
|
36
37
|
:i18n="i18n"
|
|
37
38
|
class="col-span-12" />
|
|
38
39
|
</div>
|
|
@@ -67,7 +68,7 @@
|
|
|
67
68
|
},
|
|
68
69
|
identifiers: {
|
|
69
70
|
type: Object,
|
|
70
|
-
default:
|
|
71
|
+
default: () => ({})
|
|
71
72
|
},
|
|
72
73
|
draft: {
|
|
73
74
|
type: Boolean,
|
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
</slot>
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
|
+
<!-- <pre>modelsPathRangeConfig = {{ modelsPathRangeConfig }}</pre>
|
|
22
|
+
<pre>modelsPathRangeFunctions = {{ modelsPathRangeFunctions }}</pre> -->
|
|
23
|
+
|
|
21
24
|
<div class="bg-surface-0 dark:bg-surface-900 p-4 shadow-sm rounded-border" v-if="modelsPathRangeFunctions">
|
|
22
25
|
<range-viewer v-for="(modelsPathRangeFunction, index) in modelsPathRangeFunctions"
|
|
23
26
|
:key="JSON.stringify(modelsPathRangeConfig)+index"
|
|
@@ -55,6 +58,7 @@
|
|
|
55
58
|
</template>
|
|
56
59
|
</range-viewer>
|
|
57
60
|
</div>
|
|
61
|
+
|
|
58
62
|
<div v-else class="flex items-start p-6 bg-pink-100 rounded-border border border-pink-300 mb-6">
|
|
59
63
|
<i class="pi pi-times-circle text-pink-900 text-2xl mr-4" />
|
|
60
64
|
<div class="mr-4">
|
|
@@ -113,18 +117,20 @@
|
|
|
113
117
|
type: String,
|
|
114
118
|
required: true,
|
|
115
119
|
},
|
|
116
|
-
|
|
117
|
-
type:
|
|
118
|
-
default: () => (
|
|
119
|
-
},
|
|
120
|
-
view: {
|
|
121
|
-
type: String,
|
|
122
|
-
default: undefined
|
|
120
|
+
views: {
|
|
121
|
+
type: Array,
|
|
122
|
+
default: () => ([]) // it can be array of identifiers or single identifier
|
|
123
123
|
}
|
|
124
124
|
})
|
|
125
|
-
const { service, model,
|
|
125
|
+
const { service, model, views } = toRefs(props)
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
const viewsArray = computed(() => views.value?.length ? views.value : [{
|
|
128
|
+
name: 'range',
|
|
129
|
+
identifiers: {
|
|
130
|
+
}
|
|
131
|
+
}])
|
|
132
|
+
|
|
133
|
+
import AutoObjectIdentification from './DefaultObjectIdentification.vue'
|
|
128
134
|
|
|
129
135
|
const ObjectIdentification = computed(() =>
|
|
130
136
|
injectComponent({
|
|
@@ -139,28 +145,34 @@
|
|
|
139
145
|
const api = useApi()
|
|
140
146
|
const path = usePath()
|
|
141
147
|
|
|
148
|
+
const serviceDefinition = computed(() => {
|
|
149
|
+
const serviceDefinition = api.metadata.api.value.services.find(s => s.name === service.value)
|
|
150
|
+
return serviceDefinition
|
|
151
|
+
})
|
|
152
|
+
|
|
142
153
|
const modelDefinition = computed(() => {
|
|
143
|
-
return
|
|
154
|
+
return serviceDefinition.value.models[model.value]
|
|
144
155
|
})
|
|
145
156
|
|
|
146
157
|
const modelsPathRangeConfig = computed(() => {
|
|
147
158
|
return {
|
|
148
159
|
service: service.value,
|
|
149
160
|
model: model.value,
|
|
150
|
-
definition: modelDefinition.value,
|
|
161
|
+
//definition: modelDefinition.value,
|
|
151
162
|
reverse: true,
|
|
152
|
-
|
|
163
|
+
views: viewsArray.value.map(view => ({
|
|
164
|
+
...view,
|
|
165
|
+
view: modelDefinition.value?.crud?.[view.name]
|
|
166
|
+
}))
|
|
153
167
|
}
|
|
154
168
|
})
|
|
155
169
|
const modelsPathRangeFunctions = computed(() => {
|
|
156
|
-
const config = modelsPathRangeConfig.value
|
|
157
|
-
const rangeView = config.view
|
|
170
|
+
const config = modelsPathRangeConfig.value
|
|
158
171
|
if(!path[config.service]) return null
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
...ident,
|
|
172
|
+
const views = config.views
|
|
173
|
+
const serviceViews = serviceDefinition.value.views
|
|
174
|
+
return views.map(view => (range) => serviceViews[view.view] && path[config.service][view.view]({
|
|
175
|
+
...view.identifiers,
|
|
164
176
|
...(config.reverse ? reverseRange(range) : range),
|
|
165
177
|
}))
|
|
166
178
|
})
|
|
@@ -203,14 +215,19 @@
|
|
|
203
215
|
}
|
|
204
216
|
}
|
|
205
217
|
|
|
206
|
-
const createRoute = computed(() =>
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
218
|
+
const createRoute = computed(() => {
|
|
219
|
+
const identifiersObject = viewsArray?.value[0]?.identifiers
|
|
220
|
+
if(!identifiersObject) return null
|
|
221
|
+
const identifiers = Object.values(identifiersObject)
|
|
222
|
+
return {
|
|
223
|
+
name: 'auto-form:editor',
|
|
224
|
+
params: {
|
|
225
|
+
serviceName: service.value,
|
|
226
|
+
modelName: model.value,
|
|
227
|
+
identifiers
|
|
228
|
+
}
|
|
212
229
|
}
|
|
213
|
-
})
|
|
230
|
+
})
|
|
214
231
|
|
|
215
232
|
function deleteObject(event, object) {
|
|
216
233
|
confirm.require({
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
</slot>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
+
<!-- <pre>{{ modelDefinition }}</pre> -->
|
|
19
|
+
|
|
18
20
|
<div class="bg-surface-0 dark:bg-surface-900 p-4 shadow-sm rounded-border" v-if="modelsPaths">
|
|
19
21
|
<div v-for="({ value: object }, index) in (modelsData ?? [])">
|
|
20
22
|
<div v-if="!object" class="text-xl text-surface-800 dark:text-surface-50 my-1 mx-4">
|
|
@@ -105,14 +107,14 @@
|
|
|
105
107
|
type: String,
|
|
106
108
|
required: true,
|
|
107
109
|
},
|
|
108
|
-
|
|
110
|
+
views: {
|
|
109
111
|
type: Object,
|
|
110
112
|
default: () => ({})
|
|
111
113
|
},
|
|
112
114
|
})
|
|
113
|
-
const { service, model,
|
|
115
|
+
const { service, model, views } = toRefs(props)
|
|
114
116
|
|
|
115
|
-
import AutoObjectIdentification from './
|
|
117
|
+
import AutoObjectIdentification from './DefaultObjectIdentification.vue'
|
|
116
118
|
|
|
117
119
|
const ObjectIdentification = computed(() =>
|
|
118
120
|
injectComponent({
|
|
@@ -127,8 +129,13 @@
|
|
|
127
129
|
const api = useApi()
|
|
128
130
|
const path = usePath()
|
|
129
131
|
|
|
132
|
+
const serviceDefinition = computed(() => {
|
|
133
|
+
const serviceDefinition = api.metadata.api.value.services.find(s => s.name === service.value)
|
|
134
|
+
return serviceDefinition
|
|
135
|
+
})
|
|
136
|
+
|
|
130
137
|
const modelDefinition = computed(() => {
|
|
131
|
-
return
|
|
138
|
+
return serviceDefinition.value.models[model.value]
|
|
132
139
|
})
|
|
133
140
|
|
|
134
141
|
const modelsPathConfig = computed(() => {
|
|
@@ -138,15 +145,26 @@
|
|
|
138
145
|
definition: modelDefinition.value,
|
|
139
146
|
}
|
|
140
147
|
})
|
|
148
|
+
|
|
149
|
+
const modelsPathRangeConfig = computed(() => {
|
|
150
|
+
return {
|
|
151
|
+
service: service.value,
|
|
152
|
+
model: model.value,
|
|
153
|
+
//definition: modelDefinition.value,
|
|
154
|
+
reverse: true,
|
|
155
|
+
views: views.value.map(view => ({
|
|
156
|
+
...view,
|
|
157
|
+
view: modelDefinition.value?.crud?.[view.name]
|
|
158
|
+
}))
|
|
159
|
+
}
|
|
160
|
+
})
|
|
141
161
|
const modelsPaths = computed(() => {
|
|
142
|
-
const config =
|
|
143
|
-
const readView = config.definition?.crud?.read
|
|
162
|
+
const config = modelsPathRangeConfig.value
|
|
144
163
|
if(!path[config.service]) return null
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
...ident
|
|
164
|
+
const views = config.views
|
|
165
|
+
const serviceViews = serviceDefinition.value.views
|
|
166
|
+
return views.map(view => serviceViews[view.view] && path[config.service][view.view]({
|
|
167
|
+
...view.identifiers,
|
|
150
168
|
}))
|
|
151
169
|
})
|
|
152
170
|
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
<h4>object</h4>
|
|
11
11
|
<pre>{{ object }}</pre>-->
|
|
12
12
|
|
|
13
|
+
<ScopePath :objectType="service + '_' + model" :object="object.to ?? object.id" class="mb-6" />
|
|
14
|
+
|
|
13
15
|
<div v-if="object">
|
|
14
16
|
<div class="bg-surface-0 dark:bg-surface-900 p-4 shadow-sm rounded-border mb-6">
|
|
15
17
|
|
|
@@ -36,7 +38,7 @@
|
|
|
36
38
|
|
|
37
39
|
<div v-for="preparedRelation of visibleObjectRelations" class="mb-6">
|
|
38
40
|
<ModelSingle :service="preparedRelation.service" :model="preparedRelation.model"
|
|
39
|
-
|
|
41
|
+
:views="preparedRelation.views">
|
|
40
42
|
<template #header>
|
|
41
43
|
<div class="text-xl">
|
|
42
44
|
<ObjectIdentification
|
|
@@ -54,7 +56,7 @@
|
|
|
54
56
|
|
|
55
57
|
<div v-for="preparedRelation of visibleRangeRelations" class="mb-6">
|
|
56
58
|
<ModelList :service="preparedRelation.service" :model="preparedRelation.model"
|
|
57
|
-
:
|
|
59
|
+
:views="preparedRelation.views">
|
|
58
60
|
<template #header>
|
|
59
61
|
<div class="text-xl">
|
|
60
62
|
<ObjectIdentification
|
|
@@ -91,7 +93,8 @@
|
|
|
91
93
|
|
|
92
94
|
<div class="bg-surface-0 dark:bg-surface-900 p-4 shadow-sm rounded-border">
|
|
93
95
|
|
|
94
|
-
<pre>{{
|
|
96
|
+
<pre>visibleRangeRelations = {{ visibleRangeRelations }}</pre>
|
|
97
|
+
<pre>preparedRelations = {{ preparedRelations }}</pre>
|
|
95
98
|
|
|
96
99
|
<div v-if="backwardRelations">
|
|
97
100
|
<h4>Backward relations</h4>
|
|
@@ -102,9 +105,11 @@
|
|
|
102
105
|
}}</pre>
|
|
103
106
|
</div>
|
|
104
107
|
|
|
108
|
+
|
|
109
|
+
<pre>accessControlRoles = {{ accessControlRoles }}</pre>
|
|
110
|
+
|
|
105
111
|
</div>
|
|
106
112
|
|
|
107
|
-
<pre>{{ accessControlRoles }}</pre>
|
|
108
113
|
|
|
109
114
|
</div>
|
|
110
115
|
</template>
|
|
@@ -122,6 +127,8 @@
|
|
|
122
127
|
import { ref, computed, onMounted, defineProps, defineEmits, toRefs } from 'vue'
|
|
123
128
|
import { RangeViewer, injectComponent, InjectComponent } from "@live-change/vue3-components"
|
|
124
129
|
|
|
130
|
+
import ScopePath from './ObjectPath.vue'
|
|
131
|
+
|
|
125
132
|
const props = defineProps({
|
|
126
133
|
service: {
|
|
127
134
|
type: String,
|
|
@@ -148,7 +155,7 @@
|
|
|
148
155
|
|
|
149
156
|
const emit = defineEmits(['saved', 'draftSaved', 'draftDiscarded', 'saveError', 'created' ])
|
|
150
157
|
|
|
151
|
-
import AutoObjectIdentification from './
|
|
158
|
+
import AutoObjectIdentification from './DefaultObjectIdentification.vue'
|
|
152
159
|
const ObjectIdentification = computed(() =>
|
|
153
160
|
injectComponent({
|
|
154
161
|
name: 'ObjectIdentification',
|
|
@@ -166,7 +173,8 @@
|
|
|
166
173
|
return api.services?.[service.value]?.models?.[model.value]
|
|
167
174
|
})
|
|
168
175
|
|
|
169
|
-
import { getForwardRelations, getBackwardRelations, anyRelationsTypes, prepareObjectRelations }
|
|
176
|
+
import { getForwardRelations, getBackwardRelations, anyRelationsTypes, prepareObjectRelations }
|
|
177
|
+
from '../../logic/relations.js'
|
|
170
178
|
const forwardRelations = computed(() => getForwardRelations(modelDefinition.value, () => true, api))
|
|
171
179
|
const backwardRelations = computed(() => getBackwardRelations(modelDefinition.value, api))
|
|
172
180
|
|
|
@@ -192,11 +200,14 @@
|
|
|
192
200
|
return prepareObjectRelations(objectType.value, object.value.to ?? object.value.id, api)
|
|
193
201
|
})
|
|
194
202
|
|
|
195
|
-
const visibleRangeRelations = computed(() => preparedRelations.value.
|
|
196
|
-
|
|
197
|
-
if(
|
|
198
|
-
return
|
|
199
|
-
|
|
203
|
+
const visibleRangeRelations = computed(() => preparedRelations.value.map(preparedRelation => {
|
|
204
|
+
const accessibleViews = preparedRelation.views.filter(view => preparedRelation.access.value[view.name])
|
|
205
|
+
if(accessibleViews.length === 0) return null
|
|
206
|
+
return {
|
|
207
|
+
...preparedRelation,
|
|
208
|
+
views: accessibleViews
|
|
209
|
+
}
|
|
210
|
+
}).filter(x => x !== null))
|
|
200
211
|
|
|
201
212
|
const visibleObjectRelations = computed(() => preparedRelations.value.filter(preparedRelation => {
|
|
202
213
|
if(!preparedRelation.singular) return false
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<!-- <h1>Scope Path</h1>
|
|
4
|
+
<pre>pathsPath = {{ pathsPath }}</pre>
|
|
5
|
+
<pre>paths = {{ paths }}</pre>
|
|
6
|
+
<pre>selectedPaths = {{ selectedPaths }}</pre>
|
|
7
|
+
<pre>selectedPathWithElements = {{ selectedPathWithElements }}</pre> -->
|
|
8
|
+
<div v-for="path in selectedPathsWithElements" :key="path">
|
|
9
|
+
<Breadcrumb :model="path">
|
|
10
|
+
<template #item="{ item }">
|
|
11
|
+
<AutoObjectIdentification :objectType="item.objectType" :object="item.object"
|
|
12
|
+
:link="item.object !== object && item.objectType !== objectType" />
|
|
13
|
+
</template>
|
|
14
|
+
</Breadcrumb>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup>
|
|
20
|
+
|
|
21
|
+
import Breadcrumb from 'primevue/breadcrumb'
|
|
22
|
+
import AutoObjectIdentification from './AutoObjectIdentification.vue'
|
|
23
|
+
import { ref, computed, onMounted, defineProps, toRefs, inject } from 'vue'
|
|
24
|
+
const props = defineProps({
|
|
25
|
+
objectType: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: true
|
|
28
|
+
},
|
|
29
|
+
object: {
|
|
30
|
+
type: String,
|
|
31
|
+
required: true
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
const { objectType, object } = toRefs(props)
|
|
35
|
+
|
|
36
|
+
import { usePath, live } from '@live-change/vue3-ssr'
|
|
37
|
+
const path = usePath()
|
|
38
|
+
|
|
39
|
+
function longest(scopePaths) {
|
|
40
|
+
return scopePaths.sort((a, b) => b.intermediate.length - a.intermediate.length).at(0)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function longestByType(scopePaths) {
|
|
44
|
+
const scopePathsByType = new Map()
|
|
45
|
+
for(const scopePath of scopePaths) {
|
|
46
|
+
const type = scopePath.scopeType
|
|
47
|
+
if(!scopePathsByType.has(type)) scopePathsByType.set(type, [])
|
|
48
|
+
scopePathsByType.get(type).push(scopePath)
|
|
49
|
+
}
|
|
50
|
+
const result = []
|
|
51
|
+
for(const type of scopePathsByType.keys()) {
|
|
52
|
+
result.push(longest(scopePathsByType.get(type)))
|
|
53
|
+
}
|
|
54
|
+
return result
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function scopePathElements(path) {
|
|
58
|
+
const {objectType, object, scopeType, scope, intermediate} = path
|
|
59
|
+
const root = { objectType: scopeType, object: scope, }
|
|
60
|
+
const elements = [root]
|
|
61
|
+
for(let i = 0; i < intermediate.length; i+=2) {
|
|
62
|
+
const propertyName = intermediate[i]
|
|
63
|
+
elements[elements.length - 1].toProperty = propertyName
|
|
64
|
+
const objectInfo = intermediate[i+1]
|
|
65
|
+
if(objectInfo) {
|
|
66
|
+
const separatorPosition = objectInfo.indexOf(':')
|
|
67
|
+
const objectType = JSON.parse(objectInfo.substring(0, separatorPosition))
|
|
68
|
+
const object = JSON.parse(objectInfo.substring(separatorPosition + 1))
|
|
69
|
+
elements.push({ objectType, object, propertyFrom: propertyName })
|
|
70
|
+
} else {
|
|
71
|
+
elements.push({ objectType, object, propertyFrom: propertyName })
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return elements
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const scopePathConfig = inject('scopePathConfig', {
|
|
78
|
+
scopeType: undefined,
|
|
79
|
+
scope: undefined,
|
|
80
|
+
scopeSelector: longestByType,
|
|
81
|
+
pathsPath: (objectType, object, config) => path.scope.objectScopePaths({
|
|
82
|
+
objectType, object,
|
|
83
|
+
scopeType: config.scopeType,
|
|
84
|
+
scope: config.scope
|
|
85
|
+
}),
|
|
86
|
+
pathElements: scopePathElements
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const pathsPath = computed(() => scopePathConfig.pathsPath(objectType.value, object.value, scopePathConfig))
|
|
90
|
+
|
|
91
|
+
const [paths] = await Promise.all([
|
|
92
|
+
live(pathsPath)
|
|
93
|
+
])
|
|
94
|
+
|
|
95
|
+
const selectedPaths = computed(() => {
|
|
96
|
+
return scopePathConfig.scopeSelector(paths.value)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const selectedPathsWithElements = computed(
|
|
100
|
+
() => selectedPaths.value.map(scopePath => scopePathConfig.pathElements(scopePath))
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
</script>
|
|
106
|
+
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
|
|
104
104
|
const definitionIf = computed(() => {
|
|
105
105
|
if(definition.value?.if) {
|
|
106
|
-
if(definition.value?.if.function) {
|
|
106
|
+
if(definition.value?.if.function) {
|
|
107
107
|
return eval(`(${definition.value.if.function})`)
|
|
108
108
|
} else {
|
|
109
109
|
throw new Error('Unknown if type' + JSON.stringify(definition.value.if))
|
|
@@ -114,6 +114,9 @@
|
|
|
114
114
|
|
|
115
115
|
const visible = computed(() => {
|
|
116
116
|
if(!definition.value) return false
|
|
117
|
+
if(definition.value.type === 'type' && props.propName.endsWith('Type') && !definition.value.showType) {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
117
120
|
if(definitionIf.value) {
|
|
118
121
|
return definitionIf.value({
|
|
119
122
|
source: definition.value,
|
|
@@ -158,8 +161,8 @@
|
|
|
158
161
|
&& props.modelValue?.length < minLengthValidation.value.length
|
|
159
162
|
)
|
|
160
163
|
|
|
161
|
-
import {
|
|
162
|
-
const inputConfig = computed(() =>
|
|
164
|
+
import { injectInputConfigByDefinition } from './inputConfigInjection.js'
|
|
165
|
+
const inputConfig = computed(() => injectInputConfigByDefinition(definition.value))
|
|
163
166
|
|
|
164
167
|
const label = computed(() => props.i18n + (props.label || definition.value.label || props.name))
|
|
165
168
|
|