@live-change/frontend-auto-form 0.9.7 → 0.9.8
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/src/components/crud/AutoObjectIdentification.vue +79 -0
- package/front/src/components/crud/EditorButtons.vue +17 -19
- package/front/src/components/crud/ModelEditor.vue +37 -6
- package/front/src/components/crud/ModelList.vue +147 -0
- package/front/src/logic/editorData.js +13 -12
- package/front/src/pages/Editor.vue +27 -15
- package/front/src/pages/List.vue +3 -13
- package/front/src/pages/Models.vue +15 -3
- package/front/src/router.js +7 -0
- package/package.json +13 -13
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template v-if="typeof identificationConfig === 'string'">
|
|
3
|
+
<span><i class="pi pi-box mr-1"></i>{{ objectData[identificationConfig] }}</span>
|
|
4
|
+
</template>
|
|
5
|
+
<template v-else>
|
|
6
|
+
<span>
|
|
7
|
+
<i class="pi pi-box mr-1"></i>
|
|
8
|
+
<strong>{{ objectType }}</strong>: {{ object ?? objectData.to ?? objectData.id }}
|
|
9
|
+
</span>
|
|
10
|
+
</template>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup>
|
|
14
|
+
|
|
15
|
+
import { ref, computed, onMounted, defineProps, defineEmits, toRefs } from 'vue'
|
|
16
|
+
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
objectType: {
|
|
19
|
+
type: String,
|
|
20
|
+
required: true
|
|
21
|
+
},
|
|
22
|
+
object: {
|
|
23
|
+
type: String,
|
|
24
|
+
required: true
|
|
25
|
+
},
|
|
26
|
+
data: {
|
|
27
|
+
type: Object,
|
|
28
|
+
default: null
|
|
29
|
+
},
|
|
30
|
+
inline: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: false
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
const { objectType, object, data, inline } = toRefs(props)
|
|
36
|
+
|
|
37
|
+
import { useApi, usePath, live } from '@live-change/vue3-ssr'
|
|
38
|
+
const api = useApi()
|
|
39
|
+
const path = usePath()
|
|
40
|
+
|
|
41
|
+
const serviceAndModel = computed(() => {
|
|
42
|
+
const [service, model] = objectType.value.split('_')
|
|
43
|
+
return { service, model }
|
|
44
|
+
})
|
|
45
|
+
const service = computed(() => serviceAndModel.value.service)
|
|
46
|
+
const model = computed(() => serviceAndModel.value.model)
|
|
47
|
+
|
|
48
|
+
const modelDefinition = computed(() => {
|
|
49
|
+
return api.services?.[service.value]?.models?.[model.value]
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const identificationViewName = computed(() => {
|
|
53
|
+
return modelDefinition.value?.crud?.identification || modelDefinition.value?.crud?.read
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const identificationConfig = computed(() => {
|
|
57
|
+
return modelDefinition.value?.identification
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const objectDataPath = computed(() => {
|
|
61
|
+
if(data.value) return null
|
|
62
|
+
if(!identificationConfig.value) return null
|
|
63
|
+
const viewName = identificationViewName.value
|
|
64
|
+
if(!viewName) return null
|
|
65
|
+
const modelName = model.value
|
|
66
|
+
return path[service.value][viewName]({
|
|
67
|
+
[modelName[0].toLowerCase() + modelName.slice(1)]: object.value
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const loadedObjectData = await live(objectDataPath)
|
|
72
|
+
const objectData = computed(() => data.value || loadedObjectData.value)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<style scoped>
|
|
78
|
+
|
|
79
|
+
</style>
|
|
@@ -1,32 +1,30 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="flex flex-row justify-content-between align-items-center mt-3">
|
|
3
|
-
<div class="flex flex-column">
|
|
4
|
-
<div v-if="
|
|
5
|
-
|
|
6
|
-
</div>
|
|
7
|
-
<div v-if="savingDraft" class="text-500 mr-2">
|
|
8
|
-
<i class="pi pi-spin pi-spinner" style="font-size: 2rem"></i>
|
|
2
|
+
<div class="flex flex-column-reverse md:flex-row justify-content-between align-items-center mt-3">
|
|
3
|
+
<div class="flex flex-column mt-2 md:mt-0">
|
|
4
|
+
<div v-if="savingDraft" class="text-500 mr-2 flex flex-row align-items-center">
|
|
5
|
+
<i class="pi pi-spin pi-spinner mr-2" style="font-size: 1.23rem"></i>
|
|
9
6
|
<span>Saving draft...</span>
|
|
10
7
|
</div>
|
|
11
|
-
<div v-if="
|
|
8
|
+
<div v-else-if="draftChanged" class="text-sm text-500 mr-2">
|
|
9
|
+
Draft changed
|
|
10
|
+
</div>
|
|
11
|
+
<div v-else-if="validationResult" class="font-semibold p-error mr-2">
|
|
12
12
|
Before saving, please correct the errors above.
|
|
13
13
|
</div>
|
|
14
|
+
<div v-else-if="!changed" class="">
|
|
15
|
+
No changes to save.
|
|
16
|
+
</div>
|
|
14
17
|
</div>
|
|
15
18
|
<div class="flex flex-row">
|
|
16
19
|
<slot name="submit" v-if="!validationResult">
|
|
17
|
-
<div
|
|
18
|
-
<Button type="submit" label="Save"
|
|
19
|
-
|
|
20
|
-
<div v-else class="flex flex-row align-items-center ml-2">
|
|
21
|
-
<div class="mr-2">
|
|
22
|
-
No changes to save.
|
|
23
|
-
</div>
|
|
24
|
-
<Button type="submit" label="Save" class="" icon="pi pi-save" disabled />
|
|
20
|
+
<div class="ml-2">
|
|
21
|
+
<Button v-if="exists" type="submit" :label="'Save '+model.name" :disabled="!changed" icon="pi pi-save" />
|
|
22
|
+
<Button v-else type="submit" :label="'Create '+model.name" :disabled="!changed" icon="pi pi-sparkles" />
|
|
25
23
|
</div>
|
|
26
24
|
</slot>
|
|
27
25
|
<slot name="reset" v-if="resetButton">
|
|
28
|
-
<div
|
|
29
|
-
<Button label="Reset" class="ml-2" icon="pi pi-eraser"
|
|
26
|
+
<div>
|
|
27
|
+
<Button type="reset" label="Reset" class="ml-2" :disabled="!changed" icon="pi pi-eraser"/>
|
|
30
28
|
</div>
|
|
31
29
|
</slot>
|
|
32
30
|
</div>
|
|
@@ -61,7 +59,7 @@
|
|
|
61
59
|
const model = computed(() => editor.value.model)
|
|
62
60
|
|
|
63
61
|
const changed = computed(() => editor.value.changed.value)
|
|
64
|
-
|
|
62
|
+
const exists = computed(() => !!editor.value.saved.value)
|
|
65
63
|
const draftChanged = computed(() => editor.value.draftChanged?.value)
|
|
66
64
|
const savingDraft = computed(() => editor.value.savingDraft?.value)
|
|
67
65
|
const sourceChanged = computed(() => editor.value.sourceChanged?.value)
|
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
|
|
3
|
+
<!--
|
|
4
|
+
<h4>identifiers as object</h4>
|
|
5
|
+
<pre>{{ identifiersObject }}</pre>
|
|
6
|
+
|
|
7
|
+
<h4>definition</h4>
|
|
8
|
+
<pre>{{ modelDefinition }}</pre>-->
|
|
9
|
+
|
|
10
|
+
<div class="">
|
|
11
|
+
Service <strong>{{ service }}</strong>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="text-2xl mb-4">
|
|
14
|
+
<span v-if="isNew">Create </span>
|
|
15
|
+
<span v-else>Edit </span>
|
|
16
|
+
<strong>{{ model }}</strong>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<form v-if="editor" @submit="handleSave" @reset="handleReset">
|
|
4
20
|
<auto-editor
|
|
5
21
|
:definition="modelDefinition"
|
|
6
22
|
v-model="editor.value.value"
|
|
7
23
|
:rootValue="editor.value.value"
|
|
8
24
|
:i18n="i18n" />
|
|
9
25
|
<EditorButtons :editor="editor" reset-button />
|
|
10
|
-
</
|
|
26
|
+
</form>
|
|
11
27
|
</div>
|
|
12
28
|
</template>
|
|
13
29
|
|
|
@@ -68,7 +84,7 @@
|
|
|
68
84
|
draft: draft.value,
|
|
69
85
|
autoSave: true,
|
|
70
86
|
...options.value,
|
|
71
|
-
onSaved: (...args) =>
|
|
87
|
+
onSaved: (...args) => handleSaved(...args),
|
|
72
88
|
onDraftSaved: (...args) => emit('draftSaved', ...args),
|
|
73
89
|
onDraftDiscarded: (...args) => emit('draftDiscarded', ...args),
|
|
74
90
|
onSaveError: (...args) => emit('saveError', ...args),
|
|
@@ -82,10 +98,25 @@
|
|
|
82
98
|
}
|
|
83
99
|
})
|
|
84
100
|
|
|
85
|
-
const isNew = computed(() => editor
|
|
101
|
+
const isNew = computed(() => !editor?.value?.saved?.value)
|
|
102
|
+
|
|
103
|
+
function handleSave(ev) {
|
|
104
|
+
ev.preventDefault()
|
|
105
|
+
editor.value.save()
|
|
106
|
+
}
|
|
86
107
|
|
|
87
|
-
|
|
88
|
-
|
|
108
|
+
function handleReset(ev) {
|
|
109
|
+
ev.preventDefault()
|
|
110
|
+
editor.value.reset()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function handleSaved(saveResult) {
|
|
114
|
+
console.log("SAVED", saveResult, isNew.value, editor.value.isNew)
|
|
115
|
+
if(saveResult && isNew.value && editor.value.isNew) {
|
|
116
|
+
emit('created', saveResult)
|
|
117
|
+
}
|
|
118
|
+
emit('saved', saveResult)
|
|
119
|
+
}
|
|
89
120
|
|
|
90
121
|
</script>
|
|
91
122
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="w-full lg:w-8 md:w-11">
|
|
3
|
+
<!-- <h4>definition</h4>
|
|
4
|
+
<pre>{{ modelDefinition }}</pre>-->
|
|
5
|
+
|
|
6
|
+
<div class="surface-card w-full p-3 shadow-1 border-round mb-2">
|
|
7
|
+
<div class="">
|
|
8
|
+
Service <strong>{{ service }}</strong>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="text-2xl">
|
|
11
|
+
<strong>{{ pluralize(model) }}</strong>
|
|
12
|
+
<span class="ml-1">list</span>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="surface-card p-3 shadow-1 border-round">
|
|
17
|
+
<range-viewer :key="JSON.stringify(modelsPathRangeConfig)"
|
|
18
|
+
:pathFunction="modelsPathRangeFunction"
|
|
19
|
+
:canLoadTop="false" canDropBottom
|
|
20
|
+
loadBottomSensorSize="4000px" dropBottomSensorSize="3000px">
|
|
21
|
+
<template #empty>
|
|
22
|
+
<div class="text-xl text-800 my-3 mx-3">
|
|
23
|
+
No {{ pluralize(model) }} found
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
<template #default="{ item: object }">
|
|
27
|
+
<div class="flex flex-row align-items-center justify-content-between">
|
|
28
|
+
<ObjectIdentification
|
|
29
|
+
:objectType="service + '_' + model"
|
|
30
|
+
:object="object.to ?? object.id"
|
|
31
|
+
:data="object"
|
|
32
|
+
/>
|
|
33
|
+
<div class="flex flex-row">
|
|
34
|
+
<router-link :to="editRoute(object)" class="no-underline">
|
|
35
|
+
<Button icon="pi pi-pencil" severity="primary" label="Edit" class="mr-2" />
|
|
36
|
+
</router-link>
|
|
37
|
+
|
|
38
|
+
<Button icon="pi pi-eraser" severity="primary" label="Delete" class="mr-2" />
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</template>
|
|
42
|
+
</range-viewer>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="mt-3 flex flex-row justify-content-end mr-2">
|
|
46
|
+
<router-link :to="createRoute" class="no-underline2">
|
|
47
|
+
<Button icon="pi pi-plus" :label="'Create new '+model" />
|
|
48
|
+
</router-link>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script setup>
|
|
56
|
+
|
|
57
|
+
import { ref, computed, onMounted, defineProps, toRefs } from 'vue'
|
|
58
|
+
import { RangeViewer, injectComponent } from "@live-change/vue3-components"
|
|
59
|
+
import pluralize from 'pluralize'
|
|
60
|
+
|
|
61
|
+
const props = defineProps({
|
|
62
|
+
service: {
|
|
63
|
+
type: String,
|
|
64
|
+
required: true,
|
|
65
|
+
},
|
|
66
|
+
model: {
|
|
67
|
+
type: String,
|
|
68
|
+
required: true,
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
const { service, model } = toRefs(props)
|
|
72
|
+
|
|
73
|
+
import AutoObjectIdentification from './AutoObjectIdentification.vue'
|
|
74
|
+
|
|
75
|
+
const ObjectIdentification = computed(() =>
|
|
76
|
+
injectComponent({
|
|
77
|
+
name: 'ObjectIdentification',
|
|
78
|
+
type: service.value + '_' + model.value,
|
|
79
|
+
service: service.value,
|
|
80
|
+
model: model.value
|
|
81
|
+
}, AutoObjectIdentification)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
import { useApi, usePath, live, reverseRange } from '@live-change/vue3-ssr'
|
|
85
|
+
const api = useApi()
|
|
86
|
+
const path = usePath()
|
|
87
|
+
|
|
88
|
+
const modelDefinition = computed(() => {
|
|
89
|
+
return api.services?.[service.value]?.models?.[model.value]
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const modelsPathRangeConfig = computed(() => {
|
|
93
|
+
return {
|
|
94
|
+
service: service.value,
|
|
95
|
+
model: model.value,
|
|
96
|
+
definition: modelDefinition.value,
|
|
97
|
+
reverse: true
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
const modelsPathRangeFunction = computed(() => {
|
|
101
|
+
const config = modelsPathRangeConfig.value
|
|
102
|
+
const rangeView = config.definition?.crud?.range
|
|
103
|
+
return (range) => path[config.service][rangeView]({
|
|
104
|
+
...(config.reverse ? reverseRange(range) : range),
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
function objectIdentifiers(object) {
|
|
109
|
+
const identifiers = {}
|
|
110
|
+
for(const identifierDefinition of modelDefinition.value.identifiers) {
|
|
111
|
+
if(typeof identifierDefinition === 'string') {
|
|
112
|
+
identifiers[identifierDefinition] = object[identifierDefinition]
|
|
113
|
+
} else {
|
|
114
|
+
if(identifierDefinition.field === 'id') {
|
|
115
|
+
identifiers[identifierDefinition.name] = object?.to ?? object.id
|
|
116
|
+
} else {
|
|
117
|
+
identifiers[identifierDefinition.name] = object[identifierDefinition.field]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return identifiers
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function editRoute(object) {
|
|
125
|
+
return {
|
|
126
|
+
name: 'auto-form:editor',
|
|
127
|
+
params: {
|
|
128
|
+
serviceName: service.value,
|
|
129
|
+
modelName: model.value,
|
|
130
|
+
identifiers: Object.values(objectIdentifiers(object))
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const createRoute = computed(() => ({
|
|
136
|
+
name: 'auto-form:editor',
|
|
137
|
+
params: {
|
|
138
|
+
serviceName: service.value,
|
|
139
|
+
modelName: model.name
|
|
140
|
+
}
|
|
141
|
+
}))
|
|
142
|
+
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
<style scoped>
|
|
146
|
+
|
|
147
|
+
</style>
|
|
@@ -59,12 +59,8 @@ export default function editorData(options) {
|
|
|
59
59
|
let draftIdParts = []
|
|
60
60
|
for(const identifier of identifiersNames) {
|
|
61
61
|
if(typeof identifier === 'object') {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
idKey = key
|
|
65
|
-
}
|
|
66
|
-
draftIdParts.push(value)
|
|
67
|
-
}
|
|
62
|
+
if(identifier.field === 'id') idKey = identifier.name
|
|
63
|
+
draftIdParts.push('id')
|
|
68
64
|
} else {
|
|
69
65
|
draftIdParts.push(identifier)
|
|
70
66
|
}
|
|
@@ -110,6 +106,9 @@ export default function editorData(options) {
|
|
|
110
106
|
saving.value = true
|
|
111
107
|
savePromise = (async () => {
|
|
112
108
|
try {
|
|
109
|
+
if(createOrUpdateAction) {
|
|
110
|
+
return createOrUpdateAction(requestData)
|
|
111
|
+
}
|
|
113
112
|
if(savedData.value) {
|
|
114
113
|
return updateAction(requestData)
|
|
115
114
|
} else {
|
|
@@ -124,7 +123,7 @@ export default function editorData(options) {
|
|
|
124
123
|
})()
|
|
125
124
|
if(!autoSave && workingZone)
|
|
126
125
|
workingZone.addPromise('save:'+serviceName+':'+modelName, savePromise.catch(() => {}))
|
|
127
|
-
await savePromise
|
|
126
|
+
return await savePromise
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
if(draft) {
|
|
@@ -140,6 +139,7 @@ export default function editorData(options) {
|
|
|
140
139
|
autoSave: true,
|
|
141
140
|
debounce,
|
|
142
141
|
timeField,
|
|
142
|
+
isNew,
|
|
143
143
|
resetOnError: false,
|
|
144
144
|
onSave: () => {
|
|
145
145
|
onDraftSaved()
|
|
@@ -158,9 +158,9 @@ export default function editorData(options) {
|
|
|
158
158
|
JSON.stringify(draftData.value.from) !== JSON.stringify(editableSavedData.value))
|
|
159
159
|
|
|
160
160
|
async function save() {
|
|
161
|
-
await saveData(synchronizedData.value.value)
|
|
161
|
+
const saveResult = await saveData(synchronizedData.value.value)
|
|
162
162
|
if(draftData.value) await removeDraftAction(draftIdentifiers)
|
|
163
|
-
onSaved()
|
|
163
|
+
onSaved(saveResult)
|
|
164
164
|
if(toast && savedToast) toast.add({ severity: 'success', summary: savedToast, life: 1500 })
|
|
165
165
|
}
|
|
166
166
|
|
|
@@ -191,6 +191,7 @@ export default function editorData(options) {
|
|
|
191
191
|
reset,
|
|
192
192
|
discardDraft,
|
|
193
193
|
model,
|
|
194
|
+
isNew,
|
|
194
195
|
resetOnError: false,
|
|
195
196
|
draftChanged: synchronizedData.changed,
|
|
196
197
|
saveDraft: synchronizedData.save,
|
|
@@ -202,14 +203,14 @@ export default function editorData(options) {
|
|
|
202
203
|
} else {
|
|
203
204
|
const synchronizedData = synchronized({
|
|
204
205
|
source,
|
|
205
|
-
update:
|
|
206
|
+
update: saveData,
|
|
206
207
|
updateDataProperty,
|
|
207
208
|
identifiers,
|
|
208
209
|
recursive,
|
|
209
210
|
autoSave,
|
|
210
211
|
debounce,
|
|
211
|
-
onSave: () => {
|
|
212
|
-
onSaved()
|
|
212
|
+
onSave: (result) => {
|
|
213
|
+
onSaved(result)
|
|
213
214
|
if(toast && savedToast) toast.add({ severity: 'success', summary: savedToast, life: 1500 })
|
|
214
215
|
},
|
|
215
216
|
onSaveError(e) {
|
|
@@ -2,17 +2,8 @@
|
|
|
2
2
|
<div class="w-full lg:w-8 md:w-11">
|
|
3
3
|
<div class="surface-card p-3 shadow-1 border-round">
|
|
4
4
|
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
</div>
|
|
8
|
-
|
|
9
|
-
<h4>identifiers as object</h4>
|
|
10
|
-
<pre>{{ identifiersObject }}</pre>
|
|
11
|
-
|
|
12
|
-
<h4>definition</h4>
|
|
13
|
-
<pre>{{ modelDefinition }}</pre>
|
|
14
|
-
|
|
15
|
-
<ModelEditor :service="serviceName" :model="modelName" :identifiers="identifiersObject" draft />
|
|
5
|
+
<ModelEditor :service="serviceName" :model="modelName" :identifiers="identifiersObject" draft
|
|
6
|
+
@created="handleCreated"/>
|
|
16
7
|
|
|
17
8
|
</div>
|
|
18
9
|
</div>
|
|
@@ -22,6 +13,8 @@
|
|
|
22
13
|
|
|
23
14
|
import ModelEditor from "../components/crud/ModelEditor.vue"
|
|
24
15
|
|
|
16
|
+
|
|
17
|
+
|
|
25
18
|
import { ref, computed, onMounted, defineProps, toRefs } from 'vue'
|
|
26
19
|
|
|
27
20
|
const props = defineProps({
|
|
@@ -59,15 +52,34 @@
|
|
|
59
52
|
if(typeof identifierDefinition === 'string') {
|
|
60
53
|
result[identifierDefinition] = identifier
|
|
61
54
|
} else {
|
|
62
|
-
result[
|
|
55
|
+
result[identifierDefinition.name] = identifier
|
|
63
56
|
}
|
|
64
57
|
}
|
|
65
58
|
return result
|
|
66
59
|
})
|
|
67
60
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
61
|
+
import { useRouter } from 'vue-router'
|
|
62
|
+
const router = useRouter()
|
|
63
|
+
|
|
64
|
+
function handleCreated(id) {
|
|
65
|
+
const newIdentifiers = modelDefinition.value.identifiers.map((identifier, i) => {
|
|
66
|
+
if(typeof identifier === 'object' && identifier.field === 'id') {
|
|
67
|
+
return id
|
|
68
|
+
}
|
|
69
|
+
return identifiers.value[i]
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
//console.log("newIdentifiers", newIdentifiers)
|
|
73
|
+
if(JSON.stringify(identifiers.value) !== JSON.stringify(newIdentifiers)) {
|
|
74
|
+
router.push({
|
|
75
|
+
name: 'auto-form:editor',
|
|
76
|
+
params: {
|
|
77
|
+
serviceName: serviceName.value,
|
|
78
|
+
modelName: modelName.value,
|
|
79
|
+
identifiers: newIdentifiers
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
71
83
|
}
|
|
72
84
|
|
|
73
85
|
</script>
|
package/front/src/pages/List.vue
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<div class="surface-card p-3 shadow-1 border-round">
|
|
4
|
-
List
|
|
5
|
-
</div>
|
|
6
|
-
</div>
|
|
2
|
+
<ModelList :service="serviceName" :model="modelName" />
|
|
7
3
|
</template>
|
|
8
4
|
|
|
9
5
|
<script setup>
|
|
6
|
+
import ModelList from "../components/crud/ModelList.vue"
|
|
10
7
|
|
|
11
8
|
import { ref, computed, onMounted, defineProps, toRefs } from 'vue'
|
|
12
9
|
|
|
@@ -18,21 +15,14 @@
|
|
|
18
15
|
modelName: {
|
|
19
16
|
type: String,
|
|
20
17
|
required: true,
|
|
21
|
-
},
|
|
22
|
-
identifiers: {
|
|
23
|
-
type: Array,
|
|
24
|
-
default: []
|
|
25
18
|
}
|
|
26
19
|
})
|
|
27
|
-
const { serviceName } = toRefs(props)
|
|
20
|
+
const { serviceName, modelName } = toRefs(props)
|
|
28
21
|
|
|
29
22
|
import { useApi, usePath, live } from '@live-change/vue3-ssr'
|
|
30
23
|
const api = useApi()
|
|
31
24
|
const path = usePath()
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
26
|
</script>
|
|
37
27
|
|
|
38
28
|
<style scoped>
|
|
@@ -10,14 +10,16 @@
|
|
|
10
10
|
<div class="text-xl flex flex-row align-items-center mr-4">
|
|
11
11
|
<strong>{{ model.name }}</strong>
|
|
12
12
|
<span class="mx-1">model</span>
|
|
13
|
-
<div v-for="relation of model.relations"
|
|
13
|
+
<div v-for="relation of model.relations">
|
|
14
14
|
<span class="mr-1">-</span>
|
|
15
15
|
<span>{{ relation.name }}</span>
|
|
16
16
|
<!-- <span v-if="relation.config.what"></span>-->
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
19
|
-
<div>
|
|
20
|
-
<
|
|
19
|
+
<div class="mt-2 md:mt-0">
|
|
20
|
+
<router-link :to="listRoute(serviceWithModels.name, model)" class="no-underline">
|
|
21
|
+
<Button icon="pi pi-list" severity="primary" label="List" class="mr-2" />
|
|
22
|
+
</router-link>
|
|
21
23
|
<router-link :to="createRoute(serviceWithModels.name, model)" class="no-underline">
|
|
22
24
|
<Button icon="pi pi-plus" severity="warning" :label="'Create new '+model.name" />
|
|
23
25
|
</router-link>
|
|
@@ -79,6 +81,16 @@
|
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
function listRoute(serviceName, model) {
|
|
85
|
+
return {
|
|
86
|
+
name: 'auto-form:list',
|
|
87
|
+
params: {
|
|
88
|
+
serviceName,
|
|
89
|
+
modelName: model.name,
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
82
94
|
</script>
|
|
83
95
|
|
|
84
96
|
<style scoped>
|
package/front/src/router.js
CHANGED
|
@@ -14,6 +14,13 @@ export function autoFormRoutes(config = {}) {
|
|
|
14
14
|
props: true
|
|
15
15
|
}),
|
|
16
16
|
|
|
17
|
+
route({
|
|
18
|
+
name: 'auto-form:list', path: prefix + '/models/:serviceName/:modelName', meta: { },
|
|
19
|
+
component: () => import("./pages/List.vue"),
|
|
20
|
+
props: true
|
|
21
|
+
}),
|
|
22
|
+
|
|
23
|
+
|
|
17
24
|
]
|
|
18
25
|
}
|
|
19
26
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/frontend-auto-form",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.8",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"memDev": "node server/start.js memDev --enableSessions --initScript ./init.js --dbAccess",
|
|
6
6
|
"localDevInit": "rm tmp.db; lcli localDev --enableSessions --initScript ./init.js",
|
|
@@ -22,16 +22,16 @@
|
|
|
22
22
|
"type": "module",
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@fortawesome/fontawesome-free": "^6.5.2",
|
|
25
|
-
"@live-change/cli": "^0.9.
|
|
26
|
-
"@live-change/dao": "^0.9.
|
|
27
|
-
"@live-change/dao-vue3": "^0.9.
|
|
28
|
-
"@live-change/dao-websocket": "^0.9.
|
|
29
|
-
"@live-change/framework": "^0.9.
|
|
30
|
-
"@live-change/image-frontend": "^0.9.
|
|
31
|
-
"@live-change/image-service": "^0.9.
|
|
32
|
-
"@live-change/session-service": "^0.9.
|
|
33
|
-
"@live-change/vue3-components": "^0.9.
|
|
34
|
-
"@live-change/vue3-ssr": "^0.9.
|
|
25
|
+
"@live-change/cli": "^0.9.8",
|
|
26
|
+
"@live-change/dao": "^0.9.8",
|
|
27
|
+
"@live-change/dao-vue3": "^0.9.8",
|
|
28
|
+
"@live-change/dao-websocket": "^0.9.8",
|
|
29
|
+
"@live-change/framework": "^0.9.8",
|
|
30
|
+
"@live-change/image-frontend": "^0.9.8",
|
|
31
|
+
"@live-change/image-service": "^0.9.8",
|
|
32
|
+
"@live-change/session-service": "^0.9.8",
|
|
33
|
+
"@live-change/vue3-components": "^0.9.8",
|
|
34
|
+
"@live-change/vue3-ssr": "^0.9.8",
|
|
35
35
|
"@vueuse/core": "^10.11.0",
|
|
36
36
|
"codeceptjs-assert": "^0.0.5",
|
|
37
37
|
"compression": "^1.7.4",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"vue3-scroll-border": "0.1.6"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@live-change/codeceptjs-helper": "^0.9.
|
|
55
|
+
"@live-change/codeceptjs-helper": "^0.9.8",
|
|
56
56
|
"codeceptjs": "^3.6.5",
|
|
57
57
|
"generate-password": "1.7.1",
|
|
58
58
|
"playwright": "1.48.1",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"author": "Michał Łaszczewski <michal@laszczewski.pl>",
|
|
64
64
|
"license": "ISC",
|
|
65
65
|
"description": "",
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "bb09550504f28df5f4fdd73273de45dc91a857b5"
|
|
67
67
|
}
|