@live-change/frontend-auto-form 0.9.5 → 0.9.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.
@@ -0,0 +1,86 @@
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="draftChanged" class="text-sm text-500 mr-2">
5
+ Draft changed
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>
9
+ <span>Saving draft...</span>
10
+ </div>
11
+ <div v-if="validationResult" class="font-semibold p-error mr-2">
12
+ Before saving, please correct the errors above.
13
+ </div>
14
+ </div>
15
+ <div class="flex flex-row">
16
+ <slot name="submit" v-if="!validationResult">
17
+ <div v-if="changed" class="ml-2">
18
+ <Button type="submit" label="Save" class="" icon="pi pi-save" />
19
+ </div>
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 />
25
+ </div>
26
+ </slot>
27
+ <slot name="reset" v-if="resetButton">
28
+ <div v-if="changed">
29
+ <Button label="Reset" class="ml-2" icon="pi pi-eraser" @click="editor.reset" />
30
+ </div>
31
+ </slot>
32
+ </div>
33
+ </div>
34
+ </template>
35
+
36
+ <script setup>
37
+
38
+ import { ref, computed, onMounted, defineProps, defineEmits, toRefs, getCurrentInstance } from 'vue'
39
+
40
+ const props = defineProps({
41
+ editor: {
42
+ type: Object,
43
+ required: true,
44
+ },
45
+ resetButton: {
46
+ type: Boolean,
47
+ required: true,
48
+ },
49
+ options: {
50
+ type: Object,
51
+ default: () => ({})
52
+ },
53
+ i18n: {
54
+ type: String,
55
+ default: ''
56
+ }
57
+ })
58
+ const { editor, resetButton, options, i18n } = toRefs(props)
59
+
60
+ const draftEnabled = computed(() => !!editor.value.discardDraft)
61
+ const model = computed(() => editor.value.model)
62
+
63
+ const changed = computed(() => editor.value.changed.value)
64
+
65
+ const draftChanged = computed(() => editor.value.draftChanged?.value)
66
+ const savingDraft = computed(() => editor.value.savingDraft?.value)
67
+ const sourceChanged = computed(() => editor.value.sourceChanged?.value)
68
+ const saving = computed(() => editor.value.saving?.value)
69
+
70
+ const appContext = getCurrentInstance().appContext
71
+
72
+ import { validateData } from "@live-change/vue3-components"
73
+ const validationResult = computed(() => {
74
+ const currentValue = editor.value.value.value
75
+ const validationResult = validateData(model.value, currentValue, 'validation', appContext,
76
+ props.propName, props.rootValue, true)
77
+ const softValidationResult = validateData(model.value, currentValue, 'softValidation', appContext,
78
+ props.propName, props.rootValue, true)
79
+ return validationResult || softValidationResult
80
+ })
81
+
82
+ </script>
83
+
84
+ <style scoped>
85
+
86
+ </style>
@@ -6,9 +6,7 @@
6
6
  v-model="editor.value.value"
7
7
  :rootValue="editor.value.value"
8
8
  :i18n="i18n" />
9
- <div v-if="draft">
10
- save buttons
11
- </div>
9
+ <EditorButtons :editor="editor" reset-button />
12
10
  </div>
13
11
  </div>
14
12
  </template>
@@ -16,6 +14,7 @@
16
14
  <script setup>
17
15
 
18
16
  import AutoEditor from '../form/AutoEditor.vue'
17
+ import EditorButtons from './EditorButtons.vue'
19
18
 
20
19
  import { ref, computed, onMounted, defineProps, defineEmits, toRefs } from 'vue'
21
20
 
@@ -61,23 +60,33 @@
61
60
  import { computedAsync } from "@vueuse/core"
62
61
 
