@conduction/nextcloud-vue 2.62.0 → 2.63.0
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/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js +10 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js.map +1 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js +189 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js.map +1 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js +72 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js.map +1 -0
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +271 -32
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +99 -9
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +39 -18
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +53 -37
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js +10 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +293 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +189 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +216 -8
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +118 -50
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js +3 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +234 -9
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +96 -57
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +51 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +5 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/selfModeActions.js +3 -3
- package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +36 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js +121 -12
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js.map +1 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js +3 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +326 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +112 -26
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +10 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +337 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +102 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js +34 -0
- package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js.map +1 -0
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +131 -8
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js +40 -38
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +8 -6
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +19 -0
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
- package/dist/esm/composables/useDependencyCheck.js +65 -0
- package/dist/esm/composables/useDependencyCheck.js.map +1 -0
- package/dist/esm/composables/useWalkthrough.js +218 -6
- package/dist/esm/composables/useWalkthrough.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +43 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +43 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +449 -0
- package/dist/esm/store/useObjectStore.js +8 -0
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/columnFilters.js +311 -0
- package/dist/esm/utils/columnFilters.js.map +1 -0
- package/dist/esm/utils/groupAutocomplete.js +123 -0
- package/dist/esm/utils/groupAutocomplete.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +2 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/pickerOptions.js +198 -0
- package/dist/esm/utils/pickerOptions.js.map +1 -0
- package/dist/esm/utils/schema.js +83 -7
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/timelineEvents.js +174 -0
- package/dist/esm/utils/timelineEvents.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/walkthroughSignals.js +88 -0
- package/dist/esm/utils/walkthroughSignals.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +9369 -5193
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +449 -0
- package/l10n/en.json +43 -0
- package/l10n/nl.json +43 -0
- package/package.json +1 -1
- package/src/components/CnAdminActionCard/CnAdminActionCard.vue +234 -0
- package/src/components/CnAdminActionCard/index.js +4 -0
- package/src/components/CnAppNav/CnAppNav.vue +271 -32
- package/src/components/CnAppRoot/CnAppRoot.vue +129 -15
- package/src/components/CnChoiceCards/CnChoiceCards.vue +56 -31
- package/src/components/CnDashboardPage/CnDashboardPage.vue +2 -2
- package/src/components/CnDataTable/CnColumnFilterPopover.vue +524 -0
- package/src/components/CnDataTable/CnDataTable.md +3 -0
- package/src/components/CnDataTable/CnDataTable.vue +260 -4
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
- package/src/components/CnFieldHelper/CnFieldHelper.vue +4 -2
- package/src/components/CnFormDialog/CnFormDialog.vue +290 -31
- package/src/components/CnIndexPage/CnIndexPage.vue +55 -2
- package/src/components/CnIndexPage/selfModeActions.js +3 -3
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +39 -1
- package/src/components/CnResourceSelect/CnResourceSelect.vue +123 -12
- package/src/components/CnSetupWizard/CnSetupWizard.vue +392 -3
- package/src/components/CnTimelineWidget/CnTimelineWidget.vue +477 -0
- package/src/components/CnTimelineWidget/dashboardRegistration.js +32 -0
- package/src/components/CnTimelineWidget/index.js +4 -0
- package/src/components/CnWalkthrough/CnWalkthrough.vue +143 -10
- package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/CnWizardDialog/CnWizardDialog.vue +20 -1
- package/src/components/index.js +2 -0
- package/src/composables/useDependencyCheck.js +61 -0
- package/src/composables/useWalkthrough.js +217 -5
- package/src/css/dashboard.css +49 -0
- package/src/css/detail-page.css +19 -0
- package/src/css/table.css +81 -0
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +23 -1
- package/src/store/useObjectStore.js +8 -0
- package/src/utils/columnFilters.js +324 -0
- package/src/utils/groupAutocomplete.js +119 -0
- package/src/utils/libraryWidgetKeys.js +2 -0
- package/src/utils/pickerOptions.js +194 -0
- package/src/utils/schema.js +83 -7
- package/src/utils/timelineEvents.js +171 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
- package/src/utils/walkthroughSignals.js +94 -0
|
@@ -179,16 +179,20 @@
|
|
|
179
179
|
references get CnResourceSelect; plain references keep the
|
|
180
180
|
read-only NcSelect below. -->
|
|
181
181
|
<div
|
|
182
|
-
v-else-if="isReferenceField(field) && field.allowCreate"
|
|
182
|
+
v-else-if="(isReferenceField(field) || isReferenceArrayField(field)) && field.allowCreate"
|
|
183
183
|
class="cn-form-dialog__select-wrapper">
|
|
184
184
|
<CnResourceSelect
|
|
185
185
|
:inputId="'cn-form-' + field.key"
|
|
186
186
|
:inputLabel="field.label + (field.required ? ' *' : '')"
|
|
187
187
|
:register="referenceRegister(field)"
|
|
188
|
-
:schema="field.reference.schema"
|
|
188
|
+
:schema="String(field.reference.schema)"
|
|
189
189
|
:labelField="referenceLabelField(field)"
|
|
190
|
-
:
|
|
191
|
-
:
|
|
190
|
+
:multiple="!!field.reference.multiple"
|
|
191
|
+
:modelValue="referenceModelValue(field)"
|
|
192
|
+
:clearable="!field.required || !!field.reference.multiple"
|
|
193
|
+
:disabled="field.readOnly"
|
|
194
|
+
:preload="true"
|
|
195
|
+
:createHandler="term => openNestedCreate(field, term)"
|
|
192
196
|
@update:modelValue="value => onReferenceSelected(field, value)"
|
|
193
197
|
@create="obj => onReferenceCreated(field, obj)" />
|
|
194
198
|
<CnFieldHelper
|
|
@@ -199,14 +203,19 @@
|
|
|
199
203
|
|
|
200
204
|
<!-- Enum toggle (`widget: "switch"` on a 2-value enum):
|
|
201
205
|
renders a switch mapping off→enum[0], on→last enum value. -->
|
|
202
|
-
<
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
<div v-else-if="field.widget === 'switch'" class="cn-form-dialog__switch-wrapper">
|
|
207
|
+
<NcCheckboxRadioSwitch
|
|
208
|
+
:modelValue="isSwitchOn(field)"
|
|
209
|
+
:disabled="field.readOnly"
|
|
210
|
+
type="switch"
|
|
211
|
+
@update:modelValue="value => updateField(field.key, switchValueFor(field, value))">
|
|
212
|
+
{{ field.label }}{{ field.required ? ' *' : '' }}
|
|
213
|
+
</NcCheckboxRadioSwitch>
|
|
214
|
+
<CnFieldHelper
|
|
215
|
+
:text="field.description"
|
|
216
|
+
:more="field.descriptionLong"
|
|
217
|
+
:error="errors[field.key]" />
|
|
218
|
+
</div>
|
|
210
219
|
|
|
211
220
|
<!-- Select (enum / $ref object reference / single Nextcloud user, supports async function).
|
|
212
221
|
A single Nextcloud-user field (`widget: "user"`) renders here too:
|
|
@@ -214,7 +223,7 @@
|
|
|
214
223
|
user picker, and the async load/search/select machinery (getEffective*,
|
|
215
224
|
onAsyncSearch) resolves real users — mirroring how `user-multiselect`
|
|
216
225
|
shares the multiselect branch below. -->
|
|
217
|
-
<div v-else-if="field.widget === 'select' || field.widget === 'user'" class="cn-form-dialog__select-wrapper">
|
|
226
|
+
<div v-else-if="field.widget === 'select' || field.widget === 'user' || field.widget === 'group'" class="cn-form-dialog__select-wrapper">
|
|
218
227
|
<!-- A `widget: "user"` field renders NcSelectUsers, everything
|
|
219
228
|
else NcSelect. It used to be one NcSelect with
|
|
220
229
|
`:user-select="isUserField(field)"`, and @nextcloud/vue 9
|
|
@@ -256,7 +265,7 @@
|
|
|
256
265
|
</div>
|
|
257
266
|
|
|
258
267
|
<!-- Multiselect (array enum items / $ref array / Nextcloud users, supports async function) -->
|
|
259
|
-
<div v-else-if="field.widget === 'multiselect' || field.widget === 'user-multiselect'" class="cn-form-dialog__select-wrapper">
|
|
268
|
+
<div v-else-if="field.widget === 'multiselect' || field.widget === 'user-multiselect' || field.widget === 'group-multiselect'" class="cn-form-dialog__select-wrapper">
|
|
260
269
|
<NcSelect
|
|
261
270
|
:inputId="'cn-form-' + field.key"
|
|
262
271
|
:inputLabel="field.label + (field.required ? ' *' : '')"
|
|
@@ -322,14 +331,19 @@
|
|
|
322
331
|
</div>
|
|
323
332
|
|
|
324
333
|
<!-- Checkbox / Switch (boolean) -->
|
|
325
|
-
<
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
334
|
+
<div v-else-if="field.widget === 'checkbox'" class="cn-form-dialog__switch-wrapper">
|
|
335
|
+
<NcCheckboxRadioSwitch
|
|
336
|
+
:modelValue="!!formData[field.key]"
|
|
337
|
+
:disabled="field.readOnly"
|
|
338
|
+
type="switch"
|
|
339
|
+
@update:modelValue="value => updateField(field.key, value)">
|
|
340
|
+
{{ field.label }}{{ field.required ? ' *' : '' }}
|
|
341
|
+
</NcCheckboxRadioSwitch>
|
|
342
|
+
<CnFieldHelper
|
|
343
|
+
:text="field.description"
|
|
344
|
+
:more="field.descriptionLong"
|
|
345
|
+
:error="errors[field.key]" />
|
|
346
|
+
</div>
|
|
333
347
|
|
|
334
348
|
<!-- Date / Datetime (NcTextField's type validator rejects
|
|
335
349
|
'date'/'datetime-local', so use NcDateTimePickerNative) -->
|
|
@@ -438,6 +452,21 @@
|
|
|
438
452
|
</template>
|
|
439
453
|
</div>
|
|
440
454
|
|
|
455
|
+
<!-- "Create" on a select-or-create reference (`x-allow-create`) opens
|
|
456
|
+
the referenced schema's own form on top of this one. Saving it
|
|
457
|
+
selects the new object in the field that asked for it. -->
|
|
458
|
+
<CnFormDialog
|
|
459
|
+
v-if="nestedCreate !== null"
|
|
460
|
+
ref="nestedCreateDialog"
|
|
461
|
+
:schema="nestedCreate.schema"
|
|
462
|
+
:register="nestedCreate.register"
|
|
463
|
+
:item="null"
|
|
464
|
+
:initialData="nestedCreate.initialData"
|
|
465
|
+
:recoverDraft="false"
|
|
466
|
+
data-testid="cn-form-dialog-nested-create"
|
|
467
|
+
@confirm="onNestedCreateConfirm"
|
|
468
|
+
@close="onNestedCreateClose" />
|
|
469
|
+
|
|
441
470
|
<template #actions>
|
|
442
471
|
<!-- One announcement per state change, so a screen reader hears
|
|
443
472
|
"Draft saved" once rather than on every keystroke. -->
|
|
@@ -494,7 +523,9 @@ import {
|
|
|
494
523
|
splitDynamicFormData,
|
|
495
524
|
} from '../../utils/dynamicProperties.js'
|
|
496
525
|
import { shouldShow } from '../../utils/fieldCondition.js'
|
|
526
|
+
import { resolveNextcloudGroup, searchNextcloudGroups } from '../../utils/groupAutocomplete.js'
|
|
497
527
|
import { objectDisplayName } from '../../utils/objectName.js'
|
|
528
|
+
import { languageOptions, resolveDefaultToken, timezoneOptions } from '../../utils/pickerOptions.js'
|
|
498
529
|
import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
499
530
|
import { fieldsFromSchema, isTenantProperty } from '../../utils/schema.js'
|
|
500
531
|
import { resolveNextcloudUser, searchNextcloudUsers } from '../../utils/userAutocomplete.js'
|
|
@@ -1025,6 +1056,13 @@ export default {
|
|
|
1025
1056
|
* `dynamicToken` guards the definitions fetch.
|
|
1026
1057
|
*/
|
|
1027
1058
|
prefillToken: '',
|
|
1059
|
+
/**
|
|
1060
|
+
* The inline create in progress, or null: `{ field, schema,
|
|
1061
|
+
* register, slug, initialData, resolve }`. Set when "Create" is
|
|
1062
|
+
* chosen on a select-or-create reference; renders a nested form
|
|
1063
|
+
* for the referenced schema.
|
|
1064
|
+
*/
|
|
1065
|
+
nestedCreate: null,
|
|
1028
1066
|
}
|
|
1029
1067
|
},
|
|
1030
1068
|
|
|
@@ -1295,6 +1333,7 @@ export default {
|
|
|
1295
1333
|
.filter((field) => shouldShow(field, this.formData))
|
|
1296
1334
|
.map((field) => this.applySemanticResolution(field))
|
|
1297
1335
|
.map((field) => this.degradeUnresolvableReference(field))
|
|
1336
|
+
.map((field) => this.applyCodePicker(field))
|
|
1298
1337
|
},
|
|
1299
1338
|
|
|
1300
1339
|
/**
|
|
@@ -1809,11 +1848,16 @@ export default {
|
|
|
1809
1848
|
// Create mode: initialize with field defaults
|
|
1810
1849
|
const data = {}
|
|
1811
1850
|
for (const field of this.resolvedFields) {
|
|
1851
|
+
const tokenDefault = field.defaultToken ? resolveDefaultToken(field.defaultToken, field) : null
|
|
1812
1852
|
if (field.default !== null && field.default !== undefined) {
|
|
1813
1853
|
data[field.key] = field.default
|
|
1854
|
+
} else if (tokenDefault !== null) {
|
|
1855
|
+
// `x-default: current-language` / `current-timezone`:
|
|
1856
|
+
// a NEW object starts with the user's own value.
|
|
1857
|
+
data[field.key] = tokenDefault
|
|
1814
1858
|
} else if (field.widget === 'checkbox') {
|
|
1815
1859
|
data[field.key] = false
|
|
1816
|
-
} else if (field.widget === 'tags' || field.widget === 'multiselect' || field.widget === 'user-multiselect') {
|
|
1860
|
+
} else if (field.widget === 'tags' || field.widget === 'multiselect' || field.widget === 'user-multiselect' || field.widget === 'group-multiselect') {
|
|
1817
1861
|
data[field.key] = []
|
|
1818
1862
|
} else if (field.widget === 'code') {
|
|
1819
1863
|
data[field.key] = ''
|
|
@@ -1880,10 +1924,35 @@ export default {
|
|
|
1880
1924
|
this.resolveUserLabel(uid)
|
|
1881
1925
|
}
|
|
1882
1926
|
}
|
|
1927
|
+
} else if (this.isGroupField(field) || this.isGroupArrayField(field)) {
|
|
1928
|
+
const value = this.formData[field.key]
|
|
1929
|
+
for (const gid of (Array.isArray(value) ? value : [value])) {
|
|
1930
|
+
if (gid) {
|
|
1931
|
+
this.resolveGroupLabel(gid)
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1883
1934
|
}
|
|
1884
1935
|
}
|
|
1885
1936
|
},
|
|
1886
1937
|
|
|
1938
|
+
/**
|
|
1939
|
+
* Resolve a stored group id to its display name and cache it in
|
|
1940
|
+
* `referenceLabels`. No-op once cached; falls back to the gid.
|
|
1941
|
+
*
|
|
1942
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
1943
|
+
* @param {string} gid The stored group id.
|
|
1944
|
+
* @return {Promise<void>}
|
|
1945
|
+
*/
|
|
1946
|
+
async resolveGroupLabel(gid) {
|
|
1947
|
+
if (!gid || this.referenceLabels[gid]) {
|
|
1948
|
+
return
|
|
1949
|
+
}
|
|
1950
|
+
const option = await resolveNextcloudGroup(gid)
|
|
1951
|
+
if (option && option.id) {
|
|
1952
|
+
this.referenceLabels = { ...this.referenceLabels, [option.id]: option.label || String(option.id) }
|
|
1953
|
+
}
|
|
1954
|
+
},
|
|
1955
|
+
|
|
1887
1956
|
/**
|
|
1888
1957
|
* Resolve a single user UID to its display name (edit mode) and cache it
|
|
1889
1958
|
* in `referenceLabels` so the select shows the name instead of the UID.
|
|
@@ -2380,6 +2449,67 @@ export default {
|
|
|
2380
2449
|
return !!(field && field.userPicker && field.userPicker.multiple)
|
|
2381
2450
|
},
|
|
2382
2451
|
|
|
2452
|
+
/**
|
|
2453
|
+
* Whether a field is a single Nextcloud group (`format: 'nc-group'` or
|
|
2454
|
+
* `referenceType: 'nextcloud-group'`). Stores the group id.
|
|
2455
|
+
*
|
|
2456
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
2457
|
+
* @param {object} field The field definition
|
|
2458
|
+
* @return {boolean}
|
|
2459
|
+
*/
|
|
2460
|
+
isGroupField(field) {
|
|
2461
|
+
return !!(field && field.groupPicker && !field.groupPicker.multiple)
|
|
2462
|
+
},
|
|
2463
|
+
|
|
2464
|
+
/**
|
|
2465
|
+
* Whether a field is an array of Nextcloud groups.
|
|
2466
|
+
*
|
|
2467
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
2468
|
+
* @param {object} field The field definition
|
|
2469
|
+
* @return {boolean}
|
|
2470
|
+
*/
|
|
2471
|
+
isGroupArrayField(field) {
|
|
2472
|
+
return !!(field && field.groupPicker && field.groupPicker.multiple)
|
|
2473
|
+
},
|
|
2474
|
+
|
|
2475
|
+
/**
|
|
2476
|
+
* Whether a single-value field stores an id that maps to a label
|
|
2477
|
+
* fetched elsewhere: an object reference, a user or a group.
|
|
2478
|
+
*
|
|
2479
|
+
* @param {object} field The field definition
|
|
2480
|
+
* @return {boolean}
|
|
2481
|
+
*/
|
|
2482
|
+
isIdPickerField(field) {
|
|
2483
|
+
return this.isReferenceField(field) || this.isUserField(field) || this.isGroupField(field)
|
|
2484
|
+
},
|
|
2485
|
+
|
|
2486
|
+
/**
|
|
2487
|
+
* The multi-value sibling of `isIdPickerField`.
|
|
2488
|
+
*
|
|
2489
|
+
* @param {object} field The field definition
|
|
2490
|
+
* @return {boolean}
|
|
2491
|
+
*/
|
|
2492
|
+
isIdPickerArrayField(field) {
|
|
2493
|
+
return this.isReferenceArrayField(field) || this.isUserArrayField(field) || this.isGroupArrayField(field)
|
|
2494
|
+
},
|
|
2495
|
+
|
|
2496
|
+
/**
|
|
2497
|
+
* Turn a language or time zone field into a searchable select over
|
|
2498
|
+
* a fixed code list: labels in the user's language, the code stored.
|
|
2499
|
+
* Other fields pass through unchanged.
|
|
2500
|
+
*
|
|
2501
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
2502
|
+
* @param {object} field A resolved field descriptor.
|
|
2503
|
+
* @return {object} The field, as a `select` when it is a code picker.
|
|
2504
|
+
*/
|
|
2505
|
+
applyCodePicker(field) {
|
|
2506
|
+
if (!field || (field.widget !== 'language' && field.widget !== 'timezone')) {
|
|
2507
|
+
return field
|
|
2508
|
+
}
|
|
2509
|
+
const list = field.widget === 'language' ? languageOptions() : timezoneOptions()
|
|
2510
|
+
return { ...field, widget: 'select', codePicker: field.widget, enum: list.codes, enumLabels: list.labels }
|
|
2511
|
+
},
|
|
2512
|
+
|
|
2383
2513
|
/**
|
|
2384
2514
|
* Check if a field has an async enum (function instead of static array).
|
|
2385
2515
|
* Object references (`$ref`) and Nextcloud user references are treated
|
|
@@ -2389,7 +2519,7 @@ export default {
|
|
|
2389
2519
|
* @return {boolean}
|
|
2390
2520
|
*/
|
|
2391
2521
|
isAsyncEnum(field) {
|
|
2392
|
-
return typeof field.enum === 'function' || this.
|
|
2522
|
+
return typeof field.enum === 'function' || this.isIdPickerField(field)
|
|
2393
2523
|
},
|
|
2394
2524
|
|
|
2395
2525
|
/**
|
|
@@ -2401,7 +2531,7 @@ export default {
|
|
|
2401
2531
|
* @return {boolean}
|
|
2402
2532
|
*/
|
|
2403
2533
|
isAsyncItemsEnum(field) {
|
|
2404
|
-
return !!(field.items && typeof field.items.enum === 'function') || this.
|
|
2534
|
+
return !!(field.items && typeof field.items.enum === 'function') || this.isIdPickerArrayField(field)
|
|
2405
2535
|
},
|
|
2406
2536
|
|
|
2407
2537
|
/**
|
|
@@ -2468,12 +2598,119 @@ export default {
|
|
|
2468
2598
|
* @return {void}
|
|
2469
2599
|
*/
|
|
2470
2600
|
onReferenceSelected(field, value) {
|
|
2601
|
+
if (Array.isArray(value)) {
|
|
2602
|
+
this.updateField(field.key, value)
|
|
2603
|
+
return
|
|
2604
|
+
}
|
|
2471
2605
|
this.updateField(field.key, value || null)
|
|
2472
2606
|
if (value) {
|
|
2473
2607
|
this.applyTemplateFill(field, value)
|
|
2474
2608
|
}
|
|
2475
2609
|
},
|
|
2476
2610
|
|
|
2611
|
+
/**
|
|
2612
|
+
* The value CnResourceSelect binds for a select-or-create reference:
|
|
2613
|
+
* an array of ids for a multi-value reference, else the id string.
|
|
2614
|
+
*
|
|
2615
|
+
* @param {object} field The reference field descriptor.
|
|
2616
|
+
* @return {string|string[]} The bound value.
|
|
2617
|
+
*/
|
|
2618
|
+
referenceModelValue(field) {
|
|
2619
|
+
const value = this.formData[field.key]
|
|
2620
|
+
if (field.reference && field.reference.multiple) {
|
|
2621
|
+
return Array.isArray(value) ? value.map((v) => String(v)) : []
|
|
2622
|
+
}
|
|
2623
|
+
return value !== null && value !== undefined ? String(value) : ''
|
|
2624
|
+
},
|
|
2625
|
+
|
|
2626
|
+
/**
|
|
2627
|
+
* "Create" on a select-or-create reference: open the referenced
|
|
2628
|
+
* schema's own form, prefilled with the typed term, on top of this one.
|
|
2629
|
+
*
|
|
2630
|
+
* Resolves with the created object once that form is saved, or with
|
|
2631
|
+
* null when it is closed without saving. CnResourceSelect then selects
|
|
2632
|
+
* the new object. When the referenced schema cannot be loaded, the
|
|
2633
|
+
* term alone is saved as before, so the field never dead-ends.
|
|
2634
|
+
*
|
|
2635
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
2636
|
+
* @param {object} field The reference field descriptor.
|
|
2637
|
+
* @param {string} term The typed search term.
|
|
2638
|
+
* @return {Promise<object|null>} The created object, or null.
|
|
2639
|
+
*/
|
|
2640
|
+
async openNestedCreate(field, term) {
|
|
2641
|
+
const store = this.getObjectStore()
|
|
2642
|
+
const register = this.referenceRegister(field)
|
|
2643
|
+
if (!store || !register || !field.reference) {
|
|
2644
|
+
return null
|
|
2645
|
+
}
|
|
2646
|
+
const schemaRef = String(field.reference.schema)
|
|
2647
|
+
const slug = store.createObjectTypeSlug(register, schemaRef)
|
|
2648
|
+
if (!store.objectTypeRegistry[slug]) {
|
|
2649
|
+
store.registerObjectType(slug, schemaRef, register)
|
|
2650
|
+
}
|
|
2651
|
+
const labelField = this.referenceLabelField(field)
|
|
2652
|
+
let schema = null
|
|
2653
|
+
try {
|
|
2654
|
+
schema = await store.fetchSchema(slug)
|
|
2655
|
+
} catch {
|
|
2656
|
+
schema = null
|
|
2657
|
+
}
|
|
2658
|
+
if (!schema || !schema.properties) {
|
|
2659
|
+
return store.saveObject(slug, { [labelField]: term })
|
|
2660
|
+
}
|
|
2661
|
+
const initialData = {}
|
|
2662
|
+
if (term && schema.properties[labelField]) {
|
|
2663
|
+
initialData[labelField] = term
|
|
2664
|
+
}
|
|
2665
|
+
return new Promise((resolve) => {
|
|
2666
|
+
this.nestedCreate = { field, schema, register, slug, initialData, resolve }
|
|
2667
|
+
})
|
|
2668
|
+
},
|
|
2669
|
+
|
|
2670
|
+
/**
|
|
2671
|
+
* The nested create form was confirmed: save the object, hand it to
|
|
2672
|
+
* the waiting picker and close the nested form. A refused save keeps
|
|
2673
|
+
* the nested form open with the server's message.
|
|
2674
|
+
*
|
|
2675
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
2676
|
+
* @param {object} formData The nested form's values.
|
|
2677
|
+
* @return {Promise<void>}
|
|
2678
|
+
*/
|
|
2679
|
+
async onNestedCreateConfirm(formData) {
|
|
2680
|
+
const pending = this.nestedCreate
|
|
2681
|
+
if (!pending) {
|
|
2682
|
+
return
|
|
2683
|
+
}
|
|
2684
|
+
const store = this.getObjectStore()
|
|
2685
|
+
const created = store ? await store.saveObject(pending.slug, formData) : null
|
|
2686
|
+
if (!created) {
|
|
2687
|
+
const error = store && store.errors ? store.errors[pending.slug] : null
|
|
2688
|
+
const message = (error && error.message)
|
|
2689
|
+
|| t('nextcloud-vue', 'The item could not be created.')
|
|
2690
|
+
const dialog = this.$refs.nestedCreateDialog
|
|
2691
|
+
if (dialog && typeof dialog.setValidationErrors === 'function') {
|
|
2692
|
+
dialog.setValidationErrors({}, message)
|
|
2693
|
+
}
|
|
2694
|
+
return
|
|
2695
|
+
}
|
|
2696
|
+
this.nestedCreate = null
|
|
2697
|
+
pending.resolve(created)
|
|
2698
|
+
},
|
|
2699
|
+
|
|
2700
|
+
/**
|
|
2701
|
+
* The nested create form was closed without saving: leave the field
|
|
2702
|
+
* as it was.
|
|
2703
|
+
*
|
|
2704
|
+
* @return {void}
|
|
2705
|
+
*/
|
|
2706
|
+
onNestedCreateClose() {
|
|
2707
|
+
const pending = this.nestedCreate
|
|
2708
|
+
this.nestedCreate = null
|
|
2709
|
+
if (pending) {
|
|
2710
|
+
pending.resolve(null)
|
|
2711
|
+
}
|
|
2712
|
+
},
|
|
2713
|
+
|
|
2477
2714
|
/**
|
|
2478
2715
|
* Copy template values off a selected/created reference object into the
|
|
2479
2716
|
* form, per the field's `fillFrom` map (`{ formKey: sourceKey }`). When
|
|
@@ -2920,9 +3157,14 @@ export default {
|
|
|
2920
3157
|
}
|
|
2921
3158
|
this.asyncState = newState
|
|
2922
3159
|
|
|
2923
|
-
// Trigger initial load for each async field
|
|
3160
|
+
// Trigger initial load for each async field. A select-or-create
|
|
3161
|
+
// reference loads its own options (CnResourceSelect), so it is
|
|
3162
|
+
// skipped here rather than fetched twice.
|
|
2924
3163
|
this.$nextTick(() => {
|
|
2925
3164
|
for (const field of fields) {
|
|
3165
|
+
if (field.allowCreate && field.reference) {
|
|
3166
|
+
continue
|
|
3167
|
+
}
|
|
2926
3168
|
if (this.isAsyncEnum(field) || this.isAsyncItemsEnum(field)) {
|
|
2927
3169
|
this.loadAsyncOptions(field, '')
|
|
2928
3170
|
}
|
|
@@ -2963,6 +3205,18 @@ export default {
|
|
|
2963
3205
|
if (Object.keys(labels).length > 0) {
|
|
2964
3206
|
this.referenceLabels = { ...this.referenceLabels, ...labels }
|
|
2965
3207
|
}
|
|
3208
|
+
} else if (this.isGroupField(field) || this.isGroupArrayField(field)) {
|
|
3209
|
+
// Nextcloud group: search groups, store the gid, show the name.
|
|
3210
|
+
results = await searchNextcloudGroups(query)
|
|
3211
|
+
const labels = {}
|
|
3212
|
+
for (const opt of results) {
|
|
3213
|
+
if (opt && opt.id) {
|
|
3214
|
+
labels[opt.id] = opt.label || String(opt.id)
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
if (Object.keys(labels).length > 0) {
|
|
3218
|
+
this.referenceLabels = { ...this.referenceLabels, ...labels }
|
|
3219
|
+
}
|
|
2966
3220
|
} else {
|
|
2967
3221
|
const enumFn = typeof field.enum === 'function' ? field.enum : field.items.enum
|
|
2968
3222
|
results = await enumFn(query)
|
|
@@ -3021,7 +3275,7 @@ export default {
|
|
|
3021
3275
|
* @return {object|null}
|
|
3022
3276
|
*/
|
|
3023
3277
|
getEffectiveSelectedOption(field) {
|
|
3024
|
-
if (this.
|
|
3278
|
+
if (this.isIdPickerField(field)) {
|
|
3025
3279
|
// Reference / user fields store the UUID/UID — resolve it to a
|
|
3026
3280
|
// display option `{ id, label }` (label from the resolved-labels
|
|
3027
3281
|
// cache, falling back to the id until it loads).
|
|
@@ -3048,7 +3302,7 @@ export default {
|
|
|
3048
3302
|
* @param {object|null} option The selected option
|
|
3049
3303
|
*/
|
|
3050
3304
|
onEffectiveSelectChange(field, option) {
|
|
3051
|
-
if (this.
|
|
3305
|
+
if (this.isIdPickerField(field)) {
|
|
3052
3306
|
// Reference / user fields store the chosen id (UUID / UID),
|
|
3053
3307
|
// not the full option. Cache its label so the selection displays.
|
|
3054
3308
|
if (option && option.id) {
|
|
@@ -3057,7 +3311,7 @@ export default {
|
|
|
3057
3311
|
this.updateField(field.key, option ? option.id : null)
|
|
3058
3312
|
// Reference options are label-only ({id,label}) — pass the id so
|
|
3059
3313
|
// template pre-fill fetches the full object.
|
|
3060
|
-
if (option && option.id) {
|
|
3314
|
+
if (option && option.id && this.isReferenceField(field)) {
|
|
3061
3315
|
this.applyTemplateFill(field, String(option.id))
|
|
3062
3316
|
}
|
|
3063
3317
|
} else if (this.isAsyncEnum(field)) {
|
|
@@ -3089,7 +3343,7 @@ export default {
|
|
|
3089
3343
|
* @return {Array}
|
|
3090
3344
|
*/
|
|
3091
3345
|
getEffectiveSelectedArrayOptions(field) {
|
|
3092
|
-
if (this.
|
|
3346
|
+
if (this.isIdPickerArrayField(field)) {
|
|
3093
3347
|
// Reference / user arrays store an array of ids (UUIDs / UIDs) —
|
|
3094
3348
|
// resolve each to a display option `{ id, label }`.
|
|
3095
3349
|
const uuids = this.formData[field.key]
|
|
@@ -3112,7 +3366,7 @@ export default {
|
|
|
3112
3366
|
* @param {Array} options The selected options
|
|
3113
3367
|
*/
|
|
3114
3368
|
onEffectiveMultiSelectChange(field, options) {
|
|
3115
|
-
if (this.
|
|
3369
|
+
if (this.isIdPickerArrayField(field)) {
|
|
3116
3370
|
// Reference / user arrays store an array of ids (UUIDs / UIDs).
|
|
3117
3371
|
// Cache labels so the chips still display the human names.
|
|
3118
3372
|
const list = options || []
|
|
@@ -3408,6 +3662,11 @@ export default {
|
|
|
3408
3662
|
margin-inline-end: auto;
|
|
3409
3663
|
}
|
|
3410
3664
|
|
|
3665
|
+
.cn-form-dialog__switch-wrapper {
|
|
3666
|
+
display: flex;
|
|
3667
|
+
flex-direction: column;
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3411
3670
|
.cn-form-dialog__form {
|
|
3412
3671
|
display: flex;
|
|
3413
3672
|
flex-direction: column;
|
|
@@ -450,6 +450,10 @@
|
|
|
450
450
|
:includeColumns="includeColumns"
|
|
451
451
|
:columnOverrides="columnOverrides"
|
|
452
452
|
:rowClass="rowClass"
|
|
453
|
+
:filterable="headerFilters && !!effectiveSchema"
|
|
454
|
+
:activeFilters="effectiveActiveFilters"
|
|
455
|
+
:filterRegister="typeof register === 'string' ? register : ''"
|
|
456
|
+
@columnFilter="onColumnFilterEvent"
|
|
453
457
|
@sort="onSortEvent"
|
|
454
458
|
@select="onSelect"
|
|
455
459
|
@rowClick="onRowClick"
|
|
@@ -798,6 +802,7 @@ import { DEFAULT_ROW_INDICATOR_CAP } from '../../utils/rowIndicators.js'
|
|
|
798
802
|
import { buildRouteQueryFromViewState, buildViewCreatePayload, extractViewState, extractViewStateFromRouteQuery, savedViewScope, viewMatchesScope } from '../../utils/savedViewHelpers.js'
|
|
799
803
|
import { columnsFromSchema } from '../../utils/schema.js'
|
|
800
804
|
import { resolveScopeLayout } from '../../utils/scopeListLayout.js'
|
|
805
|
+
import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js'
|
|
801
806
|
import { CnActionsBar } from '../CnActionsBar/index.js'
|
|
802
807
|
import { CnAdvancedFormDialog } from '../CnAdvancedFormDialog/index.js'
|
|
803
808
|
import { CnBoardView } from '../CnBoardView/index.js'
|
|
@@ -2285,6 +2290,22 @@ export default {
|
|
|
2285
2290
|
default: false,
|
|
2286
2291
|
},
|
|
2287
2292
|
|
|
2293
|
+
/**
|
|
2294
|
+
* Per-column filters in the table header: a filter button on every
|
|
2295
|
+
* column that can filter, opening a small panel that fits the column
|
|
2296
|
+
* (checkboxes, yes/no/any, from and to, a searchable reference list).
|
|
2297
|
+
* On by default; a column opts out with `filterable: false`, a page
|
|
2298
|
+
* with `pages[].config.headerFilters: false`. Filters write into the
|
|
2299
|
+
* same active-filter map as the facet sidebar, so they persist in the
|
|
2300
|
+
* route query and reach the same OpenRegister query parameters.
|
|
2301
|
+
*
|
|
2302
|
+
* @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-every-backed-column-filters-from-its-header
|
|
2303
|
+
*/
|
|
2304
|
+
headerFilters: {
|
|
2305
|
+
type: Boolean,
|
|
2306
|
+
default: true,
|
|
2307
|
+
},
|
|
2308
|
+
|
|
2288
2309
|
/**
|
|
2289
2310
|
* Show a filter menu (funnel button) in the table header, above the
|
|
2290
2311
|
* row-actions column. Its menu lists every enum/badge column's values as
|
|
@@ -5225,6 +5246,39 @@ export default {
|
|
|
5225
5246
|
this.$emit('filter-change', payload)
|
|
5226
5247
|
},
|
|
5227
5248
|
|
|
5249
|
+
/**
|
|
5250
|
+
* A header filter was applied or cleared. One column can own several
|
|
5251
|
+
* query parameters (a range is `key[gte]` and `key[lte]`), so they are
|
|
5252
|
+
* applied together: one fetch, one route update.
|
|
5253
|
+
*
|
|
5254
|
+
* @param {{key: string, params: object}} payload From CnDataTable's `column-filter`.
|
|
5255
|
+
* @return {void}
|
|
5256
|
+
* @spec openspec/changes/table-header-sort-and-filter/specs/cn-data-table/spec.md#requirement-a-header-filter-speaks-the-sidebars-query-language
|
|
5257
|
+
*/
|
|
5258
|
+
onColumnFilterEvent(payload) {
|
|
5259
|
+
const params = (payload && payload.params) || {}
|
|
5260
|
+
const entries = Object.entries(params).map(([key, values]) => [key, Array.isArray(values) ? values : []])
|
|
5261
|
+
if (this.isSelfFetchMode) {
|
|
5262
|
+
const next = { ...this.list.activeFilters.value }
|
|
5263
|
+
for (const [key, values] of entries) {
|
|
5264
|
+
if (values.length === 0) {
|
|
5265
|
+
delete next[key]
|
|
5266
|
+
} else {
|
|
5267
|
+
next[key] = values
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5270
|
+
this.list.activeFilters.value = next
|
|
5271
|
+
this.list.refresh(1)
|
|
5272
|
+
this.persistViewStateToRoute(this.currentViewState())
|
|
5273
|
+
}
|
|
5274
|
+
for (const [key, values] of entries) {
|
|
5275
|
+
if (this.isNamedSource) {
|
|
5276
|
+
this.setNamedFilter(key, values)
|
|
5277
|
+
}
|
|
5278
|
+
this.$emit('filter-change', { key, values })
|
|
5279
|
+
}
|
|
5280
|
+
},
|
|
5281
|
+
|
|
5228
5282
|
/**
|
|
5229
5283
|
* Record one sidebar choice on a named-source page and mirror it into
|
|
5230
5284
|
* the URL.
|
|
@@ -6460,8 +6514,7 @@ export default {
|
|
|
6460
6514
|
if (typeof window === 'undefined' || !created) {
|
|
6461
6515
|
return
|
|
6462
6516
|
}
|
|
6463
|
-
|
|
6464
|
-
window.dispatchEvent(new CustomEvent('cn-walkthrough:object-created', { detail }))
|
|
6517
|
+
dispatchObjectCreated({ register: this.register, schema: this.exportSchemaSlug, object: created })
|
|
6465
6518
|
},
|
|
6466
6519
|
|
|
6467
6520
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js'
|
|
1
2
|
import {
|
|
2
3
|
cloneObjectForCopy,
|
|
3
4
|
runSelfExportRequest,
|
|
@@ -196,9 +197,8 @@ export function createSelfModeActions(ctx) {
|
|
|
196
197
|
// @self.register/@self.schema are numeric DB ids, not slugs, so
|
|
197
198
|
// override with this page's own slug props (self-fetch mode
|
|
198
199
|
// guarantees ctx.schema() is already a string).
|
|
199
|
-
if (isCreate
|
|
200
|
-
|
|
201
|
-
window.dispatchEvent(new CustomEvent('cn-walkthrough:object-created', { detail }))
|
|
200
|
+
if (isCreate) {
|
|
201
|
+
dispatchObjectCreated({ register: ctx.register(), schema: ctx.schema(), object: saved })
|
|
202
202
|
}
|
|
203
203
|
refreshList(ctx)
|
|
204
204
|
if (isCreate && typeof ctx.afterCreateSuccess === 'function') {
|
|
@@ -268,6 +268,9 @@
|
|
|
268
268
|
ref="createDialog"
|
|
269
269
|
:schema="createSchema"
|
|
270
270
|
:item="null"
|
|
271
|
+
:register="content.register || ''"
|
|
272
|
+
:initialData="createInitialData"
|
|
273
|
+
:lockedFields="createLockedFields"
|
|
271
274
|
:size="formSize"
|
|
272
275
|
:columns="formColumns"
|
|
273
276
|
:includeFields="formIncludeFields"
|
|
@@ -293,6 +296,7 @@ import { followLinkClick, openRowTarget, resolveHref } from '../../utils/linkNav
|
|
|
293
296
|
import { objectFieldValue } from '../../utils/objectName.js'
|
|
294
297
|
import { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
295
298
|
import { markNewTabHandled } from '../../utils/rowAuxClick.js'
|
|
299
|
+
import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js'
|
|
296
300
|
import { CnRowActions } from '../CnRowActions/index.js'
|
|
297
301
|
|
|
298
302
|
/**
|
|
@@ -1010,6 +1014,39 @@ export default {
|
|
|
1010
1014
|
return Array.isArray(c.formExcludeFields) ? c.formExcludeFields : []
|
|
1011
1015
|
},
|
|
1012
1016
|
|
|
1017
|
+
/**
|
|
1018
|
+
* Create-form seed values: the list's scalar filter values (the parent
|
|
1019
|
+
* this list is scoped to, e.g. `{ lead: '<uuid>' }`), limited to keys
|
|
1020
|
+
* the schema declares, so the new row shows its parent already chosen.
|
|
1021
|
+
*
|
|
1022
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
1023
|
+
* @return {object}
|
|
1024
|
+
*/
|
|
1025
|
+
createInitialData() {
|
|
1026
|
+
const props = (this.createSchema && this.createSchema.properties) || {}
|
|
1027
|
+
const out = {}
|
|
1028
|
+
for (const [key, value] of Object.entries(this.resolvedFilter || {})) {
|
|
1029
|
+
if (!(key in props) || value === null || value === undefined || value === '' || typeof value === 'object') {
|
|
1030
|
+
continue
|
|
1031
|
+
}
|
|
1032
|
+
if (typeof value === 'string' && value.charAt(0) === '@') {
|
|
1033
|
+
continue
|
|
1034
|
+
}
|
|
1035
|
+
out[key] = value
|
|
1036
|
+
}
|
|
1037
|
+
return out
|
|
1038
|
+
},
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* The seeded parent keys are locked: a row added to this list belongs
|
|
1042
|
+
* to the record the list is scoped to.
|
|
1043
|
+
*
|
|
1044
|
+
* @return {string[]}
|
|
1045
|
+
*/
|
|
1046
|
+
createLockedFields() {
|
|
1047
|
+
return Object.keys(this.createInitialData)
|
|
1048
|
+
},
|
|
1049
|
+
|
|
1013
1050
|
/** Per-field overrides for the create dialog (`content.formFieldOverrides`). */
|
|
1014
1051
|
formFieldOverrides() {
|
|
1015
1052
|
const c = this.content || {}
|
|
@@ -1358,7 +1395,8 @@ export default {
|
|
|
1358
1395
|
}
|
|
1359
1396
|
}
|
|
1360
1397
|
const url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema })
|
|
1361
|
-
await axios.post(url, payload)
|
|
1398
|
+
const response = await axios.post(url, payload)
|
|
1399
|
+
dispatchObjectCreated({ register: c.register, schema: c.schema, object: (response && response.data) || payload })
|
|
1362
1400
|
if (this.$refs.createDialog) {
|
|
1363
1401
|
this.$refs.createDialog.setResult({ success: true })
|
|
1364
1402
|
}
|