[NAE-2060] Merge 6.2.10, 6.4.2 into 6.5.0#329
Conversation
- fix of resolving component portal for dashboards
…oice caseRef for 6.4.1 - add all functionality from branch NAE2047 to another release
- fix filter loading in case view - add option to load filter in caseRef/multichoice/enumeration fields
- add lazy loading image preview in file field
- nullpointer handling
[NAE-2031] Dashboard bug fix
[NAE-2056] Fix NAE-2048 Apply saved filter of advanced search in caseRef field, in release 6.4.1
[NAE-2057] Fix NAE-2047 Fulltext search and advanced search usability with multichoice caseRef, in release 6.4.1
- update CHANGELOG.md
- update changelog and version
- update release date in changelog
- implement exporting cases from case view - add export service - add attribute allow export to default tab view and default tabbed case view - add export button to default tabbed case view
- resolve comments - implement onDestroy - added translation - fix import
- resolve comments regarding translations
- fix test - update url for request
- add tests for export service
- update version - add changelog
NAE-2100 - Case view export button as NAE feature
…ut error message - fix reloading a non-existing task
- flatten the array with values of taskrefs
[NAE-2268] Fix resolving of referenced taskId on frontend
[NAE-2231] Unable to change behavior of taskRef on finish event witho…
…vent outcome - change forEach to for
# Conflicts: # projects/netgrif-components-core/src/lib/task-content/services/task-content.service.ts
[NAE-2393] Validation property on set data event
- increment version and changelog
[NAE-2101] release 6.4.2
- chanegs according the bug in release branch and some proposed fixes from coderabbit
- reverting most of the task [NAE-2393] Validation property on set data event - implement validation on setData only for button field
- fix according to PR
- add to changelog
[NAE-2101] release 6.4.2
[NAE-2401] Timestamp of case dataSet change
- fixes according to PR
[NAE-2101] release 6.4.2
# Conflicts: # CHANGELOG.md # package.json # projects/netgrif-components-core/package.json # projects/netgrif-components-core/src/lib/navigation/utility/filter-extraction.service.ts # projects/netgrif-components-core/src/lib/utility/navigation-item-task-filter-factory.ts # projects/netgrif-components/package.json # projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.ts # projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tab-view/default-tab-view.component.ts # projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.ts
- after merge fix
WalkthroughThis PR introduces case export functionality, extends header approval support for case views, adds task execution state tracking, refines filter composition, updates data models with new optional fields, and implements related UI wiring. The changes span the export service layer, approval synchronization, task panel improvements, filter merging, and tabbed case view export button integration. ChangesData Models and Interfaces
Export Service
Header Approval System
Task Panel and Execution State
Filter Composition and Navigation
Case View and Pagination
Tabbed Case View Export UI
Tests, Mocks, and Miscellaneous
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
projects/netgrif-components-core/src/lib/header/abstract-header.component.spec.ts (1)
40-90: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winConsider adding coverage for the new approval synchronization paths.
The existing spec only asserts that the component is created. With
CaseViewServiceandDATA_FIELD_PORTAL_DATAleft unprovided here, none of the new behavior (resolveApprovalDatafields,resolveApprovalValue,indeterminate,typeApproval, and the cleanup of_subCases) is exercised. Given the non-trivial subscription wiring and the_changeValueflag protocol, a focused test that provides a stubCaseViewService(with acases$BehaviorSubjectand acasesarray) plus aDATA_FIELD_PORTAL_DATAMultichoiceField mock would catch regressions in both directions of the bidirectional sync and verify unsubscription on destroy.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@projects/netgrif-components-core/src/lib/header/abstract-header.component.spec.ts` around lines 40 - 90, Add unit tests to cover the new approval synchronization paths by providing a stubbed CaseViewService and a DATA_FIELD_PORTAL_DATA mock so the component exercises resolveApprovalDatafields, resolveApprovalValue, indeterminate, typeApproval, and the _subCases cleanup; specifically, in the spec for TestHeaderComponent create a fake CaseViewService with a BehaviorSubject cases$ and an initial cases array, provide a MultichoiceField-like object for DATA_FIELD_PORTAL_DATA that triggers the bidirectional sync, assert both directions of value propagation (including _changeValue gating) and that subscriptions in _subCases are unsubscribed on component destroy; use TestBed providers to override CaseViewService and DATA_FIELD_PORTAL_DATA and add focused it(...) cases to validate the described behaviors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CHANGELOG.md`:
- Around line 47-52: The MD022 lint errors are caused by missing blank lines
after the subsection headings "### Fixed" and "### Changed" in CHANGELOG.md; fix
by inserting a single empty line immediately after each of those headings (i.e.,
place a blank line between "### Fixed" and its list and between "### Changed"
and its list) so each subsection heading is followed by a required blank line.
In `@projects/netgrif-components-core/src/lib/export/services/export.service.ts`:
- Around line 43-48: The code creates a blob URL with window.URL.createObjectURL
(urlBlob) and never revokes it, causing memory buildup; update the export logic
in ExportService (export.service.ts) so that after appending linkElement,
calling linkElement.click(), and removing it from document.body you also call
window.URL.revokeObjectURL(urlBlob) (optionally defer revoke with a short
setTimeout or revoke in a linkElement.onload handler to ensure the download has
started) to release the object URL and prevent memory leaks.
- Line 27: Replace the fragile any-cast accessing the private field by using the
public operator getter: where mergeOperation is computed from activeFilter,
check instanceof MergedFilter and read (activeFilter as MergedFilter).operator
instead of (activeFilter as any)._operator, defaulting to MergeOperator.AND;
update the expression that sets mergeOperation to reference the
MergedFilter.operator getter.
In
`@projects/netgrif-components-core/src/lib/header/abstract-header.component.ts`:
- Around line 219-227: The indeterminate() method uses unsafe property access
(this._dataFieldPortalData?.dataField.value.includes and .length on
value/choices) which can throw if value or choices are null/undefined; update
indeterminate() to mirror the null-safe pattern used in resolveApprovalValue():
use optional chaining on dataField, value and choices (e.g.
this._dataFieldPortalData?.dataField?.value?.includes(...),
this._dataFieldPortalData?.dataField?.value?.length, and
this._dataFieldPortalData?.dataField?.choices?.length) or coalesce lengths to 0
(e.g. (this._dataFieldPortalData?.dataField?.value?.length ?? 0) <
(this._dataFieldPortalData?.dataField?.choices?.length ?? 0)) so no property is
accessed on undefined; specifically fix the branches that reference
this._dataFieldPortalData?.dataField.value.includes(...) and the final length
comparison to use ?. or nullish coalescing as in resolveApprovalValue().
- Around line 262-267: The resolveApprovalValue method can throw when
dataField.value is null/undefined; update resolveApprovalValue to first verify
that this._dataFieldPortalData?.dataField?.value is present and is an
array/string supporting includes (e.g., Array.isArray(...) or typeof check)
before calling .includes, returning false if absent; apply the identical
defensive check to the indeterminate() method so both functions short-circuit
safely when dataField.value is unset.
- Around line 255-259: The enumeration branch currently subscribes to
approvalFormControl.valueChanges and unconditionally clears
_dataFieldPortalData.dataField.value; change it to first guard with the same
_caseViewService null check used in the multichoice branch, then use the emitted
value from approvalFormControl (the subscription parameter) to decide
action—e.g., only clear _dataFieldPortalData.dataField.value when the toggle is
false (or when appropriate for "unapproved"), and leave or synchronize the value
when true; reference approvalFormControl.valueChanges, _dataFieldPortalData,
EnumerationField, and _caseViewService when locating and updating the logic.
- Around line 233-260: resolveApprovalDatafields() currently creates multiple
subscriptions (approvalFormControl.valueChanges,
this._dataFieldPortalData.dataField.valueChanges(), and the existing _subCases)
but only _subCases is tracked; fix by collecting all subscriptions (e.g., add
them to a shared Subscription like this._subs or use an ngUnsubscribe Subject
with takeUntil) and ensure they are unsubscribed in ngOnDestroy; specifically
update resolveApprovalDatafields to add the approvalFormControl.valueChanges
subscriptions and the dataField.valueChanges() subscription to the shared
teardown mechanism and modify ngOnDestroy to unsubscribe/complete that shared
Subscription/Subject so the component no longer receives events after destroy.
- Around line 235-253: Replace the fragile _changeValue flag mutual-subscription
logic by using Angular FormControl's {emitEvent: false}: change all
approvalFormControl.setValue(this.resolveApprovalValue()) calls (initial, inside
the _dataFieldPortalData.dataField.valueChanges() subscriber, and inside the
_subCases subscription) to
approvalFormControl.setValue(this.resolveApprovalValue(), { emitEvent: false });
remove the _changeValue property and all assignments/conditionals that check it
(including the if (this._changeValue) branch inside
approvalFormControl.valueChanges subscription) so the valueChanges handler
always writes to this._dataFieldPortalData.dataField.value and the other
subscriptions update the form control without causing an echo.
In
`@projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts`:
- Around line 98-101: The JSDoc for the constant ITEM_FIELD_ID_CASE_ALLOW_EXPORT
is wrong (mentions "table mode"); update its comment to describe export
permission instead — e.g., "Boolean field that is true if export is allowed in
case view" — so it correctly reflects the constant's purpose (compare to
ITEM_FIELD_ID_CASE_ALLOW_TABLE_MODE for wording style).
In
`@projects/netgrif-components-core/src/lib/navigation/utility/filter-extraction.service.ts`:
- Around line 89-93: The recursive extraction currently merges filterData into
each local filterSegment (filterSegment.merge(filterData, MergeOperator.AND))
before recursing, which causes duplicate filter clauses; change
extractCompleteFilterFromData so it does not merge filterData at each
level—instead collect/merge only local filterSegments during recursion and after
the recursive call returns, merge the single filterData into the final combined
filter once (i.e., call merge(filterData, MergeOperator.AND) only on the result
of extractCompleteFilterFromData or merge at the top level), ensuring references
to filterSegment, filterData, MergeOperator.AND, and
extractCompleteFilterFromData remain consistent.
In `@projects/netgrif-components-core/src/lib/resources/interface/case.ts`:
- Line 24: The field lastModifiedDataSet has inconsistent shapes across fixtures
(sometimes undefined, sometimes an empty array); to fix, decide on a single
shape and align types and fixtures: either change the interface declaration to
lastModifiedDataSet?: NaeDate[] | undefined and update places that currently set
undefined to use [] for empty arrays (e.g., create-mock-case.ts,
abstract-case-panel.component.spec.ts), or keep the interface as
lastModifiedDataSet?: NaeDate | undefined and update mocks that use [] (e.g.,
create-mock-case.ts, panels.component.ts, abstract-case-panel.component.spec.ts)
to use undefined instead; update the interface and all usages of
lastModifiedDataSet accordingly so the type and runtime values are consistent.
- Line 24: Add a JSDoc comment for the interface field lastModifiedDataSet in
the Case interface explaining that it represents the timestamp when the case's
dataset (or data model) was last changed as opposed to lastModified which is the
timestamp for any case content update; state when it will be populated (e.g.,
updated when dataset/schema changes, otherwise undefined), intended consumers
(UI or sync logic needing dataset-change detection), and expected format/type
(NaeDate) and nullability; mirror style and tone of the existing lastModified
JSDoc so it's consistent and clear.
In
`@projects/netgrif-components-core/src/lib/search/search-service/search.service.ts`:
- Around line 307-313: The updateWithFullFilter method currently only sets
_activeFilter.next(newFilter) but does not update internal state like
_rootPredicate or _fullTextFilter nor trigger a search, which can make
additionalFiltersApplied and clearPredicates behave incorrectly; either (A)
update updateWithFullFilter to also synchronize _rootPredicate and
_fullTextFilter (and call the existing search trigger/path used by other filter
changes) so internal getters and methods remain consistent, or (B) if it is
intended to bypass the predicate tree, update the JSDoc for updateWithFullFilter
to explicitly state it bypasses predicate syncing, list side effects
(additionalFiltersApplied, clearPredicates, no automatic search trigger), and
advise when to use it versus the standard predicate API (e.g., for loading
pre-composed filters only).
In
`@projects/netgrif-components-core/src/lib/task-content/services/task-content.service.ts`:
- Around line 316-318: The code collects referencedTaskIds from TaskRefField
values and then dereferences each id without checking taskFieldsIndex; guard
against undefined entries by verifying this.taskFieldsIndex[id] exists and has a
.fields before calling this.findTaskRefId(taskId, ...). In the loop that
iterates referencedTaskIds (and before assigning taskRefId), skip or continue
for ids where this.taskFieldsIndex[id] is falsy or lacks a fields property to
avoid runtime crashes when TaskRefField.value is empty/stale.
In
`@projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts`:
- Around line 140-142: Add a JSDoc comment to the public getter cases to
document that it returns a synchronous snapshot of the internal _cases array
(not a live stream), that it may be an empty array before the first emission
from the cases$ observable, and advise consumers to subscribe to cases$ for
reactive updates; reference the getter name cases, the backing field _cases and
the observable cases$ in the comment for clarity.
In `@projects/netgrif-components/src/lib/header/header.component.ts`:
- Around line 31-32: The constructor parameter _dataFieldPortalData in
HeaderComponent is overly narrow; change its type from
DataFieldPortalData<MultichoiceField> to DataFieldPortalData<MultichoiceField |
EnumerationField> so the subclass matches AbstractHeaderComponent's contract;
update the generic on the injected symbol DATA_FIELD_PORTAL_DATA used in the
HeaderComponent constructor and ensure resolveApprovalDatafields() and
typeApproval() usages remain compatible with both MultichoiceField and
EnumerationField.
In
`@projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/refs/default-case-ref-list-view/default-case-ref-list-view.component.ts`:
- Around line 86-95: The constructor early return when !this._baseFilter ||
!this._baseFilter.filter can leave this.initFilter undefined; either ensure
initFilter is initialized to a safe default Filter instance there (e.g. new
Filter(...)) or remove the early return and set this.initFilter to a fallback,
and also add a defensive guard at the start of loadFilter that returns or skips
the merge if this.initFilter is falsy before calling this.initFilter.merge(...);
update references in the constructor branch handling this._baseFilter.filter and
in the loadFilter method to rely on the guaranteed/checked this.initFilter.
- Around line 92-94: The subscription to this._baseFilter.filter in
DefaultCaseRefListViewComponent is never stored or unsubscribed; store the
Subscription (e.g. assign the result of subscribe to a private field like
_baseFilterSub), implement OnDestroy on DefaultCaseRefListViewComponent, and
call this._baseFilterSub?.unsubscribe() inside ngOnDestroy to clean up the
observable and prevent the memory leak; ensure to import OnDestroy and
Subscription and update the class implements clause accordingly.
In
`@projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tab-view/default-tab-view.component.ts`:
- Line 95: The variable caseViewAllowExport is being extracted with the wrong
generic type boolean[]; update the extractFieldValueFromData call to use boolean
(not boolean[]) to match the
injected-tabbed-case-view-data-with-navigation-item-task-data.ts interface
(caseViewAllowExport: boolean). Locate the call to extractFieldValueFromData in
default-tab-view.component.ts (the const caseViewAllowExport assignment) and
change the generic type parameter to boolean, and then run type checks to ensure
downstream code using caseViewAllowExport expects a single boolean (adjust any
usages if they assumed an array).
---
Outside diff comments:
In
`@projects/netgrif-components-core/src/lib/header/abstract-header.component.spec.ts`:
- Around line 40-90: Add unit tests to cover the new approval synchronization
paths by providing a stubbed CaseViewService and a DATA_FIELD_PORTAL_DATA mock
so the component exercises resolveApprovalDatafields, resolveApprovalValue,
indeterminate, typeApproval, and the _subCases cleanup; specifically, in the
spec for TestHeaderComponent create a fake CaseViewService with a
BehaviorSubject cases$ and an initial cases array, provide a
MultichoiceField-like object for DATA_FIELD_PORTAL_DATA that triggers the
bidirectional sync, assert both directions of value propagation (including
_changeValue gating) and that subscriptions in _subCases are unsubscribed on
component destroy; use TestBed providers to override CaseViewService and
DATA_FIELD_PORTAL_DATA and add focused it(...) cases to validate the described
behaviors.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 24fda076-9734-4e71-bcd4-92cbc62496d6
📒 Files selected for processing (47)
CHANGELOG.mdprojects/nae-example-app/src/app/doc/panels/panels.component.tsprojects/netgrif-components-core/src/assets/i18n/de.jsonprojects/netgrif-components-core/src/assets/i18n/en.jsonprojects/netgrif-components-core/src/assets/i18n/sk.jsonprojects/netgrif-components-core/src/lib/authorization/permission/permission.service.spec.tsprojects/netgrif-components-core/src/lib/data-fields/case-ref-field/model/abstract-case-ref-base-field-component.tsprojects/netgrif-components-core/src/lib/data-fields/file-list-field/file-list-default-field/abstract-file-list-default-field.component.tsprojects/netgrif-components-core/src/lib/event/model/event-outcomes/task-outcomes/finish-task-event-outcome.tsprojects/netgrif-components-core/src/lib/export/public-api.tsprojects/netgrif-components-core/src/lib/export/services/export.service.spec.tsprojects/netgrif-components-core/src/lib/export/services/export.service.tsprojects/netgrif-components-core/src/lib/header/abstract-header.component.spec.tsprojects/netgrif-components-core/src/lib/header/abstract-header.component.tsprojects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.tsprojects/netgrif-components-core/src/lib/navigation/utility/filter-extraction.service.tsprojects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.spec.tsprojects/netgrif-components-core/src/lib/panel/task-panel-list/task-panel-list-pagination/abstract-task-list-pagination.component.tsprojects/netgrif-components-core/src/lib/panel/task-panel/abstract-task-panel.component.spec.tsprojects/netgrif-components-core/src/lib/panel/task-panel/abstract-task-panel.component.tsprojects/netgrif-components-core/src/lib/resources/interface/case.tsprojects/netgrif-components-core/src/lib/resources/interface/task.tsprojects/netgrif-components-core/src/lib/search/search-service/search.service.tsprojects/netgrif-components-core/src/lib/task-content/services/task-content.service.tsprojects/netgrif-components-core/src/lib/task/services/finish-task.service.tsprojects/netgrif-components-core/src/lib/task/services/task-data.service.tsprojects/netgrif-components-core/src/lib/utility/navigation-item-task-filter-factory.tsprojects/netgrif-components-core/src/lib/utility/tests/mocks/mock-case-resource.service.tsprojects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-case.tsprojects/netgrif-components-core/src/lib/view/case-view/components/case-list-paginator/abstract-case-list-paginator.component.tsprojects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.tsprojects/netgrif-components-core/src/public-api.tsprojects/netgrif-components/src/lib/data-fields/file-field/file-default-field/file-default-field.component.htmlprojects/netgrif-components/src/lib/data-fields/task-ref-field/task-ref-dashboard-field/task-ref-dashboard-field.component.tsprojects/netgrif-components/src/lib/header/header.component.tsprojects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/factory-methods.tsprojects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/model/injected-tabbed-case-view-data-with-navigation-item-task-data.tsprojects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/refs/default-case-ref-list-view/default-case-ref-list-view.component.htmlprojects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/refs/default-case-ref-list-view/default-case-ref-list-view.component.tsprojects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tab-view/default-tab-view.component.spec.tsprojects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tab-view/default-tab-view.component.tsprojects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.htmlprojects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.scssprojects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.spec.tsprojects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.tsprojects/netgrif-components/src/lib/panel/task-panel/task-panel.component.tsprojects/netgrif-components/src/lib/view/tree-case-view/tree-component/add-child-node/add-child-node.component.spec.ts
💤 Files with no reviewable changes (1)
- projects/netgrif-components-core/src/lib/data-fields/file-list-field/file-list-default-field/abstract-file-list-default-field.component.ts
| ### Fixed | ||
| - [NAE-2031] Dashboard bug fix | ||
|
|
||
| ### Changed | ||
| - [NAE-2047] Fulltext search and advanced search usability with multichoice caseRef | ||
| - [NAE-2048] Apply saved filter of advanced search in caseRef field |
There was a problem hiding this comment.
Fix markdownlint MD022 in 6.4.1 subsection headings.
Line 47 and Line 50 headings are missing required blank lines below, which will keep markdown lint warnings active.
Suggested patch
### Fixed
+
- [NAE-2031] Dashboard bug fix
### Changed
+
- [NAE-2047] Fulltext search and advanced search usability with multichoice caseRef
- [NAE-2048] Apply saved filter of advanced search in caseRef field📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ### Fixed | |
| - [NAE-2031] Dashboard bug fix | |
| ### Changed | |
| - [NAE-2047] Fulltext search and advanced search usability with multichoice caseRef | |
| - [NAE-2048] Apply saved filter of advanced search in caseRef field | |
| ### Fixed | |
| - [NAE-2031] Dashboard bug fix | |
| ### Changed | |
| - [NAE-2047] Fulltext search and advanced search usability with multichoice caseRef | |
| - [NAE-2048] Apply saved filter of advanced search in caseRef field |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 47-47: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 50-50: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@CHANGELOG.md` around lines 47 - 52, The MD022 lint errors are caused by
missing blank lines after the subsection headings "### Fixed" and "### Changed"
in CHANGELOG.md; fix by inserting a single empty line immediately after each of
those headings (i.e., place a blank line between "### Fixed" and its list and
between "### Changed" and its list) so each subsection heading is followed by a
required blank line.
| } | ||
|
|
||
| public downloadExcelFromCurrentSelection(activeFilter: Filter, currentHeaders: Array<HeaderColumn>): Observable<boolean> { | ||
| const mergeOperation = activeFilter instanceof MergedFilter ? (activeFilter as any)._operator : MergeOperator.AND; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate MergedFilter definition(s)
fd -i "merged-filter.ts" | xargs -r -I{} sh -c 'echo "=== {} ==="; sed -n "1,220p" "{}"'
# Find direct internal-field usage patterns
rg -n -C2 '\._operator\b|as any\)\._operator\b|instanceof\s+MergedFilter' --type=tsRepository: netgrif/components
Length of output: 11999
Use the public operator getter instead of accessing private _operator via any cast.
Line 27 casts to any to access the private _operator field, which is unnecessarily fragile and bypasses type safety. The MergedFilter class provides a public getter:
const mergeOperation = activeFilter instanceof MergedFilter ? (activeFilter as MergedFilter).operator : MergeOperator.AND;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@projects/netgrif-components-core/src/lib/export/services/export.service.ts`
at line 27, Replace the fragile any-cast accessing the private field by using
the public operator getter: where mergeOperation is computed from activeFilter,
check instanceof MergedFilter and read (activeFilter as MergedFilter).operator
instead of (activeFilter as any)._operator, defaulting to MergeOperator.AND;
update the expression that sets mergeOperation to reference the
MergedFilter.operator getter.
| const urlBlob = window.URL.createObjectURL(blob); | ||
| linkElement.setAttribute('href', urlBlob); | ||
| linkElement.setAttribute('download', 'export.xlsx'); | ||
| document.body.appendChild(linkElement); | ||
| linkElement.click(); | ||
| document.body.removeChild(linkElement); |
There was a problem hiding this comment.
Revoke object URLs after triggering the download.
createObjectURL on Line 43 is never revoked, so repeated exports can accumulate browser memory.
Suggested patch
const urlBlob = window.URL.createObjectURL(blob);
linkElement.setAttribute('href', urlBlob);
linkElement.setAttribute('download', 'export.xlsx');
document.body.appendChild(linkElement);
linkElement.click();
document.body.removeChild(linkElement);
+ window.URL.revokeObjectURL(urlBlob);
return of(true);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const urlBlob = window.URL.createObjectURL(blob); | |
| linkElement.setAttribute('href', urlBlob); | |
| linkElement.setAttribute('download', 'export.xlsx'); | |
| document.body.appendChild(linkElement); | |
| linkElement.click(); | |
| document.body.removeChild(linkElement); | |
| const urlBlob = window.URL.createObjectURL(blob); | |
| linkElement.setAttribute('href', urlBlob); | |
| linkElement.setAttribute('download', 'export.xlsx'); | |
| document.body.appendChild(linkElement); | |
| linkElement.click(); | |
| document.body.removeChild(linkElement); | |
| window.URL.revokeObjectURL(urlBlob); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@projects/netgrif-components-core/src/lib/export/services/export.service.ts`
around lines 43 - 48, The code creates a blob URL with
window.URL.createObjectURL (urlBlob) and never revokes it, causing memory
buildup; update the export logic in ExportService (export.service.ts) so that
after appending linkElement, calling linkElement.click(), and removing it from
document.body you also call window.URL.revokeObjectURL(urlBlob) (optionally
defer revoke with a short setTimeout or revoke in a linkElement.onload handler
to ensure the download has started) to release the object URL and prevent memory
leaks.
| public indeterminate() { | ||
| if (this._caseViewService) { | ||
| return this._dataFieldPortalData?.dataField?.value?.length > 0 && | ||
| this._caseViewService.cases.some(value => this._dataFieldPortalData?.dataField.value.includes(value.stringId)) && | ||
| !this.resolveApprovalValue(); | ||
| } | ||
| return this._dataFieldPortalData?.dataField?.value?.length > 0 && | ||
| this._dataFieldPortalData?.dataField?.value?.length < this._dataFieldPortalData?.dataField?.choices?.length; | ||
| } |
There was a problem hiding this comment.
Mirror the null-safe access fix here.
Line 222 has the same misplaced optional chaining as resolveApprovalValue() — this._dataFieldPortalData?.dataField.value.includes(...) will throw if dataField.value is null/undefined. Line 226 similarly accesses .length on value and choices after only chaining the outer object. Please apply the same defensive access pattern as suggested for resolveApprovalValue().
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@projects/netgrif-components-core/src/lib/header/abstract-header.component.ts`
around lines 219 - 227, The indeterminate() method uses unsafe property access
(this._dataFieldPortalData?.dataField.value.includes and .length on
value/choices) which can throw if value or choices are null/undefined; update
indeterminate() to mirror the null-safe pattern used in resolveApprovalValue():
use optional chaining on dataField, value and choices (e.g.
this._dataFieldPortalData?.dataField?.value?.includes(...),
this._dataFieldPortalData?.dataField?.value?.length, and
this._dataFieldPortalData?.dataField?.choices?.length) or coalesce lengths to 0
(e.g. (this._dataFieldPortalData?.dataField?.value?.length ?? 0) <
(this._dataFieldPortalData?.dataField?.choices?.length ?? 0)) so no property is
accessed on undefined; specifically fix the branches that reference
this._dataFieldPortalData?.dataField.value.includes(...) and the final length
comparison to use ?. or nullish coalescing as in resolveApprovalValue().
| protected resolveApprovalDatafields() { | ||
| if (this._dataFieldPortalData !== null && this._dataFieldPortalData.dataField instanceof MultichoiceField && this._caseViewService) { | ||
| this.approvalFormControl.setValue(this.resolveApprovalValue()); | ||
| this.approvalFormControl.valueChanges.subscribe(value => { | ||
| if (this._changeValue) { | ||
| if (value) { | ||
| this._dataFieldPortalData.dataField.value = this._caseViewService.cases.map(caze => caze.stringId); | ||
| } else { | ||
| this._dataFieldPortalData.dataField.value = []; | ||
| } | ||
| } | ||
| this._changeValue = true; | ||
| }) | ||
| this._dataFieldPortalData.dataField.valueChanges().subscribe(() => { | ||
| this._changeValue = false; | ||
| this.approvalFormControl.setValue(this.resolveApprovalValue()); | ||
| }) | ||
| this._subCases = this._caseViewService.cases$.subscribe(() => { | ||
| this._changeValue = false; | ||
| this.approvalFormControl.setValue(this.resolveApprovalValue()); | ||
| }) | ||
| } | ||
| if (this._dataFieldPortalData !== null && this._dataFieldPortalData.dataField instanceof EnumerationField) { | ||
| this.approvalFormControl.valueChanges.subscribe(value => { | ||
| this._dataFieldPortalData.dataField.value = null; | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Memory leak: three new subscriptions are never unsubscribed.
resolveApprovalDatafields() creates four subscriptions but only _subCases is tracked and cleaned up in ngOnDestroy. The two approvalFormControl.valueChanges.subscribe(...) (lines 236, 256) and this._dataFieldPortalData.dataField.valueChanges().subscribe(...) (line 246) keep references to the component (via this) after destroy. Since dataField and the form control can outlive the header component (e.g., when the portal data is provided by a parent), these subscriptions will leak and continue firing on a destroyed component, potentially writing to a destroyed field and double-firing the next time the header re-renders.
🔒 Proposed fix: track and dispose all subscriptions
protected _changeValue: boolean;
protected _subCases: Subscription;
+ protected _subApprovalControl: Subscription;
+ protected _subDataFieldValue: Subscription;
+ protected _subEnumApprovalControl: Subscription; ngOnDestroy(): void {
if (this.canOverflow) {
this.subColumnWidthControl.unsubscribe();
this.subColumnCountControl.unsubscribe();
this.subOverflowControl.unsubscribe();
}
- if (this._subCases) {
- this._subCases.unsubscribe();
- }
+ this._subCases?.unsubscribe();
+ this._subApprovalControl?.unsubscribe();
+ this._subDataFieldValue?.unsubscribe();
+ this._subEnumApprovalControl?.unsubscribe();
}- this.approvalFormControl.valueChanges.subscribe(value => {
+ this._subApprovalControl = this.approvalFormControl.valueChanges.subscribe(value => {
...
})
- this._dataFieldPortalData.dataField.valueChanges().subscribe(() => {
+ this._subDataFieldValue = this._dataFieldPortalData.dataField.valueChanges().subscribe(() => {
...
})
this._subCases = this._caseViewService.cases$.subscribe(() => {
...
})
}
if (this._dataFieldPortalData !== null && this._dataFieldPortalData.dataField instanceof EnumerationField) {
- this.approvalFormControl.valueChanges.subscribe(value => {
+ this._subEnumApprovalControl = this.approvalFormControl.valueChanges.subscribe(value => {
this._dataFieldPortalData.dataField.value = null;
})
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@projects/netgrif-components-core/src/lib/header/abstract-header.component.ts`
around lines 233 - 260, resolveApprovalDatafields() currently creates multiple
subscriptions (approvalFormControl.valueChanges,
this._dataFieldPortalData.dataField.valueChanges(), and the existing _subCases)
but only _subCases is tracked; fix by collecting all subscriptions (e.g., add
them to a shared Subscription like this._subs or use an ngUnsubscribe Subject
with takeUntil) and ensure they are unsubscribed in ngOnDestroy; specifically
update resolveApprovalDatafields to add the approvalFormControl.valueChanges
subscriptions and the dataField.valueChanges() subscription to the shared
teardown mechanism and modify ngOnDestroy to unsubscribe/complete that shared
Subscription/Subject so the component no longer receives events after destroy.
| public get cases(): Array<Case> { | ||
| return this._cases; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | 💤 Low value
Consider documenting the getter's behavior.
The new cases getter provides synchronous access to the cached cases array. Consider adding JSDoc to clarify:
- That it returns the current snapshot (not live updates)
- That it returns an empty array before the first emission from
cases$ - Whether consumers should prefer
cases$observable for reactive updates
📝 Suggested documentation
+ /**
+ * Returns the current snapshot of loaded cases.
+ * Returns an empty array if no cases have been loaded yet.
+ * For reactive updates, subscribe to {`@link` cases$} instead.
+ */
public get cases(): Array<Case> {
return this._cases;
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts`
around lines 140 - 142, Add a JSDoc comment to the public getter cases to
document that it returns a synchronous snapshot of the internal _cases array
(not a live stream), that it may be an empty array before the first emission
from the cases$ observable, and advise consumers to subscribe to cases$ for
reactive updates; reference the getter name cases, the backing field _cases and
the observable cases$ in the comment for clarity.
| @Optional() @Inject(DATA_FIELD_PORTAL_DATA) protected _dataFieldPortalData: DataFieldPortalData<MultichoiceField>) { | ||
| super(injector, translate, overflowService); | ||
| this._changeValue = true; | ||
| } | ||
|
|
||
| public indeterminate() { | ||
| return this._dataFieldPortalData?.dataField?.value?.length > 0 && | ||
| this._dataFieldPortalData?.dataField?.value?.length < this._dataFieldPortalData?.dataField?.choices?.length; | ||
| } | ||
|
|
||
| public typeApproval() { | ||
| return this._dataFieldPortalData?.dataField instanceof MultichoiceField ? 'multichoice' : 'enumeration'; | ||
| } | ||
|
|
||
| ngOnInit() { | ||
| super.ngOnInit(); | ||
| if (this._dataFieldPortalData !== null && this._dataFieldPortalData.dataField instanceof MultichoiceField) { | ||
| this.approvalFormControl.setValue(this._dataFieldPortalData?.dataField.value.length === this._dataFieldPortalData?.dataField.choices.length); | ||
| this.approvalFormControl.valueChanges.subscribe(value => { | ||
| if (this._changeValue) { | ||
| if (value) { | ||
| this._dataFieldPortalData.dataField.value = this._dataFieldPortalData?.dataField.choices.map(val => val.key); | ||
| } else { | ||
| this._dataFieldPortalData.dataField.value = []; | ||
| } | ||
| } | ||
| this._changeValue = true; | ||
| }) | ||
| this._dataFieldPortalData.dataField.valueChanges().subscribe(() => { | ||
| this._changeValue = false; | ||
| this.approvalFormControl.setValue(this._dataFieldPortalData?.dataField.value.length === this._dataFieldPortalData?.dataField.choices.length); | ||
| }) | ||
| } | ||
| if (this._dataFieldPortalData !== null && this._dataFieldPortalData.dataField instanceof EnumerationField) { | ||
| this.approvalFormControl.valueChanges.subscribe(value => { | ||
| this._dataFieldPortalData.dataField.value = null; | ||
| }) | ||
| } | ||
| super(injector, translate, overflowService, _caseViewService, _dataFieldPortalData); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm the variance / shape of DataFieldPortalData<T> to ensure the change is safe
rg -nP --type=ts -C5 'DataFieldPortalData\b' -g '*data-field-portal-data-injection-token*'
# Find all consumers passing DATA_FIELD_PORTAL_DATA to validate which field types are actually provided
rg -nP --type=ts -C3 'DATA_FIELD_PORTAL_DATA'Repository: netgrif/components
Length of output: 50375
🏁 Script executed:
fd -t f 'abstract.*header' -iRepository: netgrif/components
Length of output: 1177
🏁 Script executed:
rg -n 'class AbstractHeaderComponent' --type ts -A 15Repository: netgrif/components
Length of output: 1992
🏁 Script executed:
rg -n 'resolveApprovalDatafields' --type ts -B 2 -A 10Repository: netgrif/components
Length of output: 3319
🏁 Script executed:
rg -n '_dataFieldPortalData' 'projects/netgrif-components-core/src/lib/header/abstract-header.component.ts' -B 2 -A 2Repository: netgrif/components
Length of output: 2744
🏁 Script executed:
rg -n 'constructor' 'projects/netgrif-components-core/src/lib/header/abstract-header.component.ts' -A 20Repository: netgrif/components
Length of output: 1055
Change _dataFieldPortalData generic to match the base class.
AbstractHeaderComponent declares DataFieldPortalData<MultichoiceField | EnumerationField> and explicitly handles both types via instanceof checks in resolveApprovalDatafields() (lines 255–259) and typeApproval() (which returns 'enumeration' as fallback). Narrowing the generic to DataFieldPortalData<MultichoiceField> in HeaderComponent misrepresents the component's type contract—the base class supports EnumerationField, so the subclass should as well.
🔧 Proposed fix
- `@Optional`() `@Inject`(DATA_FIELD_PORTAL_DATA) protected _dataFieldPortalData: DataFieldPortalData<MultichoiceField>) {
+ `@Optional`() `@Inject`(DATA_FIELD_PORTAL_DATA) protected _dataFieldPortalData: DataFieldPortalData<MultichoiceField | EnumerationField>) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@projects/netgrif-components/src/lib/header/header.component.ts` around lines
31 - 32, The constructor parameter _dataFieldPortalData in HeaderComponent is
overly narrow; change its type from DataFieldPortalData<MultichoiceField> to
DataFieldPortalData<MultichoiceField | EnumerationField> so the subclass matches
AbstractHeaderComponent's contract; update the generic on the injected symbol
DATA_FIELD_PORTAL_DATA used in the HeaderComponent constructor and ensure
resolveApprovalDatafields() and typeApproval() usages remain compatible with
both MultichoiceField and EnumerationField.
| if (!this._baseFilter || !this._baseFilter.filter) { | ||
| return; | ||
| } | ||
| if (this._baseFilter.filter instanceof Filter) { | ||
| this.initFilter = this._baseFilter.filter | ||
| } else { | ||
| this._baseFilter.filter.subscribe(observableFilter => { | ||
| this.initFilter = observableFilter; | ||
| }); | ||
| } |
There was a problem hiding this comment.
Guard against undefined initFilter in constructor.
The early return on line 87 leaves initFilter undefined when _baseFilter or _baseFilter.filter doesn't exist. However, the loadFilter method at line 148 calls this.initFilter.merge(...) without checking if initFilter is defined, which will cause a null pointer exception.
🛡️ Proposed fix
this.search = !!_caseRefSearch;
this.createCase = !!_caseRefCreateCase;
- if (!this._baseFilter || !this._baseFilter.filter) {
- return;
- }
- if (this._baseFilter.filter instanceof Filter) {
+ if (this._baseFilter && this._baseFilter.filter) {
+ if (this._baseFilter.filter instanceof Filter) {
- this.initFilter = this._baseFilter.filter
+ this.initFilter = this._baseFilter.filter
- } else {
+ } else {
- this._baseFilter.filter.subscribe(observableFilter => {
+ this._baseFilter.filter.subscribe(observableFilter => {
- this.initFilter = observableFilter;
+ this.initFilter = observableFilter;
- });
+ });
+ }
}
}Additionally, add a guard in the loadFilter method:
loadFilter(filterData: SavedFilterMetadata) {
+ if (!this.initFilter) {
+ this.searchService.updateWithFullFilter(filterData.filter);
+ return;
+ }
this.searchService.updateWithFullFilter(this.initFilter.merge(filterData.filter, MergeOperator.AND));
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/refs/default-case-ref-list-view/default-case-ref-list-view.component.ts`
around lines 86 - 95, The constructor early return when !this._baseFilter ||
!this._baseFilter.filter can leave this.initFilter undefined; either ensure
initFilter is initialized to a safe default Filter instance there (e.g. new
Filter(...)) or remove the early return and set this.initFilter to a fallback,
and also add a defensive guard at the start of loadFilter that returns or skips
the merge if this.initFilter is falsy before calling this.initFilter.merge(...);
update references in the constructor branch handling this._baseFilter.filter and
in the loadFilter method to rely on the guaranteed/checked this.initFilter.
| this._baseFilter.filter.subscribe(observableFilter => { | ||
| this.initFilter = observableFilter; | ||
| }); |
There was a problem hiding this comment.
Memory leak: unsubscribed observable.
The subscription created for the observable filter in the constructor is never unsubscribed, causing a memory leak. The component should implement OnDestroy and clean up the subscription.
🔧 Proposed fix
Add a field to store the subscription:
private initFilter: Filter;
+ private _baseFilterSub: Subscription;Store and conditionally unsubscribe:
} else {
- this._baseFilter.filter.subscribe(observableFilter => {
+ this._baseFilterSub = this._baseFilter.filter.subscribe(observableFilter => {
this.initFilter = observableFilter;
});
}Implement OnDestroy:
import { ..., OnDestroy } from '@angular/core';
import { ..., Subscription } from 'rxjs';
export class DefaultCaseRefListViewComponent extends AbstractCaseViewComponent implements AfterViewInit, OnDestroy {
// ...
ngOnDestroy(): void {
this._baseFilterSub?.unsubscribe();
}
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/refs/default-case-ref-list-view/default-case-ref-list-view.component.ts`
around lines 92 - 94, The subscription to this._baseFilter.filter in
DefaultCaseRefListViewComponent is never stored or unsubscribed; store the
Subscription (e.g. assign the result of subscribe to a private field like
_baseFilterSub), implement OnDestroy on DefaultCaseRefListViewComponent, and
call this._baseFilterSub?.unsubscribe() inside ngOnDestroy to clean up the
observable and prevent the memory leak; ensure to import OnDestroy and
Subscription and update the class implements clause accordingly.
| const caseViewHeadersMode = extractFieldValueFromData<string[]>(viewDataGroups, GroupNavigationConstants.ITEM_FIELD_ID_CASE_HEADERS_MODE); | ||
| const caseViewAllowTableMode = extractFieldValueFromData<boolean>(viewDataGroups, GroupNavigationConstants.ITEM_FIELD_ID_CASE_ALLOW_TABLE_MODE); | ||
| const caseViewDefaultHeadersMode = extractFieldValueFromData<string[]>(viewDataGroups, GroupNavigationConstants.ITEM_FIELD_ID_CASE_DEFAULT_HEADERS_MODE); | ||
| const caseViewAllowExport = extractFieldValueFromData<boolean[]>(viewDataGroups, GroupNavigationConstants.ITEM_FIELD_ID_CASE_ALLOW_EXPORT); |
There was a problem hiding this comment.
Fix the type parameter mismatch.
The generic type parameter boolean[] is inconsistent with the interface definition at line 24 of injected-tabbed-case-view-data-with-navigation-item-task-data.ts, which defines caseViewAllowExport: boolean (not an array). This type mismatch could cause type errors or unexpected runtime behavior if the extracted value is actually an array.
🔧 Proposed fix
- const caseViewAllowExport = extractFieldValueFromData<boolean[]>(viewDataGroups, GroupNavigationConstants.ITEM_FIELD_ID_CASE_ALLOW_EXPORT);
+ const caseViewAllowExport = extractFieldValueFromData<boolean>(viewDataGroups, GroupNavigationConstants.ITEM_FIELD_ID_CASE_ALLOW_EXPORT);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const caseViewAllowExport = extractFieldValueFromData<boolean[]>(viewDataGroups, GroupNavigationConstants.ITEM_FIELD_ID_CASE_ALLOW_EXPORT); | |
| const caseViewAllowExport = extractFieldValueFromData<boolean>(viewDataGroups, GroupNavigationConstants.ITEM_FIELD_ID_CASE_ALLOW_EXPORT); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tab-view/default-tab-view.component.ts`
at line 95, The variable caseViewAllowExport is being extracted with the wrong
generic type boolean[]; update the extractFieldValueFromData call to use boolean
(not boolean[]) to match the
injected-tabbed-case-view-data-with-navigation-item-task-data.ts interface
(caseViewAllowExport: boolean). Locate the call to extractFieldValueFromData in
default-tab-view.component.ts (the const caseViewAllowExport assignment) and
change the generic type parameter to boolean, and then run type checks to ensure
downstream code using caseViewAllowExport expects a single boolean (adjust any
usages if they assumed an array).


Description
merge of versions 6.2.10 and 6.4.2 into 6.5.0
Implements NAE-2060
How Has Been This Tested?
Automated tests for version 6.5.0 were run on this branch
Test Configuration
Checklist:
Summary by CodeRabbit
Release Notes
New Features
Improvements
Bug Fixes