63
62
  const editor = computedAsync(async () => {
64
- const ed = await editorData({
65
- service: service.value,
66
- model: model.value,
67
- identifiers: identifiers.value,
68
- draft: draft.value,
69
- autoSave: true,
70
- ...options.value,
71
- onSaved: (...args) => emit('saved', ...args),
72
- onDraftSaved: (...args) => emit('draftSaved', ...args),
73
- onDraftDiscarded: (...args) => emit('draftDiscarded', ...args),
74
- onSaveError: (...args) => emit('saveError', ...args),
75
- onCreated: (...args) => emit('created', ...args),
76
- })
77
- //console.log("ED", ed)
78
- return ed
63
+ try {
64
+ const ed = await editorData({
65
+ service: service.value,
66
+ model: model.value,
67
+ identifiers: identifiers.value,
68
+ draft: draft.value,
69
+ autoSave: true,
70
+ ...options.value,
71
+ onSaved: (...args) => emit('saved', ...args),
72
+ onDraftSaved: (...args) => emit('draftSaved', ...args),
73
+ onDraftDiscarded: (...args) => emit('draftDiscarded', ...args),
74
+ onSaveError: (...args) => emit('saveError', ...args),
75
+ onCreated: (...args) => emit('created', ...args),
76
+ })
77
+ //console.log("ED", ed)
78
+ return ed
79
+ } catch(e) {
80
+ console.error("EDITOR ERROR", e)
81
+ return null
82
+ }
79
83
  })
80
84
 
85
+ const isNew = computed(() => editor.value.saved.value)
86
+
87
+ /// TODO: detect when we should change identifiers
88
+ // const savedIdentifiers
89
+
81
90
  </script>
82
91
 
83
92
  <style scoped>
