@live-change/frontend-auto-form 0.9.77 → 0.9.78
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/ActionButtons.vue +80 -0
- package/front/src/components/crud/ActionForm.vue +102 -0
- package/front/src/components/crud/EditorButtons.vue +6 -11
- package/front/src/components/crud/ModelEditor.vue +1 -0
- package/front/src/components/form/ArrayInput.vue +6 -1
- package/front/src/components/form/AutoEditor.vue +8 -4
- package/front/src/components/form/AutoField.vue +9 -8
- package/front/src/components/form/AutoInput.vue +6 -0
- package/front/src/components/form/GroupField.vue +6 -1
- package/front/src/logic/actionData.js +230 -0
- package/front/src/logic/editorData.js +37 -32
- package/front/src/logic/utils.js +20 -0
- package/front/src/logic/validation.js +45 -0
- package/front/src/pages/Action.vue +45 -0
- package/front/src/pages/Actions.vue +72 -0
- package/front/src/router.js +12 -0
- package/index.js +2 -0
- package/package.json +13 -13
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col-reverse md:flex-row justify-between items-center">
|
|
3
|
+
<div class="flex flex-col mt-2 md:mt-0">
|
|
4
|
+
<div v-if="savingDraft" class="text-surface-500 dark:text-surface-300 mr-2 flex flex-row items-center">
|
|
5
|
+
<i class="pi pi-spin pi-spinner mr-2" style="font-size: 1.23rem"></i>
|
|
6
|
+
<span>Executing...</span>
|
|
7
|
+
</div>
|
|
8
|
+
<div v-else-if="draftChanged" class="text-sm text-surface-500 dark:text-surface-300 mr-2">
|
|
9
|
+
Draft changed
|
|
10
|
+
</div>
|
|
11
|
+
<Message v-else-if="validationResult" severity="error" variant="simple" size="small" class="mr-2">
|
|
12
|
+
Before running, please correct the errors above.
|
|
13
|
+
</Message>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="flex flex-row">
|
|
16
|
+
<slot name="submit" v-if="!validationResult">
|
|
17
|
+
<div class="ml-2">
|
|
18
|
+
<Button
|
|
19
|
+
type="submit"
|
|
20
|
+
:label="submitting === true ? 'Executing...' : 'Execute'"
|
|
21
|
+
:icon="submitting === true ? 'pi pi-spin pi-spinner' : 'pi pi-play'"
|
|
22
|
+
:disabled="submitting"
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
</slot>
|
|
26
|
+
<slot name="reset" v-if="resetButton">
|
|
27
|
+
<div>
|
|
28
|
+
<Button type="reset" label="Reset" class="ml-2" :disabled="!changed" icon="pi pi-eraser"/>
|
|
29
|
+
</div>
|
|
30
|
+
</slot>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup>
|
|
36
|
+
|
|
37
|
+
import Message from "primevue/message"
|
|
38
|
+
|
|
39
|
+
import { ref, computed, onMounted, defineProps, defineEmits, toRefs, getCurrentInstance, unref } from 'vue'
|
|
40
|
+
|
|
41
|
+
const props = defineProps({
|
|
42
|
+
actionFormData: {
|
|
43
|
+
type: Object,
|
|
44
|
+
required: true,
|
|
45
|
+
},
|
|
46
|
+
resetButton: {
|
|
47
|
+
type: Boolean,
|
|
48
|
+
required: true,
|
|
49
|
+
},
|
|
50
|
+
options: {
|
|
51
|
+
type: Object,
|
|
52
|
+
default: () => ({})
|
|
53
|
+
},
|
|
54
|
+
i18n: {
|
|
55
|
+
type: String,
|
|
56
|
+
default: ''
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
const { actionFormData, resetButton, options, i18n } = toRefs(props)
|
|
60
|
+
|
|
61
|
+
const changed = computed(() => unref(actionFormData).changed.value)
|
|
62
|
+
const draftChanged = computed(() => unref(actionFormData).draftChanged?.value)
|
|
63
|
+
const savingDraft = computed(() => unref(actionFormData).savingDraft?.value)
|
|
64
|
+
const submitting = computed(() => unref(actionFormData).submitting?.value)
|
|
65
|
+
const propertiesErrors = computed(() => unref(actionFormData).propertiesErrors?.value)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
const validationResult = computed(() => {
|
|
69
|
+
const errors = propertiesErrors.value
|
|
70
|
+
if(errors && Object.keys(errors).length > 0) {
|
|
71
|
+
return errors
|
|
72
|
+
}
|
|
73
|
+
return null
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<style scoped>
|
|
79
|
+
|
|
80
|
+
</style>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="actionFormData">
|
|
3
|
+
<div class="text-xl mb-2">
|
|
4
|
+
Service <strong>{{ service }}</strong>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="text-2xl mb-6">
|
|
7
|
+
Action <strong>{{ action }}</strong>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<form @submit="handleSubmit" @reset="handleReset">
|
|
11
|
+
<div class="flex flex-col gap-4">
|
|
12
|
+
<auto-editor
|
|
13
|
+
:definition="actionFormData.action.definition"
|
|
14
|
+
v-model="actionFormData.value"
|
|
15
|
+
:rootValue="actionFormData.value"
|
|
16
|
+
:errors="actionFormData.propertiesErrors"
|
|
17
|
+
:i18n="i18n"
|
|
18
|
+
/>
|
|
19
|
+
|
|
20
|
+
<ActionButtons
|
|
21
|
+
:actionFormData="actionFormData"
|
|
22
|
+
:resetButton="true"
|
|
23
|
+
:i18n="i18n"
|
|
24
|
+
/>
|
|
25
|
+
|
|
26
|
+
<!-- <div class="flex justify-end mt-4 gap-2">
|
|
27
|
+
<Button
|
|
28
|
+
type="reset"
|
|
29
|
+
:label="'Reset'"
|
|
30
|
+
:icon="'pi pi-times'"
|
|
31
|
+
:disabled="actionFormData.submitting"
|
|
32
|
+
severity="danger"
|
|
33
|
+
/>
|
|
34
|
+
<Button
|
|
35
|
+
type="submit"
|
|
36
|
+
:label="actionFormData.submitting === true ? 'Executing...' : 'Execute'"
|
|
37
|
+
:icon="actionFormData.submitting === true ? 'pi pi-spin pi-spinner' : 'pi pi-play'"
|
|
38
|
+
:disabled="actionFormData.submitting"
|
|
39
|
+
severity="success"
|
|
40
|
+
/>
|
|
41
|
+
</div> -->
|
|
42
|
+
</div>
|
|
43
|
+
</form>
|
|
44
|
+
|
|
45
|
+
<!-- <pre>propertiesErrors = {{ actionFormData.propertiesErrors }}</pre>
|
|
46
|
+
<pre>definition = {{ actionFormData.action.definition }}</pre> -->
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
|
|
50
|
+
<script setup>
|
|
51
|
+
|
|
52
|
+
import { ref, computed, onMounted, defineProps, defineEmits, toRefs } from 'vue'
|
|
53
|
+
import Button from 'primevue/button'
|
|
54
|
+
import AutoEditor from '../form/AutoEditor.vue'
|
|
55
|
+
import ActionButtons from './ActionButtons.vue'
|
|
56
|
+
import { useToast } from 'primevue/usetoast'
|
|
57
|
+
const toast = useToast()
|
|
58
|
+
|
|
59
|
+
const props = defineProps({
|
|
60
|
+
service: {
|
|
61
|
+
type: String,
|
|
62
|
+
required: true,
|
|
63
|
+
},
|
|
64
|
+
action: {
|
|
65
|
+
type: String,
|
|
66
|
+
required: true,
|
|
67
|
+
},
|
|
68
|
+
i18n: {
|
|
69
|
+
type: String,
|
|
70
|
+
default: ''
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const { service, action, i18n } = toRefs(props)
|
|
75
|
+
|
|
76
|
+
const emit = defineEmits(['done', 'error'])
|
|
77
|
+
|
|
78
|
+
import { useApi } from '@live-change/vue3-ssr'
|
|
79
|
+
const api = useApi()
|
|
80
|
+
|
|
81
|
+
import { actionData } from '@live-change/frontend-auto-form'
|
|
82
|
+
|
|
83
|
+
const actionFormData = await actionData({
|
|
84
|
+
service: service.value,
|
|
85
|
+
action: action.value,
|
|
86
|
+
i18n: i18n.value
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const handleSubmit = (ev) => {
|
|
90
|
+
ev.preventDefault()
|
|
91
|
+
actionFormData.submit()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const handleReset = (ev) => {
|
|
95
|
+
ev.preventDefault()
|
|
96
|
+
actionFormData.reset()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<style scoped>
|
|
102
|
+
</style>
|
|
@@ -66,22 +66,17 @@
|
|
|
66
66
|
const savingDraft = computed(() => editor.value.savingDraft?.value)
|
|
67
67
|
const sourceChanged = computed(() => editor.value.sourceChanged?.value)
|
|
68
68
|
const saving = computed(() => editor.value.saving?.value)
|
|
69
|
-
|
|
69
|
+
const propertiesErrors = computed(() => editor.value.propertiesErrors?.value)
|
|
70
|
+
|
|
70
71
|
const appContext = getCurrentInstance().appContext
|
|
71
72
|
|
|
72
73
|
import { validateData } from "@live-change/vue3-components"
|
|
73
74
|
const validationResult = computed(() => {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
const errors = propertiesErrors.value
|
|
76
|
+
if(errors && Object.keys(errors).length > 0) {
|
|
77
|
+
return errors
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
-
props.propName, props.rootValue, true)
|
|
80
|
-
const softValidationResult = validateData(model.value, currentValue, 'softValidation', appContext,
|
|
81
|
-
props.propName, props.rootValue, true)
|
|
82
|
-
console.log("currentValue", currentValue)
|
|
83
|
-
console.log("validationResult", validationResult, softValidationResult)
|
|
84
|
-
return validationResult || softValidationResult
|
|
79
|
+
return null
|
|
85
80
|
})
|
|
86
81
|
|
|
87
82
|
</script>
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
</div>
|
|
48
48
|
<auto-input :modelValue="value" :definition="definition.of || definition.items"
|
|
49
49
|
@update:modelValue="value => updateItem(index, value)"
|
|
50
|
-
:rootValue="props.rootValue" :
|
|
50
|
+
:rootValue="props.rootValue" :errors="props.errors"
|
|
51
|
+
:propName="props.propName + '.' + index"
|
|
51
52
|
:i18n="i18n" />
|
|
52
53
|
</div>
|
|
53
54
|
<div>
|
|
@@ -82,6 +83,10 @@
|
|
|
82
83
|
type: Object,
|
|
83
84
|
default: () => ({})
|
|
84
85
|
},
|
|
86
|
+
errors: {
|
|
87
|
+
type: Object,
|
|
88
|
+
default: () => ({})
|
|
89
|
+
},
|
|
85
90
|
propName: {
|
|
86
91
|
type: String,
|
|
87
92
|
default: ''
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
@update:modelValue="value => updateModelProperty(property, value)"
|
|
6
6
|
:definition="definition.properties[property]"
|
|
7
7
|
:label="property"
|
|
8
|
-
:rootValue="props.rootValue" :
|
|
9
|
-
:
|
|
8
|
+
:rootValue="props.rootValue" :errors="props.errors"
|
|
9
|
+
:propName="(propName ? propName + '.' : '') + property"
|
|
10
|
+
:i18n="i18n"
|
|
10
11
|
class="col-span-12" />
|
|
11
12
|
</div>
|
|
12
13
|
</template>
|
|
@@ -38,10 +39,14 @@
|
|
|
38
39
|
items: {
|
|
39
40
|
type: String
|
|
40
41
|
}
|
|
42
|
+
},
|
|
43
|
+
errors: {
|
|
44
|
+
type: Object,
|
|
45
|
+
default: () => ({})
|
|
41
46
|
}
|
|
42
47
|
})
|
|
43
48
|
|
|
44
|
-
const { modelValue, definition, propName, editableProperties } = toRefs(props)
|
|
49
|
+
const { modelValue, definition, propName, editableProperties, errors } = toRefs(props)
|
|
45
50
|
|
|
46
51
|
const emit = defineEmits(['update:modelValue'])
|
|
47
52
|
|
|
@@ -58,7 +63,6 @@
|
|
|
58
63
|
emit('update:modelValue', data)
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
|
|
62
66
|
</script>
|
|
63
67
|
|
|
64
68
|
<style scoped>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
:class="props.inputClass" :style="props.inputStyle"
|
|
11
11
|
:attributes="props.inputAttributes"
|
|
12
12
|
:propName="props.propName"
|
|
13
|
-
:rootValue="props.rootValue"
|
|
13
|
+
:rootValue="props.rootValue" :errors="props.errors"
|
|
14
14
|
@update:modelValue="value => emit('update:modelValue', value)"
|
|
15
15
|
:id="uid"
|
|
16
16
|
:i18n="i18n" />
|
|
@@ -81,6 +81,10 @@
|
|
|
81
81
|
type: Object,
|
|
82
82
|
default: () => ({})
|
|
83
83
|
},
|
|
84
|
+
errors: {
|
|
85
|
+
type: Object,
|
|
86
|
+
default: () => ({})
|
|
87
|
+
},
|
|
84
88
|
propName: {
|
|
85
89
|
type: String,
|
|
86
90
|
default: ''
|
|
@@ -99,7 +103,7 @@
|
|
|
99
103
|
|
|
100
104
|
const emit = defineEmits(['update:modelValue'])
|
|
101
105
|
|
|
102
|
-
const { error, definition, modelValue } = toRefs(props)
|
|
106
|
+
const { error, definition, modelValue, errors } = toRefs(props)
|
|
103
107
|
|
|
104
108
|
const definitionIf = computed(() => {
|
|
105
109
|
if(definition.value?.if) {
|
|
@@ -129,12 +133,8 @@
|
|
|
129
133
|
|
|
130
134
|
import { validateData } from "@live-change/vue3-components"
|
|
131
135
|
const appContext = getCurrentInstance().appContext
|
|
132
|
-
const validationResult = computed(() => {
|
|
133
|
-
|
|
134
|
-
props.propName, props.rootValue, true)
|
|
135
|
-
const softValidationResult = validateData(definition.value, modelValue.value, 'softValidation', appContext,
|
|
136
|
-
props.propName, props.rootValue, true)
|
|
137
|
-
return validationResult || softValidationResult || error.value
|
|
136
|
+
const validationResult = computed(() => {
|
|
137
|
+
return errors.value?.[props.propName] || error.value
|
|
138
138
|
})
|
|
139
139
|
|
|
140
140
|
function findValidation(name) {
|
|
@@ -189,6 +189,7 @@
|
|
|
189
189
|
inputClass: [props.inputClass, { 'p-invalid': !!validationResult.value }],
|
|
190
190
|
inputStyle: props.inputStyle,
|
|
191
191
|
rootValue: props.rootValue,
|
|
192
|
+
errors: props.errors,
|
|
192
193
|
propName: props.propName,
|
|
193
194
|
}))
|
|
194
195
|
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
type: Object,
|
|
28
28
|
default: () => ({})
|
|
29
29
|
},
|
|
30
|
+
errors: {
|
|
31
|
+
type: Object,
|
|
32
|
+
default: () => ({})
|
|
33
|
+
},
|
|
30
34
|
propName: {
|
|
31
35
|
type: String,
|
|
32
36
|
default: ''
|
|
@@ -85,6 +89,7 @@
|
|
|
85
89
|
propName: props.propName,
|
|
86
90
|
fieldName,
|
|
87
91
|
rootValue: props.rootValue,
|
|
92
|
+
errors: props.errors,
|
|
88
93
|
t, d, n, rt, te
|
|
89
94
|
})
|
|
90
95
|
}
|
|
@@ -98,6 +103,7 @@
|
|
|
98
103
|
modelValue: modelValue.value,
|
|
99
104
|
definition: definition.value,
|
|
100
105
|
rootValue: props.rootValue,
|
|
106
|
+
errors: props.errors,
|
|
101
107
|
propName: props.propName,
|
|
102
108
|
i18n: props.i18n
|
|
103
109
|
}))
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
<auto-input :modelValue="modelValue" :definition="definition" :name="props.name"
|
|
5
5
|
:class="props.inputClass" :style="props.inputStyle"
|
|
6
6
|
:properties="props.inputAttributes"
|
|
7
|
-
:rootValue="props.rootValue" :
|
|
7
|
+
:rootValue="props.rootValue" :errors="props.errors"
|
|
8
|
+
:propName="props.propName"
|
|
8
9
|
@update:modelValue="value => emit('update:modelValue', value)"
|
|
9
10
|
:i18n="props.i18n + props.propName.split('.').pop() + '.'" />
|
|
10
11
|
<Message v-if="typeof validationResult == 'string'" severity="error" variant="simple" size="small">
|
|
@@ -51,6 +52,10 @@
|
|
|
51
52
|
type: Object,
|
|
52
53
|
default: () => ({})
|
|
53
54
|
},
|
|
55
|
+
errors: {
|
|
56
|
+
type: Object,
|
|
57
|
+
default: () => ({})
|
|
58
|
+
},
|
|
54
59
|
propName: {
|
|
55
60
|
type: String,
|
|
56
61
|
default: ''
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { useToast } from 'primevue/usetoast'
|
|
2
|
+
import { usePath, live, useApi } from '@live-change/vue3-ssr'
|
|
3
|
+
import { ref, computed, inject, watch, getCurrentInstance } from 'vue'
|
|
4
|
+
import { synchronized, defaultData } from '@live-change/vue3-components'
|
|
5
|
+
|
|
6
|
+
import { propertiesValidationErrors } from './validation.js'
|
|
7
|
+
|
|
8
|
+
import { cyrb128 } from './utils.js'
|
|
9
|
+
|
|
10
|
+
export default async function actionData(options) {
|
|
11
|
+
if(!options) throw new Error('options must be provided')
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
parameters,
|
|
15
|
+
initialValue = {},
|
|
16
|
+
|
|
17
|
+
service: serviceName,
|
|
18
|
+
action: actionName,
|
|
19
|
+
|
|
20
|
+
draft = true,
|
|
21
|
+
recursive = true,
|
|
22
|
+
debounce = 600,
|
|
23
|
+
timeField = 'lastUpdate',
|
|
24
|
+
|
|
25
|
+
doneToast = "Action done",
|
|
26
|
+
errorToast = "Action error",
|
|
27
|
+
resetToast = "Reset done",
|
|
28
|
+
savedDraftToast = "Draft saved",
|
|
29
|
+
discardedDraftToast = "Draft discarded",
|
|
30
|
+
|
|
31
|
+
onDone = ({ result, data }) => {},
|
|
32
|
+
onError = ({ error, data }) => { throw error } ,
|
|
33
|
+
onReset = () => {},
|
|
34
|
+
|
|
35
|
+
onDraftSaved = () => {},
|
|
36
|
+
onDraftDiscarded = () => {},
|
|
37
|
+
|
|
38
|
+
appContext = getCurrentInstance().appContext,
|
|
39
|
+
|
|
40
|
+
toast = useToast(options.appContext),
|
|
41
|
+
path = usePath(options.appContext),
|
|
42
|
+
api = useApi(options.appContext),
|
|
43
|
+
workingZone = inject('workingZone', options.appContext),
|
|
44
|
+
|
|
45
|
+
} = options
|
|
46
|
+
|
|
47
|
+
if(!serviceName || !actionName) throw new Error('service and action must be defined')
|
|
48
|
+
|
|
49
|
+
const service = api.services[serviceName]
|
|
50
|
+
if(!service) throw new Error('service must be defined in options')
|
|
51
|
+
const action = service.actions[actionName]
|
|
52
|
+
if(!action) throw new Error('action must be defined in options')
|
|
53
|
+
|
|
54
|
+
let draftIdParts = []
|
|
55
|
+
let idKey = null
|
|
56
|
+
for(const [parameterName, parameter] of Object.entries(parameters || {})) {
|
|
57
|
+
if(parameter.type === 'id') {
|
|
58
|
+
idKey = parameterName
|
|
59
|
+
} else {
|
|
60
|
+
draftIdParts.push(parameterName)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let draftId = (idKey ? parameters[idKey]
|
|
65
|
+
: draftIdParts.map(key => JSON.stringify(parameters[key])).join('_')) ?? 'any'
|
|
66
|
+
if(draftId.length > 16) {
|
|
67
|
+
draftId = cyrb128(draftId).slice(0, 16)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const draftIdentifiers = {
|
|
71
|
+
actionType: serviceName, action: 'action', targetType: actionName, target: draftId || 'any'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const draftDataPath = (draft && path.draft.myDraft(draftIdentifiers)) || null
|
|
75
|
+
|
|
76
|
+
const createOrUpdateDraftAction = draft && api.actions.draft.setOrUpdateMyDraft
|
|
77
|
+
const removeDraftAction = draft && api.actions.draft.resetMyDraft
|
|
78
|
+
|
|
79
|
+
const propertiesServerErrors = ref({})
|
|
80
|
+
const lastSentData = ref(null)
|
|
81
|
+
const formData = ref(JSON.parse(JSON.stringify(initialValue)))
|
|
82
|
+
const done = ref(false)
|
|
83
|
+
|
|
84
|
+
let commandPromise = null
|
|
85
|
+
const submitting = ref(false)
|
|
86
|
+
async function submitData(data){
|
|
87
|
+
const requestData = JSON.parse(JSON.stringify({ ...data, ...parameters }))
|
|
88
|
+
if(commandPromise) await commandPromise // wait for previous save
|
|
89
|
+
submitting.value = true
|
|
90
|
+
commandPromise = (async () => {
|
|
91
|
+
propertiesServerErrors.value = {}
|
|
92
|
+
lastSentData.value = JSON.parse(JSON.stringify(requestData))
|
|
93
|
+
console.log("ACTION COMMAND DATA", requestData)
|
|
94
|
+
try {
|
|
95
|
+
const result = await action(requestData)
|
|
96
|
+
done.value = true
|
|
97
|
+
await onDone(result)
|
|
98
|
+
return result
|
|
99
|
+
} catch(e) {
|
|
100
|
+
console.log("ACTION COMMAND ERROR", e)
|
|
101
|
+
if(e.properties) {
|
|
102
|
+
propertiesServerErrors.value = e.properties
|
|
103
|
+
} else {
|
|
104
|
+
await onError(e)
|
|
105
|
+
}
|
|
106
|
+
return Error
|
|
107
|
+
} finally {
|
|
108
|
+
submitting.value = false
|
|
109
|
+
commandPromise = null
|
|
110
|
+
}
|
|
111
|
+
})()
|
|
112
|
+
if(workingZone) workingZone.addPromise('command:'+serviceName+':'+actionName, commandPromise)
|
|
113
|
+
return await commandPromise
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if(draft) {
|
|
117
|
+
const draftData = await live(draftDataPath)
|
|
118
|
+
function saveDraft(data){
|
|
119
|
+
return createOrUpdateDraftAction({ ...data, from: formData.value })
|
|
120
|
+
}
|
|
121
|
+
const source = computed(() => draftData.value?.data || initialValue)
|
|
122
|
+
const synchronizedData = synchronized({
|
|
123
|
+
source,
|
|
124
|
+
update: saveDraft,
|
|
125
|
+
updateDataProperty: 'data',
|
|
126
|
+
identifiers: draftIdentifiers,
|
|
127
|
+
recursive,
|
|
128
|
+
autoSave: true,
|
|
129
|
+
debounce,
|
|
130
|
+
timeField,
|
|
131
|
+
resetOnError: false,
|
|
132
|
+
onSave: () => {
|
|
133
|
+
onDraftSaved()
|
|
134
|
+
if(toast && savedDraftToast) toast.add({ severity: 'info', summary: savedDraftToast, life: 1500 })
|
|
135
|
+
},
|
|
136
|
+
onSaveError: (e) => {
|
|
137
|
+
console.error("DRAFT SAVE ERROR", e)
|
|
138
|
+
if(toast && saveDraftErrorToast)
|
|
139
|
+
toast.add({ severity: 'error', summary: saveDraftErrorToast, detail: e.message ?? e, life: 5000 })
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
const changed = computed(() =>
|
|
143
|
+
JSON.stringify(initialValue ?? {})
|
|
144
|
+
!== JSON.stringify({ ...synchronizedData.value.value, [timeField]: undefined })
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
const propertiesErrors = computed(() => propertiesValidationErrors(
|
|
148
|
+
synchronizedData.value.value, parameters, action.definition, lastSentData.value,
|
|
149
|
+
propertiesServerErrors.value, appContext))
|
|
150
|
+
|
|
151
|
+
async function submit() {
|
|
152
|
+
const result = await submitData(synchronizedData.value.value)
|
|
153
|
+
if(result === Error) return // silent return on error, because it's handled in onError
|
|
154
|
+
if(draftData.value) await removeDraftAction(draftIdentifiers)
|
|
155
|
+
onDone(result)
|
|
156
|
+
if(toast && doneToast) toast.add({ severity: 'success', summary: doneToast, life: 1500 })
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function discardDraft() {
|
|
160
|
+
const discardPromise = removeDraftAction(draftIdentifiers)
|
|
161
|
+
if(workingZone)
|
|
162
|
+
workingZone.addPromise('discardDraft:'+serviceName+':'+actionName, discardPromise)
|
|
163
|
+
await discardPromise
|
|
164
|
+
onDraftDiscarded()
|
|
165
|
+
if(toast && discardedDraftToast) toast.add({ severity: 'info', summary: discardedDraftToast, life: 1500 })
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function reset() {
|
|
169
|
+
const discardPromise = removeDraftAction(draftIdentifiers)
|
|
170
|
+
if(workingZone)
|
|
171
|
+
workingZone.addPromise('discardDraft:'+serviceName+':'+actionName, discardPromise)
|
|
172
|
+
await discardPromise
|
|
173
|
+
synchronizedData.value.value = editableSavedData.value || defaultData(model)
|
|
174
|
+
if(toast && discardedDraftToast) toast.add({ severity: 'info', summary: resetToast, life: 1500 })
|
|
175
|
+
done.value = false
|
|
176
|
+
onReset()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
parameters,
|
|
181
|
+
initialValue,
|
|
182
|
+
value: synchronizedData.value,
|
|
183
|
+
changed,
|
|
184
|
+
submit,
|
|
185
|
+
submitting,
|
|
186
|
+
reset,
|
|
187
|
+
action,
|
|
188
|
+
discardDraft,
|
|
189
|
+
propertiesErrors,
|
|
190
|
+
lastSentData,
|
|
191
|
+
draftChanged: synchronizedData.changed,
|
|
192
|
+
saveDraft: synchronizedData.save,
|
|
193
|
+
savingDraft: synchronizedData.saving,
|
|
194
|
+
done: done,
|
|
195
|
+
draft: draftData
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
} else {
|
|
199
|
+
|
|
200
|
+
async function submit() {
|
|
201
|
+
const result = await submitData(formData.value)
|
|
202
|
+
if(result === Error) return // silent return on error, because it's handled in onError
|
|
203
|
+
onSaved(result)
|
|
204
|
+
if(toast && doneToast) toast.add({ severity: 'success', summary: doneToast, life: 1500 })
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const changed = computed(() =>
|
|
208
|
+
JSON.stringify(initialValue ?? {})
|
|
209
|
+
!== JSON.stringify(formData.value)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
function reset() {
|
|
213
|
+
formData.value = JSON.parse(JSON.stringify(initialValue))
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
parameters,
|
|
218
|
+
initialValue,
|
|
219
|
+
value: formData,
|
|
220
|
+
changed,
|
|
221
|
+
submit,
|
|
222
|
+
submitting,
|
|
223
|
+
done,
|
|
224
|
+
propertiesErrors,
|
|
225
|
+
reset
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
}
|
|
@@ -1,28 +1,11 @@
|
|
|
1
1
|
import { useToast } from 'primevue/usetoast'
|
|
2
2
|
import { usePath, live, useApi } from '@live-change/vue3-ssr'
|
|
3
|
-
import { ref, computed, inject, watch } from 'vue'
|
|
3
|
+
import { ref, computed, inject, watch, getCurrentInstance } from 'vue'
|
|
4
4
|
import { synchronized, defaultData } from '@live-change/vue3-components'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
for (let i = 0, k; i < str.length; i++) {
|
|
10
|
-
k = str.charCodeAt(i);
|
|
11
|
-
h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
|
|
12
|
-
h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
|
|
13
|
-
h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
|
|
14
|
-
h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
|
|
15
|
-
}
|
|
16
|
-
h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
|
|
17
|
-
h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
|
|
18
|
-
h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
|
|
19
|
-
h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
|
|
20
|
-
h1 ^= (h2 ^ h3 ^ h4), h2 ^= h1, h3 ^= h1, h4 ^= h1;
|
|
21
|
-
const data = new Uint32Array([h4>>>0, h3>>>0, h2>>>0, h1>>>0])
|
|
22
|
-
// convert to base64
|
|
23
|
-
const data8 = new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
|
|
24
|
-
return btoa(String.fromCharCode(...data8))
|
|
25
|
-
}
|
|
6
|
+
import { propertiesValidationErrors } from './validation.js'
|
|
7
|
+
|
|
8
|
+
import { cyrb128 } from './utils.js'
|
|
26
9
|
|
|
27
10
|
export default function editorData(options) {
|
|
28
11
|
if(!options) throw new Error('options must be provided')
|
|
@@ -54,10 +37,13 @@ export default function editorData(options) {
|
|
|
54
37
|
onSaveError = () => {},
|
|
55
38
|
onCreated = (createResult) => {},
|
|
56
39
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
40
|
+
|
|
41
|
+
appContext = getCurrentInstance().appContext,
|
|
42
|
+
|
|
43
|
+
toast = useToast(options.appContext),
|
|
44
|
+
path = usePath(options.appContext),
|
|
45
|
+
api = useApi(options.appContext),
|
|
46
|
+
workingZone = inject('workingZone', options.appContext),
|
|
61
47
|
|
|
62
48
|
} = options
|
|
63
49
|
|
|
@@ -123,27 +109,39 @@ export default function editorData(options) {
|
|
|
123
109
|
))
|
|
124
110
|
const source = computed(() => editableDraftData.value || editableSavedData.value || defaultData(model))
|
|
125
111
|
|
|
112
|
+
const propertiesServerErrors = ref({})
|
|
113
|
+
const lastUploadedData = ref(null)
|
|
114
|
+
|
|
126
115
|
let savePromise = null
|
|
127
116
|
const saving = ref(false)
|
|
128
117
|
async function saveData(data){
|
|
129
118
|
const requestData = {
|
|
130
119
|
...(updateDataProperty ? { [updateDataProperty]: data } : data),
|
|
131
120
|
...identifiers
|
|
132
|
-
}
|
|
121
|
+
}
|
|
133
122
|
if(savePromise) await savePromise // wait for previous save
|
|
134
123
|
saving.value = true
|
|
135
124
|
savePromise = (async () => {
|
|
125
|
+
propertiesServerErrors.value = {}
|
|
126
|
+
lastUploadedData.value = JSON.parse(JSON.stringify(data))
|
|
127
|
+
console.log("SAVE DATA", requestData)
|
|
136
128
|
try {
|
|
137
129
|
if(createOrUpdateAction) {
|
|
138
|
-
return createOrUpdateAction(requestData)
|
|
130
|
+
return await createOrUpdateAction(requestData)
|
|
139
131
|
}
|
|
140
132
|
if(savedData.value) {
|
|
141
|
-
return updateAction(requestData)
|
|
133
|
+
return await updateAction(requestData)
|
|
142
134
|
} else {
|
|
143
135
|
const createResult = await createAction(requestData)
|
|
144
136
|
await onCreated(createResult)
|
|
145
137
|
return createResult
|
|
146
138
|
}
|
|
139
|
+
} catch(e) {
|
|
140
|
+
console.log("SAVE ERROR", e)
|
|
141
|
+
if(e.properties) {
|
|
142
|
+
propertiesServerErrors.value = e.properties
|
|
143
|
+
}
|
|
144
|
+
throw e
|
|
147
145
|
} finally {
|
|
148
146
|
saving.value = false
|
|
149
147
|
savePromise = null
|
|
@@ -167,7 +165,6 @@ export default function editorData(options) {
|
|
|
167
165
|
autoSave: true,
|
|
168
166
|
debounce,
|
|
169
167
|
timeField,
|
|
170
|
-
isNew,
|
|
171
168
|
resetOnError: false,
|
|
172
169
|
onSave: () => {
|
|
173
170
|
onDraftSaved()
|
|
@@ -183,12 +180,15 @@ export default function editorData(options) {
|
|
|
183
180
|
JSON.stringify(editableSavedData.value ?? {})
|
|
184
181
|
!== JSON.stringify({ ...synchronizedData.value.value, [timeField]: undefined })
|
|
185
182
|
)
|
|
186
|
-
|
|
187
183
|
const sourceChanged = computed(() =>
|
|
188
184
|
JSON.stringify(draftData.value.from)
|
|
189
185
|
!== JSON.stringify({ ...synchronizedData.value.value, [timeField]: undefined })
|
|
190
186
|
)
|
|
191
187
|
|
|
188
|
+
const propertiesErrors = computed(() => propertiesValidationErrors(
|
|
189
|
+
synchronizedData.value.value, identifiers, model, lastUploadedData.value,
|
|
190
|
+
propertiesServerErrors.value, appContext))
|
|
191
|
+
|
|
192
192
|
async function save() {
|
|
193
193
|
const saveResult = await saveData(synchronizedData.value.value)
|
|
194
194
|
if(draftData.value) await removeDraftAction(draftIdentifiers)
|
|
@@ -203,7 +203,7 @@ export default function editorData(options) {
|
|
|
203
203
|
await discardPromise
|
|
204
204
|
onDraftDiscarded()
|
|
205
205
|
if(toast && discardedDraftToast) toast.add({ severity: 'info', summary: discardedDraftToast, life: 1500 })
|
|
206
|
-
}
|
|
206
|
+
}
|
|
207
207
|
|
|
208
208
|
async function reset() {
|
|
209
209
|
const discardPromise = removeDraftAction(draftIdentifiers)
|
|
@@ -225,7 +225,7 @@ export default function editorData(options) {
|
|
|
225
225
|
discardDraft,
|
|
226
226
|
model,
|
|
227
227
|
isNew,
|
|
228
|
-
|
|
228
|
+
propertiesErrors,
|
|
229
229
|
draftChanged: synchronizedData.changed,
|
|
230
230
|
saveDraft: synchronizedData.save,
|
|
231
231
|
savingDraft: synchronizedData.saving,
|
|
@@ -255,6 +255,10 @@ export default function editorData(options) {
|
|
|
255
255
|
}
|
|
256
256
|
})
|
|
257
257
|
|
|
258
|
+
const propertiesErrors = computed(() => propertiesValidationErrors(
|
|
259
|
+
synchronizedData.value.value, identifiers, model, lastUploadedData.value,
|
|
260
|
+
propertiesServerErrors.value, appContext))
|
|
261
|
+
|
|
258
262
|
async function reset() {
|
|
259
263
|
synchronizedData.value.value = editableSavedData.value || defaultData(model)
|
|
260
264
|
if(toast && discardedDraftToast) toast.add({ severity: 'info', summary: resetToast, life: 1500 })
|
|
@@ -269,6 +273,7 @@ export default function editorData(options) {
|
|
|
269
273
|
saving: synchronizedData.saving,
|
|
270
274
|
saved: savedData,
|
|
271
275
|
savedPath: savedDataPath,
|
|
276
|
+
propertiesErrors,
|
|
272
277
|
reset,
|
|
273
278
|
model,
|
|
274
279
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function cyrb128(str) {
|
|
2
|
+
let h1 = 1779033703, h2 = 3144134277,
|
|
3
|
+
h3 = 1013904242, h4 = 2773480762;
|
|
4
|
+
for (let i = 0, k; i < str.length; i++) {
|
|
5
|
+
k = str.charCodeAt(i);
|
|
6
|
+
h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
|
|
7
|
+
h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
|
|
8
|
+
h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
|
|
9
|
+
h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
|
|
10
|
+
}
|
|
11
|
+
h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
|
|
12
|
+
h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
|
|
13
|
+
h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
|
|
14
|
+
h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
|
|
15
|
+
h1 ^= (h2 ^ h3 ^ h4), h2 ^= h1, h3 ^= h1, h4 ^= h1;
|
|
16
|
+
const data = new Uint32Array([h4>>>0, h3>>>0, h2>>>0, h1>>>0])
|
|
17
|
+
// convert to base64
|
|
18
|
+
const data8 = new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
|
|
19
|
+
return btoa(String.fromCharCode(...data8))
|
|
20
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { validateData } from "@live-change/vue3-components"
|
|
2
|
+
|
|
3
|
+
export function propertiesValidationErrors(rootValue, parameters, definition, lastData, propertiesServerErrors, appContext) {
|
|
4
|
+
const currentValue = {
|
|
5
|
+
...parameters,
|
|
6
|
+
...rootValue,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
console.log("propertiesValidationErrors", rootValue, parameters, definition,
|
|
10
|
+
lastData, propertiesServerErrors, appContext)
|
|
11
|
+
|
|
12
|
+
const validationResult = validateData(definition, currentValue, 'validation', appContext,
|
|
13
|
+
'', rootValue, true)
|
|
14
|
+
|
|
15
|
+
const softValidationResult = validateData(definition, currentValue, 'softValidation', appContext,
|
|
16
|
+
'', rootValue, true)
|
|
17
|
+
|
|
18
|
+
const serverValidationResult = {}
|
|
19
|
+
if(propertiesServerErrors) {
|
|
20
|
+
console.log("propertiesServerErrors", propertiesServerErrors, lastData, rootValue)
|
|
21
|
+
for(const propPathString in propertiesServerErrors) {
|
|
22
|
+
const propPath = propPathString.split('.')
|
|
23
|
+
let last = lastData
|
|
24
|
+
let current = rootValue
|
|
25
|
+
for(const prop of propPath) {
|
|
26
|
+
last = last?.[prop] ?? null
|
|
27
|
+
current = current?.[prop] ?? null
|
|
28
|
+
}
|
|
29
|
+
if(JSON.stringify(last) === JSON.stringify(current)) {
|
|
30
|
+
serverValidationResult[propPathString] = propertiesServerErrors[propPathString]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log("currentValue", currentValue)
|
|
36
|
+
console.log("validationResult", validationResult, softValidationResult)
|
|
37
|
+
console.log("serverValidationResult", serverValidationResult)
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
...softValidationResult?.propertyErrors,
|
|
41
|
+
...validationResult?.propertyErrors,
|
|
42
|
+
...serverValidationResult,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="w-full lg:w-8/12 md:w-11/12">
|
|
3
|
+
<div class="bg-surface-0 dark:bg-surface-900 p-4 shadow-sm rounded-border">
|
|
4
|
+
<div class="text-xl mb-2">
|
|
5
|
+
Service <strong>{{ serviceName }}</strong>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="text-2xl mb-4">
|
|
8
|
+
Action <strong>{{ actionName }}</strong>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<ActionForm
|
|
12
|
+
:service="serviceName"
|
|
13
|
+
:action="actionName"
|
|
14
|
+
@done="handleDone"
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup>
|
|
21
|
+
import { ref, computed, onMounted, defineProps, toRefs } from 'vue'
|
|
22
|
+
import ActionForm from '../components/crud/ActionForm.vue'
|
|
23
|
+
|
|
24
|
+
const props = defineProps({
|
|
25
|
+
serviceName: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: true
|
|
28
|
+
},
|
|
29
|
+
actionName: {
|
|
30
|
+
type: String,
|
|
31
|
+
required: true
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
const { serviceName, actionName } = toRefs(props)
|
|
35
|
+
|
|
36
|
+
import { useApi } from '@live-change/vue3-ssr'
|
|
37
|
+
const api = useApi()
|
|
38
|
+
|
|
39
|
+
function handleDone(result) {
|
|
40
|
+
console.log('Action executed:', result)
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<style scoped>
|
|
45
|
+
</style>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="w-full lg:w-8/12 md:w-11/12">
|
|
3
|
+
<div v-for="serviceWithActions of editableActionsByService"
|
|
4
|
+
class="bg-surface-0 dark:bg-surface-900 p-4 shadow-sm rounded-border">
|
|
5
|
+
<div class="text-xl mb-2">
|
|
6
|
+
Service <strong>{{ serviceWithActions.name }}</strong>
|
|
7
|
+
</div>
|
|
8
|
+
<div v-for="action of serviceWithActions.actions" class="mb-2 ml-4">
|
|
9
|
+
<div class="mb-1 flex flex-row flex-wrap items-center justify-between">
|
|
10
|
+
<div class="text-xl flex flex-row items-center mr-6">
|
|
11
|
+
<strong>{{ action.name }}</strong>
|
|
12
|
+
<span class="mx-1">action</span>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="mt-2 md:mt-0">
|
|
15
|
+
<router-link :to="actionRoute(serviceWithActions.name, action)" class="no-underline">
|
|
16
|
+
<Button icon="pi pi-play" severity="success" :label="'Execute '+action.name" />
|
|
17
|
+
</router-link>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup>
|
|
26
|
+
import { ref, computed, onMounted, defineProps, toRefs } from 'vue'
|
|
27
|
+
import Button from 'primevue/button'
|
|
28
|
+
|
|
29
|
+
const props = defineProps({
|
|
30
|
+
serviceName: {
|
|
31
|
+
type: String,
|
|
32
|
+
default: undefined
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
const { serviceName } = toRefs(props)
|
|
36
|
+
|
|
37
|
+
import { useApi } from '@live-change/vue3-ssr'
|
|
38
|
+
const api = useApi()
|
|
39
|
+
|
|
40
|
+
const editableActionsByService = computed(() => {
|
|
41
|
+
const results = []
|
|
42
|
+
for(const [currentServiceName, service] of Object.entries(api.services)) {
|
|
43
|
+
if(serviceName.value && currentServiceName !== serviceName.value) continue
|
|
44
|
+
const actions = Object.entries(service.actions || {})
|
|
45
|
+
.filter(([_, action]) => action.autoForm !== false)
|
|
46
|
+
.map(([name, action]) => ({
|
|
47
|
+
name,
|
|
48
|
+
action
|
|
49
|
+
}))
|
|
50
|
+
if(actions.length === 0) continue
|
|
51
|
+
const result = {
|
|
52
|
+
name: currentServiceName,
|
|
53
|
+
actions
|
|
54
|
+
}
|
|
55
|
+
results.push(result)
|
|
56
|
+
}
|
|
57
|
+
return results
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
function actionRoute(serviceName, action) {
|
|
61
|
+
return {
|
|
62
|
+
name: 'auto-form:action',
|
|
63
|
+
params: {
|
|
64
|
+
serviceName,
|
|
65
|
+
actionName: action.name
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<style scoped>
|
|
72
|
+
</style>
|
package/front/src/router.js
CHANGED
|
@@ -8,6 +8,12 @@ export function autoFormRoutes(config = {}) {
|
|
|
8
8
|
props: true
|
|
9
9
|
}),
|
|
10
10
|
|
|
11
|
+
route({
|
|
12
|
+
name: 'auto-form:actions', path: prefix + '/actions/:serviceName?', meta: { },
|
|
13
|
+
component: () => import("./pages/Actions.vue"),
|
|
14
|
+
props: true
|
|
15
|
+
}),
|
|
16
|
+
|
|
11
17
|
route({
|
|
12
18
|
name: 'auto-form:editor', path: prefix + '/editor/:serviceName/:modelName/:identifiers*', meta: { },
|
|
13
19
|
component: () => import("./pages/Editor.vue"),
|
|
@@ -26,6 +32,12 @@ export function autoFormRoutes(config = {}) {
|
|
|
26
32
|
props: true
|
|
27
33
|
}),
|
|
28
34
|
|
|
35
|
+
route({
|
|
36
|
+
name: 'auto-form:action', path: prefix + '/action/:serviceName/:actionName', meta: { },
|
|
37
|
+
component: () => import("./pages/Action.vue"),
|
|
38
|
+
props: true
|
|
39
|
+
}),
|
|
40
|
+
|
|
29
41
|
]
|
|
30
42
|
}
|
|
31
43
|
|
package/index.js
CHANGED
|
@@ -10,6 +10,8 @@ export * from './front/src/components/form/inputConfigInjection.js'
|
|
|
10
10
|
import editorData from './front/src/logic/editorData.js'
|
|
11
11
|
export { editorData }
|
|
12
12
|
|
|
13
|
+
import actionData from './front/src/logic/actionData.js'
|
|
14
|
+
export { actionData }
|
|
13
15
|
|
|
14
16
|
import AutoView from './front/src/components/view/AutoView.vue'
|
|
15
17
|
import AutoViewField from './front/src/components/view/AutoViewField.vue'
|
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.78",
|
|
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.7.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.78",
|
|
26
|
+
"@live-change/dao": "^0.9.78",
|
|
27
|
+
"@live-change/dao-vue3": "^0.9.78",
|
|
28
|
+
"@live-change/dao-websocket": "^0.9.78",
|
|
29
|
+
"@live-change/framework": "^0.9.78",
|
|
30
|
+
"@live-change/image-frontend": "^0.9.78",
|
|
31
|
+
"@live-change/image-service": "^0.9.78",
|
|
32
|
+
"@live-change/session-service": "^0.9.78",
|
|
33
|
+
"@live-change/vue3-components": "^0.9.78",
|
|
34
|
+
"@live-change/vue3-ssr": "^0.9.78",
|
|
35
35
|
"@vueuse/core": "^12.3.0",
|
|
36
36
|
"codeceptjs-assert": "^0.0.5",
|
|
37
37
|
"compression": "^1.7.5",
|
|
@@ -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.78",
|
|
56
56
|
"codeceptjs": "^3.6.10",
|
|
57
57
|
"generate-password": "1.7.1",
|
|
58
58
|
"playwright": "1.49.1",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"author": "Michał Łaszczewski <michal@laszczewski.pl>",
|
|
64
64
|
"license": "ISC",
|
|
65
65
|
"description": "",
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "747a9cb02377d924583925508f082c8dd5031d43"
|
|
67
67
|
}
|