Content
View differences
Updated by Aleix Suau about 5 years ago
For work packages, fields can be configured as multiple selections for all projects or for individual projects. The same function is desired for user-defined fields of the type Project.
**Acceptance requirements**
* The administration of custom fields for projects gets a checkbox "Allow multi-select", just like the lists for work packages
* The project form is extended such that list custom fields can be multi-selected
* The project advanced settings is extended such that list custom fields can be multi-selected
* The project overview list/table is extended to show multi-select custom fields
* The project details widget on the project overview / dashboard displays custom lists with multiple selections.
* When copying projects, multi-selection custom fields are also copied
<figure class="image op-uc-figure"><div class="op-uc-figure--content"><img class="op-uc-image" src="/api/v3/attachments/20761/content"></div></figure>
### Development Notes
* Admin > Custom fields > Wps > multiselect checkbox
* Project > Overview > Project details should allow multi-select custom fields
* Form Config:
* [https://docs.openproject.org/api/endpoints/projects/](https://docs.openproject.org/api/endpoints/projects/)
* [https://docs.openproject.org/api/endpoints/projects/#projects-projects-schema](https://docs.openproject.org/api/endpoints/projects/#projects-projects-schema)
* [https://docs.openproject.org/api/endpoints/projects/#projects-project-create-form](https://docs.openproject.org/api/endpoints/projects/#projects-project-create-form)
* 3 steps:
* Replace view with formly
* Enable the backend capable of multiselect fields project
* Enable multiselect fields project in the frontend
### Dev requirements
* Change Sources:
* Layout:
* Schema loading (FormConfig):
When the user changes the type.
* First load
* Schema change
* Model:
* Schema loading (Payload (Model + Default values)) :
When the user changes the type.
* First load
* Schema change
* User interaction
* Valid result (In this order) = Form config + Form Payload + User Changes (that applies to the active Type)
* Whenever the Type is changes, we have to reapply the entire previous sequence.
* Whenever the form is submitted, the User Changes have to be reset
* Form component:
* 2 ways of getting its config:
* New >>> POST to _api/v3/projects/\[projectId\]/work\_packages/form_ with payload _{\_links: {type: {href: "/api/v3/types/1"}}}_
* Existing >>> POST _api/v3/work\_packages/\[wp\_id\]/form_ with payload _{lockVersion: 2, \_links: {}}_
* _DynamicFormsHub Use cases:_
* _Keeps a reference of every active form_
* _Registers a form when it is created_
* _Removes a form when it is destroyed_
* _Keeps the unsaved changes_
* On route changes, o\_nly when they are not discarded (maximize side panel).\_
* _How do we know that the change has not been discarded (alert)_
* _New edit inputs:_
* _What is EditFieldHandler.htmlId for? (Return a unique ID for this edit field), do we need it on every input of the dynamic form?_
* _\[disabled\]_\="inFlight"?
* EditFieldHandler.handleUserKeydown:
"Handle users pressing enter inside an edit mode. Outside an edit mode, the regular save event is captured by handleUserSubmit (submit event). In an edit mode, we can't derive from a submit event wheteher the user pressed enter (and on what field he did that).
* Seems that it is only implemented in the ckEditor
* SelectEditFieldComponent:
* 3 flavours:
* CreateAutocompleterComponent
* VersionAutocompleterComponent (+ @Output create + createNewVersion + ngselect.addTag)
* WorkPackageAutocompleterComponent
* Why/how do we sort option this.halSorting.sort(availableValues)?
* What is addEmptyOption()?
* Open directly this.\_autocompleterComponent.openDirectly = true;?
* Optimization:
* Manage results with pagination
* Skip extra calls when all results are returned (no pagination)
* FormattableEditFieldComponent
* editorType
* HalResource >> constrained
* ProjectResource >> 'statusExplanation' || 'description' >> full, other constrained)
* _WorkPackageBaseResource: fieldName_ === 'description' ? 'full' : 'constrained';
* CustomTextEditFieldService >> full
* What is previewContext for?
* Implement _edit-field-controls_
* _Do we have DateTime input?_
* _Validation:_
* Frontend validation (required, minlength...)
* Do we hardcode the messages in the front?
* Backend validation:
* Can a single input have multiple errors?
* How to know which property is a resource/\_link? Do they always come inside the payload?
### TODO
* [x] Hide non-writable fields
* [ ] Replace project setting's Information tab:
* [ ] exclude required disk storage
* [ ] Top left buttons (subproject, copy project...)
* [ ] Next steps:
* [ ] Clean useless code and do a draft PR (wp specific case)
* [x] Op-form-field
* [ ] Differentiate between update/create form (/id or href)
* [ ] Create routes:
* [x] Take the formId from the params?
* [x] Custom fields (starting with \[\])
* [x] Print form
* [x] Fill model
* [x] Default values
* [x] Group fields
* [x] Layout
* [x] Validation
* [x] Sync
* [x] Async
* [x] Custom component
* [x] Send to endpoint
* [x] Postprocess the model
* [x] Set default values if the user has deleted the default value
* [ ] New Inputs:
* [x] TextEditFieldComponent
* [x] IntegerEditFieldComponent
* [x] SelectEditFieldComponent
* [ ] MultiSelectEditFieldComponent
* [ ] DateEditFieldComponent
* [ ] two datepickers are displayed
* [ ] not close on blur
* [ ] displayed when invalid (also the rest of the project)
* [ ] not disabled properly when formControl.disable
* [ ] FormattableEditFieldComponent
* [ ] _onTouch not working this_.editor.ckEditorInstance.ui.focusTracker.on( 'change:isFocused'
* [x] FloatEditFieldComponent
* [x] BooleanEditFieldComponent
* [ ] Others
* [x] Translations
* [x] Manage focus
* [x] ng-select
* [x] Between fields with tabs (inline edit)
* [ ] Routes
* [x] Existing form route
* [ ] New form route
* [x] Redirect when new form is created
* [ ] Resolve data before entering the route?
* [ ] Check touched, disabled, change, onsubmit... on every new input (datepicker)
* [x] User feedback on submit success/error
* [ ] Tests
* [ ] Documentation
* [ ] Import only necessary fields from OpenprojectFieldsModule into DynamicFormsModule (EditFieldControlsComponent)
* [ ] Accesibility
* [ ] Content projection
* [ ] Review API responses Typings
* [ ] Where do we place the submit button?
* [ ] Remove '\_links' from the model in the front, just add them in the formatToSubmit process.
* [ ] Improve performance:
* [ ] CkEditor
* [ ] Dynamic form reloaded when new resource is created (because we redirect to the new url, the resourceId changes...)
* [ ] A <formly-field> tag is wrapping all the <formly-field> tags (could be related with the way the groups are defined (\_links...))
* [ ] Dynamic V2
* [ ] Save
* [ ] Save the entire form when it is new
* [ ] Save on every field edit when it has model
* [ ] Remove values that don't belong to the schema (e.g. if the user has filled some custom fields and then changed the type )
* [ ] Manage:
* [ ] LockVersion
* [ ] Id (on new wps)
* [ ] New Inputs:
* [ ] ProjectStatusEditFieldComponent
* [ ] WorkPackageCommentFieldComponent
* [ ] CombinedDateEditFieldComponent
* [ ] PlainFormattableEditFieldComponent
* [ ] TimeEntryWorkPackageEditFieldComponent
* [ ] DurationEditFieldComponent
* [ ] WorkPackageEditFieldComponent
* [ ] Combo inputs (country > province)
* [ ] Click and edit
* [ ] Unify options (select/multiselect) format: Different data format for options: sometimes they are resources, sometimes they don't, sometimes they have \_links.self.title other don't.
* [ ] Form inputs outside of the form (how do we deal with it?)
* [ ] Wp type
* [ ] Hide op-form-field wrapper when the formly-field is hidden dynamically
* [ ] I like the idea that the pathhelperService is the responsible of building the form path.I’d also like to provide a simple API so you can load a form without knowing anything about HAL resources or PathHelper. Both ideas are not incompatible, the component could have: @Input formHref, Input resource: ‘project’ | ‘user’ | …, Input resourceId, So it can be used easy in all the scenarios (loading from resource, loading form params, loading from template…)
* [ ] Handle dynamic resourceId @Input (currently if the resourceId is async (@resourceId="resourceId$ | async")) the call to the backend is done twice and we rely on race conditions (the call without id could return earlier and ew end up with a void form).
**Acceptance requirements**
* The administration of custom fields for projects gets a checkbox "Allow multi-select", just like the lists for work packages
* The project form is extended such that list custom fields can be multi-selected
* The project advanced settings is extended such that list custom fields can be multi-selected
* The project overview list/table is extended to show multi-select custom fields
* The project details widget on the project overview / dashboard displays custom lists with multiple selections.
* When copying projects, multi-selection custom fields are also copied
<figure class="image op-uc-figure"><div class="op-uc-figure--content"><img class="op-uc-image" src="/api/v3/attachments/20761/content"></div></figure>
### Development Notes
* Admin > Custom fields > Wps > multiselect checkbox
* Project > Overview > Project details should allow multi-select custom fields
* Form Config:
* [https://docs.openproject.org/api/endpoints/projects/](https://docs.openproject.org/api/endpoints/projects/)
* [https://docs.openproject.org/api/endpoints/projects/#projects-projects-schema](https://docs.openproject.org/api/endpoints/projects/#projects-projects-schema)
* [https://docs.openproject.org/api/endpoints/projects/#projects-project-create-form](https://docs.openproject.org/api/endpoints/projects/#projects-project-create-form)
* 3 steps:
* Replace view with formly
* Enable the backend capable of multiselect fields project
* Enable multiselect fields project in the frontend
### Dev requirements
* Change Sources:
* Layout:
* Schema loading (FormConfig):
When the user changes the type.
* First load
* Schema change
* Model:
* Schema loading (Payload (Model + Default values)) :
When the user changes the type.
* First load
* Schema change
* User interaction
* Valid result (In this order) = Form config + Form Payload + User Changes (that applies to the active Type)
* Whenever the Type is changes, we have to reapply the entire previous sequence.
* Whenever the form is submitted, the User Changes have to be reset
* Form component:
* 2 ways of getting its config:
* New >>> POST to _api/v3/projects/\[projectId\]/work\_packages/form_ with payload _{\_links: {type: {href: "/api/v3/types/1"}}}_
* Existing >>> POST _api/v3/work\_packages/\[wp\_id\]/form_ with payload _{lockVersion: 2, \_links: {}}_
* _DynamicFormsHub Use cases:_
* _Keeps a reference of every active form_
* _Registers a form when it is created_
* _Removes a form when it is destroyed_
* _Keeps the unsaved changes_
* On route changes, o\_nly when they are not discarded (maximize side panel).\_
* _How do we know that the change has not been discarded (alert)_
* _New edit inputs:_
* _What is EditFieldHandler.htmlId for? (Return a unique ID for this edit field), do we need it on every input of the dynamic form?_
* _\[disabled\]_\="inFlight"?
* EditFieldHandler.handleUserKeydown:
"Handle users pressing enter inside an edit mode. Outside an edit mode, the regular save event is captured by handleUserSubmit (submit event). In an edit mode, we can't derive from a submit event wheteher the user pressed enter (and on what field he did that).
* Seems that it is only implemented in the ckEditor
* SelectEditFieldComponent:
* 3 flavours:
* CreateAutocompleterComponent
* VersionAutocompleterComponent (+ @Output create + createNewVersion + ngselect.addTag)
* WorkPackageAutocompleterComponent
* Why/how do we sort option this.halSorting.sort(availableValues)?
* What is addEmptyOption()?
* Open directly this.\_autocompleterComponent.openDirectly = true;?
* Optimization:
* Manage results with pagination
* Skip extra calls when all results are returned (no pagination)
* FormattableEditFieldComponent
* editorType
* HalResource >> constrained
* ProjectResource >> 'statusExplanation' || 'description' >> full, other constrained)
* _WorkPackageBaseResource: fieldName_ === 'description' ? 'full' : 'constrained';
* CustomTextEditFieldService >> full
* What is previewContext for?
* Implement _edit-field-controls_
* _Do we have DateTime input?_
* _Validation:_
* Frontend validation (required, minlength...)
* Do we hardcode the messages in the front?
* Backend validation:
* Can a single input have multiple errors?
* How to know which property is a resource/\_link? Do they always come inside the payload?
### TODO
* [x] Hide non-writable fields
* [ ] Replace project setting's Information tab:
* [ ] exclude required disk storage
* [ ] Top left buttons (subproject, copy project...)
* [ ] Next steps:
* [ ] Clean useless code and do a draft PR (wp specific case)
* [x] Op-form-field
* [ ] Differentiate between update/create form (/id or href)
* [ ] Create routes:
* [x] Take the formId from the params?
* [x] Custom fields (starting with \[\])
* [x] Print form
* [x] Fill model
* [x] Default values
* [x] Group fields
* [x] Layout
* [x] Validation
* [x] Sync
* [x] Async
* [x] Custom component
* [x] Send to endpoint
* [x] Postprocess the model
* [x] Set default values if the user has deleted the default value
* [ ] New Inputs:
* [x] TextEditFieldComponent
* [x] IntegerEditFieldComponent
* [x] SelectEditFieldComponent
* [ ] MultiSelectEditFieldComponent
* [ ] DateEditFieldComponent
* [ ] two datepickers are displayed
* [ ] not close on blur
* [ ] displayed when invalid (also the rest of the project)
* [ ]
* [ ] FormattableEditFieldComponent
* [ ] _onTouch not working this_.editor.ckEditorInstance.ui.focusTracker.on( 'change:isFocused'
* [x] FloatEditFieldComponent
* [x] BooleanEditFieldComponent
* [ ] Others
* [x] Translations
* [x] Manage focus
* [x] ng-select
* [x] Between fields with tabs (inline edit)
* [ ] Routes
* [x] Existing form route
* [ ] New form route
* [x] Redirect when new form is created
* [ ] Resolve data before entering the route?
* [ ] Check touched, disabled, change, onsubmit... on every new input (datepicker)
* [x] User feedback on submit success/error
* [ ] Tests
* [ ] Documentation
* [ ] Import only necessary fields from OpenprojectFieldsModule into DynamicFormsModule (EditFieldControlsComponent)
* [ ] Accesibility
* [ ] Content projection
* [ ] Review API responses Typings
* [ ] Where do we place the submit button?
* [ ] Remove '\_links' from the model in the front, just add them in the formatToSubmit process.
* [ ] Improve performance:
* [ ] CkEditor
* [ ] Dynamic form reloaded when new resource is created (because we redirect to the new url, the resourceId changes...)
* [ ] A <formly-field> tag is wrapping all the <formly-field> tags (could be related with the way the groups are defined (\_links...))
* [ ] Dynamic V2
* [ ] Save
* [ ] Save the entire form when it is new
* [ ] Save on every field edit when it has model
* [ ] Remove values that don't belong to the schema (e.g. if the user has filled some custom fields and then changed the type )
* [ ] Manage:
* [ ] LockVersion
* [ ] Id (on new wps)
* [ ] New Inputs:
* [ ] ProjectStatusEditFieldComponent
* [ ] WorkPackageCommentFieldComponent
* [ ] CombinedDateEditFieldComponent
* [ ] PlainFormattableEditFieldComponent
* [ ] TimeEntryWorkPackageEditFieldComponent
* [ ] DurationEditFieldComponent
* [ ] WorkPackageEditFieldComponent
* [ ] Combo inputs (country > province)
* [ ] Click and edit
* [ ] Unify options (select/multiselect) format: Different data format for options: sometimes they are resources, sometimes they don't, sometimes they have \_links.self.title other don't.
* [ ] Form inputs outside of the form (how do we deal with it?)
* [ ] Wp type
* [ ] Hide op-form-field wrapper when the formly-field is hidden dynamically
* [ ] I like the idea that the pathhelperService is the responsible of building the form path.I’d also like to provide a simple API so you can load a form without knowing anything about HAL resources or PathHelper. Both ideas are not incompatible, the component could have: @Input formHref, Input resource: ‘project’ | ‘user’ | …, Input resourceId, So it can be used easy in all the scenarios (loading from resource, loading form params, loading from template…)
* [ ] Handle dynamic resourceId @Input (currently if the resourceId is async (@resourceId="resourceId$ | async")) the call to the backend is done twice and we rely on race conditions (the call without id could return earlier and ew end up with a void form).