diff --git a/app/components/editable-form.hbs b/app/components/editable-form.hbs index caa635db5..8437cf154 100644 --- a/app/components/editable-form.hbs +++ b/app/components/editable-form.hbs @@ -6,11 +6,27 @@ @onSave={{@onSave}} @formInitialized={{@formInitialized}} @customHistoryMessage={{@customHistoryMessage}} + @buildMetaTtl={{@buildMetaTtl}} + @saveTitle={{@saveTitle}} > {{#if this.editableFormsEnabled}} - + Voeg een veld toe + + + {{yield}} + + {{/if}} {{yield}} diff --git a/app/components/editable-form.js b/app/components/editable-form.js index d7aec8307..985726c33 100644 --- a/app/components/editable-form.js +++ b/app/components/editable-form.js @@ -1,7 +1,9 @@ import Component from '@glimmer/component'; + import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import { service } from '@ember/service'; + import { provide } from 'ember-provide-consume-context'; export default class EditableFormComponent extends Component { @@ -12,6 +14,8 @@ export default class EditableFormComponent extends Component { @service semanticFormRepository; @service features; + @tracked showModal; + constructor() { super(...arguments); this.baseFormId = this.args.form.id; @@ -27,6 +31,7 @@ export default class EditableFormComponent extends Component { ); this.currentForm = form; this.loading = false; + this.showModal = false; } get editableFormsEnabled() { @@ -38,4 +43,9 @@ export default class EditableFormComponent extends Component { onFormUpdate() { this.updateForm(); } + + @provide('form-definition') + get formDefinition() { + return this.currentForm; + } } diff --git a/app/components/generate-custom-field-button.hbs b/app/components/generate-custom-field-button.hbs deleted file mode 100644 index b3513605f..000000000 --- a/app/components/generate-custom-field-button.hbs +++ /dev/null @@ -1,91 +0,0 @@ - - Voeg een veld toe - -{{yield}} - - -
-
- Naam - -
-
- Uit bibliotheek - - {{selected.name}} - -
-
- Type - - {{selected.label}} - -
- - - - - - Bewaar - - - - Annuleer - - - - -
- -
\ No newline at end of file diff --git a/app/components/generate-custom-field-button.js b/app/components/generate-custom-field-button.js deleted file mode 100644 index 8bee9d2db..000000000 --- a/app/components/generate-custom-field-button.js +++ /dev/null @@ -1,136 +0,0 @@ -import Component from '@glimmer/component'; -import { action } from '@ember/object'; -import { service } from '@ember/service'; -import { tracked } from '@glimmer/tracking'; -import { JSON_API_TYPE } from 'frontend-lmb/utils/constants'; -import { showErrorToast } from 'frontend-lmb/utils/toasts'; - -import { TEXT_CUSTOM_DISPLAY_TYPE_ID } from 'frontend-lmb/utils/well-known-ids'; -import { ForkingStore } from '@lblod/ember-submission-form-fields'; -import { SOURCE_GRAPH } from 'frontend-lmb/utils/constants'; -import { PROV } from 'frontend-lmb/rdf/namespaces'; -export default class GenerateCustomFieldButtonComponent extends Component { - @service formReplacements; - @service store; - @service toaster; - - @tracked showModal = false; - @tracked loading = false; - @tracked fieldName = ''; - customFieldEntry = this.store.createRecord('library-entry', { - name: 'Eigen veld', - }); - @tracked selectedLibraryFieldType = this.customFieldEntry; - @tracked selectedDisplayType = null; - - constructor() { - super(...arguments); - this.displayTypes.then((displayTypes) => { - this.selectedDisplayType = displayTypes.findBy( - 'id', - TEXT_CUSTOM_DISPLAY_TYPE_ID - ); - }); - } - - get invalidName() { - return !this.fieldName || this.fieldName.trim().length < 1; - } - - get disabled() { - return this.invalidName; - } - - get libraryFieldOptions() { - const forkingStore = new ForkingStore(); - forkingStore.parse(this.args.form.formTtl, SOURCE_GRAPH, 'text/turtle'); - - const alreadyUsedLibraryEntries = forkingStore - .match(null, PROV('wasDerivedFrom'), null, SOURCE_GRAPH) - .map((triple) => triple.object.value); - - return this.store - .findAll('library-entry', { include: 'display-type' }) - .then((entries) => { - return [ - this.customFieldEntry, - ...entries - .sortBy('id') - .reverse() - .filter((entry) => { - return ( - entry.uri && alreadyUsedLibraryEntries.indexOf(entry.uri) < 0 - ); - }), - ]; - }); - } - - get displayTypes() { - return this.store.findAll('display-type').then((entries) => { - return entries.sortBy('label'); - }); - } - - @action - async onAddField() { - this.showModal = true; - this.fieldName = ''; - } - - @action - closeModal() { - this.showModal = false; - } - - @action - selectLibraryFieldType(libraryEntry) { - this.selectedLibraryFieldType = libraryEntry; - this.displayTypes.then((types) => { - this.selectedDisplayType = - types.findBy('uri', libraryEntry.get('displayType.uri')) || - types.findBy('id', TEXT_CUSTOM_DISPLAY_TYPE_ID); - }); - } - - @action - selectDisplayType(displayType) { - this.selectedDisplayType = displayType; - } - - @action async onSaveField() { - this.loading = true; - try { - const result = await fetch(`/form-content/${this.args.form.id}/fields`, { - method: 'POST', - headers: { - 'Content-Type': JSON_API_TYPE, - }, - body: JSON.stringify({ - displayType: this.selectedDisplayType.uri, - libraryEntryUri: this.selectedLibraryFieldType.uri, - order: 9000, - name: this.fieldName, - }), - }); - - const body = await result.json(); - const newFormId = body.id; - this.formReplacements.setReplacement(this.args.form.id, newFormId); - if (this.args.onUpdate) { - this.args.onUpdate(); - } - } catch (error) { - showErrorToast( - this.toaster, - 'Er ging iets mis bij het opslaan van het veld.' - ); - } - this.loading = false; - this.showModal = false; - } - - @action updateName(event) { - this.fieldName = event.target.value; - } -} diff --git a/app/components/rdf-input-fields/crud-custom-field-modal.hbs b/app/components/rdf-input-fields/crud-custom-field-modal.hbs new file mode 100644 index 000000000..938f3c431 --- /dev/null +++ b/app/components/rdf-input-fields/crud-custom-field-modal.hbs @@ -0,0 +1,117 @@ + + <:body> +
+ + Naam + + + {{#if @isCreating}} + + Uit bibliotheek + + {{selected.name}} + + + {{/if}} + + Type + + {{selected.label}} + + +
+ + <:footer> +
+ + + {{#if @isCreating}} + + Bewaar + + {{else}} + + Pas aan + + {{/if}} + + + Annuleer + + + {{#unless @isCreating}} + + Verwijder + + {{/unless}} +
+ + +
\ No newline at end of file diff --git a/app/components/rdf-input-fields/crud-custom-field-modal.js b/app/components/rdf-input-fields/crud-custom-field-modal.js new file mode 100644 index 000000000..426a2d2b7 --- /dev/null +++ b/app/components/rdf-input-fields/crud-custom-field-modal.js @@ -0,0 +1,254 @@ +import Component from '@glimmer/component'; + +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; +import { service } from '@ember/service'; + +import { task } from 'ember-concurrency'; +import { ForkingStore } from '@lblod/ember-submission-form-fields'; +import { consume } from 'ember-provide-consume-context'; + +import { JSON_API_TYPE, SOURCE_GRAPH } from 'frontend-lmb/utils/constants'; +import { PROV } from 'frontend-lmb/rdf/namespaces'; +import { showErrorToast } from 'frontend-lmb/utils/toasts'; +import { + LIBRARY_ENTREES, + TEXT_CUSTOM_DISPLAY_TYPE, +} from 'frontend-lmb/utils/well-known-uris'; + +export default class RdfInputFieldCrudCustomFieldModalComponent extends Component { + @consume('form-definition') formDefinition; + @consume('on-form-update') onFormUpdate; + + @service store; + @service toaster; + @service formReplacements; + + @tracked isRemovingField; + + customFieldEntry = this.store.createRecord('library-entry', { + name: 'Eigen veld', + }); + + @tracked fieldName; + @tracked libraryFieldType = this.customFieldEntry; + @tracked displayType; + + constructor() { + super(...arguments); + + let withValue = TEXT_CUSTOM_DISPLAY_TYPE; + if (!this.args.isCreating) { + const { label, displayType } = this.args.field; + + this.fieldName = label; + withValue = displayType; + } + + this.displayTypes.then((displayTypes) => { + this.displayType = displayTypes.findBy('uri', withValue); + }); + } + + @action + updateFieldName(event) { + this.fieldName = event.target?.value; + } + + updateField = task(async () => { + try { + await fetch(`/form-content/${this.formDefinition.id}/fields`, { + method: 'PUT', + headers: { + 'Content-Type': JSON_API_TYPE, + }, + body: JSON.stringify({ + field: this.args.field.uri.value, + displayType: this.displayType.uri, + name: this.fieldName, + }), + }); + this.onFormUpdate(); + } catch (error) { + showErrorToast( + this.toaster, + 'Er ging iets mis bij het opslaan van het veld.' + ); + return; + } + }); + + createField = task(async () => { + try { + const result = await fetch( + `/form-content/${this.formDefinition.id}/fields`, + { + method: 'POST', + headers: { + 'Content-Type': JSON_API_TYPE, + }, + body: JSON.stringify({ + displayType: this.displayType.uri, + libraryEntryUri: this.libraryFieldType.uri, + name: this.fieldName, + }), + } + ); + + const body = await result.json(); + const newFormId = body.id; + this.formReplacements.setReplacement(this.formDefinition.id, newFormId); + this.onFormUpdate(); + } catch (error) { + showErrorToast( + this.toaster, + 'Er ging iets mis bij het opslaan van het veld.' + ); + return; + } + }); + + @action + selectLibraryFieldType(libraryEntry) { + this.libraryFieldType = libraryEntry; + this.displayTypes.then((types) => { + this.displayType = + types.findBy('uri', libraryEntry.get('displayType.uri')) || + types.findBy('uri', TEXT_CUSTOM_DISPLAY_TYPE); + }); + } + + @action + selectDisplayType(displayType) { + this.displayType = displayType; + } + + @action + async onRemove() { + this.isRemovingField = true; + await fetch(`/form-content/fields`, { + method: 'DELETE', + headers: { + 'Content-Type': JSON_API_TYPE, + }, + body: JSON.stringify({ + fieldUri: this.args.field.uri.value, + formUri: this.args.form.uri, + }), + }); + this.onFormUpdate(); + this.isRemovingField = false; + } + + @action + closeModal() { + if (this.args.onCloseModal) { + this.fieldName = null; + this.libraryFieldType = null; + this.displayType = null; + this.args.onCloseModal(); + } + } + + get displayTypes() { + return this.store.findAll('display-type').then((entries) => { + return entries.sortBy('label'); + }); + } + + get libraryEntryUri() { + if (!this.args.field) { + return null; + } + + const localStore = new ForkingStore(); + localStore.parse(this.formDefinition.formTtl, SOURCE_GRAPH, 'text/turtle'); + const libraryEntree = localStore.any( + this.args.field.uri, + PROV('wasDerivedFrom'), + null, + SOURCE_GRAPH + ); + + return libraryEntree?.value; + } + + get libraryFieldOptions() { + const forkingStore = new ForkingStore(); + forkingStore.parse( + this.formDefinition.formTtl, + SOURCE_GRAPH, + 'text/turtle' + ); + + const alreadyUsedLibraryEntries = forkingStore + .match(null, PROV('wasDerivedFrom'), null, SOURCE_GRAPH) + .map((triple) => triple.object.value); + + return this.store + .findAll('library-entry', { include: 'display-type' }) + .then((entries) => { + return [ + this.customFieldEntry, + ...entries + .sortBy('id') + .reverse() + .filter((entry) => { + return ( + entry.uri && alreadyUsedLibraryEntries.indexOf(entry.uri) < 0 + ); + }), + ]; + }); + } + + get canSaveChanges() { + return ( + this.fieldHasChanged && this.hasValidFieldName && this.libraryFieldType + ); + } + + get fieldHasChanged() { + if (this.args.isCreating) { + return this.hasValidFieldName; + } + + if (this.canSelectTypeForEntry) { + return ( + this.hasValidFieldName && + (this.fieldName !== this.args.field.label || + this.displayType.uri !== this.args.field.displayType) + ); + } + + return this.hasValidFieldName && this.fieldName !== this.args.field.label; + } + + get hasValidFieldName() { + return this.fieldName && this.fieldName.trim().length > 1; + } + + get canSelectTypeForEntry() { + if (this.args.isCreating) { + return this.libraryFieldType === this.customFieldEntry; + } + + return !LIBRARY_ENTREES.includes(this.libraryEntryUri); + } + + get saveTooltipText() { + if (this.args.isCreating) { + return 'Vul eerst al de velden in'; + } + + return 'Geen aanpassingen gevonden'; + } + + get title() { + if (this.args.isCreating) { + return 'Voeg een veld toe'; + } + + return 'Pas een veld aan'; + } +} diff --git a/app/components/rdf-input-fields/custom-field-wrapper.hbs b/app/components/rdf-input-fields/custom-field-wrapper.hbs index 1908cd364..009aa956c 100644 --- a/app/components/rdf-input-fields/custom-field-wrapper.hbs +++ b/app/components/rdf-input-fields/custom-field-wrapper.hbs @@ -1,52 +1,50 @@ {{#unless this.removed}} - {{#if @inline}} - {{this.title}} -
-
- {{yield}} +
+
+
+ {{this.title}} + + Pas aan +
+
+
+ {{yield}} +
+
+
+
- Verwijder + Naar boven -
- {{else}} -
- {{this.title}} - Verwijder + Naar onder
- {{yield}} - {{/if}} +
+ {{#each this.errors as |error|}} {{error.resultMessage}} {{/each}} @@ -54,4 +52,12 @@ {{#each this.warnings as |warning|}} {{warning.resultMessage}} {{/each}} -{{/unless}} \ No newline at end of file +{{/unless}} + + \ No newline at end of file diff --git a/app/components/rdf-input-fields/custom-field-wrapper.js b/app/components/rdf-input-fields/custom-field-wrapper.js index 0fdd896f6..6e7879ee6 100644 --- a/app/components/rdf-input-fields/custom-field-wrapper.js +++ b/app/components/rdf-input-fields/custom-field-wrapper.js @@ -1,33 +1,40 @@ import Component from '@glimmer/component'; -import { action } from '@ember/object'; -import { service } from '@ember/service'; + import { tracked } from '@glimmer/tracking'; -import { JSON_API_TYPE } from 'frontend-lmb/utils/constants'; + import { consume } from 'ember-provide-consume-context'; +import { task } from 'ember-concurrency'; + +import { JSON_API_TYPE } from 'frontend-lmb/utils/constants'; export default class RdfInputFieldsCustomFieldWrapperComponent extends Component { - @service formReplacements; - @consume('on-form-update') onUpdate; + @consume('on-form-update') onFormUpdate; + @consume('form-definition') formDefinition; + + @tracked showModal; - @tracked removing = false; get title() { return this.args.field?.label; } - @action - async onRemove() { - this.removing = true; - await fetch(`/form-content/fields`, { - method: 'DELETE', - headers: { - 'Content-Type': JSON_API_TYPE, - }, - body: JSON.stringify({ - fieldUri: this.args.field.uri.value, - formUri: this.args.form.uri, - }), - }); - this.onUpdate(); - this.removed = true; - } + moveField = task(async (direction) => { + const result = await fetch( + `/form-content/${this.formDefinition.id}/fields/move`, + { + method: 'POST', + headers: { + 'Content-Type': JSON_API_TYPE, + }, + body: JSON.stringify({ + fieldUri: this.args.field.uri.value, + formUri: this.args.form.uri, + direction, + }), + } + ); + + if (result.ok) { + this.onFormUpdate(); + } + }); } diff --git a/app/styles/_shame.scss b/app/styles/_shame.scss index b6ca17cf2..972e7de01 100644 --- a/app/styles/_shame.scss +++ b/app/styles/_shame.scss @@ -371,3 +371,7 @@ $au-region-breakpoint: medium !default; justify-content: space-between; } } + +.au-form-row-full-width { + align-items: normal !important; +} diff --git a/app/templates/mandatarissen/persoon/mandataris.hbs b/app/templates/mandatarissen/persoon/mandataris.hbs index 1acc712bc..7e3bbbf34 100644 --- a/app/templates/mandatarissen/persoon/mandataris.hbs +++ b/app/templates/mandatarissen/persoon/mandataris.hbs @@ -142,7 +142,7 @@ {{/if}}
- `); + await render(hbs``); assert.dom(this.element).hasText(''); // Template block usage: - await render(hbs` + await render(hbs` template block text -`); +`); assert.dom(this.element).hasText('template block text'); }); diff --git a/tests/unit/models/form-library/display-type-test.js b/tests/unit/models/form-library/display-type-test.js new file mode 100644 index 000000000..02d973a66 --- /dev/null +++ b/tests/unit/models/form-library/display-type-test.js @@ -0,0 +1,14 @@ +import { module, test } from 'qunit'; + +import { setupTest } from 'frontend-lmb/tests/helpers'; + +module('Unit | Model | form library/display type', function (hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord('form-library/display-type', {}); + assert.ok(model); + }); +});