@@ -24,10 +24,12 @@ export default function editorData(options) {
24
24
  discardedDraftToast = "Draft discarded",
25
25
  saveDraftErrorToast = "Error saving draft",
26
26
  saveErrorToast = "Error saving",
27
+ resetToast = "Reset done",
27
28
 
28
29
  onSaved = () => {},
29
30
  onDraftSaved = () => {},
30
31
  onDraftDiscarded = () => {},
32
+ onReset = () => {},
31
33
  onSaveError = () => {},
32
34
  onCreated = (createResult) => {},
33
35
 
@@ -37,6 +39,7 @@ export default function editorData(options) {
37
39
  workingZone = inject('workingZone')
38
40
 
39
41
  } = options
42
+
40
43
  if(!identifiers) throw new Error('identifiers must be defined')
41
44
  if(!serviceName || !modelName) throw new Error('service and model must be defined')
42
45
 
@@ -45,10 +48,11 @@ export default function editorData(options) {
45
48
  const {
46
49
  crudMethods = model.crud,
47
50
  identifiersNames = model.identifiers,
48
- editableProperties = model.editableProperties
51
+ editableProperties = model.editableProperties ?? Object.keys(model.properties)
49
52
  } = options
53
+
50
54
  if(!crudMethods) throw new Error('crud methods must be defined in model or options')
51
- if(!identifiers) throw new Error('identifiers names must be defined in model or options')
55
+ if(!identifiersNames) throw new Error('identifiers names must be defined in model or options')
52
56
  if(!editableProperties) throw new Error('editableProperties must be defined in model or options')
53
57
 
54
58
  let idKey = null
@@ -65,14 +69,14 @@ export default function editorData(options) {
65
69
  draftIdParts.push(identifier)
66
70
  }
67
71
  }
68
- const draftId = idKey ? identifiers[idKey]
69
- : draftIdParts.map(key => JSON.stringify(identifiers[key])).join('_')
70
-
72
+ const isNew = (idKey ? (!identifiers[idKey]) : (!draftIdParts.some(key => !identifiers[key])))
73
+ const draftId = (idKey ? identifiers[idKey]
74
+ : draftIdParts.map(key => JSON.stringify(identifiers[key])).join('_')) ?? 'new'
71
75
  const draftIdentifiers = {
72
76
  actionType: serviceName, action: crudMethods.read, targetType: modelName, target: draftId
73
77
  }
74
78
 
75
- const savedDataPath = path[serviceName][crudMethods.read](identifiers)
79
+ const savedDataPath = isNew ? null : path[serviceName][crudMethods.read](identifiers)
76
80
  const draftDataPath = (draft && path.draft.myDraft(draftIdentifiers)) || null
77
81
 
78
82
  const updateAction = api.actions[serviceName][crudMethods.update]
@@ -84,6 +88,7 @@ export default function editorData(options) {
84
88
  return Promise.all([
85
89
  live(savedDataPath), live(draftDataPath)
86
90
  ]).then(([savedData, draftData]) => {
91
+
87
92
  const editableSavedData = computed(() => savedData.value && Object.fromEntries(
88
93
  editableProperties.map(prop => [prop, savedData.value[prop]])
89
94
  .concat([[timeField, savedData.value[timeField]]])
@@ -148,7 +153,7 @@ export default function editorData(options) {
148
153
  })
149
154
 
150
155
  const changed = computed(() =>
151
- JSON.stringify(editableSavedData.value) !== JSON.stringify(synchronizedData.value.value))
156
+ JSON.stringify(editableSavedData.value ?? {}) !== JSON.stringify(synchronizedData.value.value))
152
157
  const sourceChanged = computed(() =>
153
158
  JSON.stringify(draftData.value.from) !== JSON.stringify(editableSavedData.value))
154
159
 
@@ -168,17 +173,30 @@ export default function editorData(options) {
168
173
  if(toast && discardedDraftToast) toast.add({ severity: 'info', summary: discardedDraftToast, life: 1500 })
169
174
  }
170
175
 
176
+ async function reset() {
177
+ const discardPromise = removeDraftAction(draftIdentifiers)
178
+ if(workingZone)
179
+ workingZone.addPromise('discardDraft:'+serviceName+':'+modelName, discardPromise)
180
+ await discardPromise
181
+ synchronizedData.value.value = editableSavedData.value || defaultData(model)
182
+ if(toast && discardedDraftToast) toast.add({ severity: 'info', summary: resetToast, life: 1500 })
183
+ onReset()
184
+ }
185
+
171
186
  return {
172
187
  value: synchronizedData.value,
173
188
  changed,
174
189
  save,
175
190
  saving,
191
+ reset,
176
192
  discardDraft,
177
193
  model,
178
194
  resetOnError: false,
179
195
  draftChanged: synchronizedData.changed,
180
196
  saveDraft: synchronizedData.save,
181
197
  savingDraft: synchronizedData.saving,
198
+ saved: savedData,
199
+ draft: draftData,
182
200
  sourceChanged /// needed for draft discard on concurrent save
183
201
  }
184
202
  } else {
@@ -202,11 +220,19 @@ export default function editorData(options) {
202
220
  }
203
221
  })
204
222
 
223
+ async function reset() {
224
+ synchronizedData.value.value = editableSavedData.value || defaultData(model)
225
+ if(toast && discardedDraftToast) toast.add({ severity: 'info', summary: resetToast, life: 1500 })
226
+ onReset()
227
+ }
228
+
205
229
  return {
206
230
  value: synchronizedData.value,
207
231
  changed: synchronizedData.changed,
208
232
  save: synchronizedData.save,
209
233
  saving: synchronizedData.saving,
234
+ saved: savedData,
235
+ reset,
210
236
  model,
211
237
  }
212
238
 
@@ -1,6 +1,7 @@
1
1
  <template>
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
  <div class="text-xl mb-2">
5
6
  Service <strong>{{ serviceName }} model {{ modelName }}</strong>
6
7
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/frontend-auto-form",
3
- "version": "0.9.5",
3
+ "version": "0.9.7",
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.5",
26
- "@live-change/dao": "^0.9.5",
27
- "@live-change/dao-vue3": "^0.9.5",
28
- "@live-change/dao-websocket": "^0.9.5",
29
- "@live-change/framework": "^0.9.5",
30
- "@live-change/image-frontend": "^0.9.5",
31
- "@live-change/image-service": "^0.9.5",
32
- "@live-change/session-service": "^0.9.5",
33
- "@live-change/vue3-components": "^0.9.5",
34
- "@live-change/vue3-ssr": "^0.9.5",
25
+ "@live-change/cli": "^0.9.7",
26
+ "@live-change/dao": "^0.9.7",
27
+ "@live-change/dao-vue3": "^0.9.7",
28
+ "@live-change/dao-websocket": "^0.9.7",
29
+ "@live-change/framework": "^0.9.7",
30
+ "@live-change/image-frontend": "^0.9.7",
31
+ "@live-change/image-service": "^0.9.7",
32
+ "@live-change/session-service": "^0.9.7",
33
+ "@live-change/vue3-components": "^0.9.7",
34
+ "@live-change/vue3-ssr": "^0.9.7",
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.5",
55
+ "@live-change/codeceptjs-helper": "^0.9.7",
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": "59f1d66de3a64b751308593e282e8635c0ecab7e"
66
+ "gitHead": "f936468bf39ea3c5b07ce14666f4b3a4a4a9287d"
67
67
  }