Skip to content

[NAE-2060] Merge 6.2.10, 6.4.2 into 6.5.0#329

Open
SamuelPalaj wants to merge 57 commits into
release/6.5.0from
NAE-2060
Open

[NAE-2060] Merge 6.2.10, 6.4.2 into 6.5.0#329
SamuelPalaj wants to merge 57 commits into
release/6.5.0from
NAE-2060

Conversation

@SamuelPalaj
Copy link
Copy Markdown
Contributor

@SamuelPalaj SamuelPalaj commented May 11, 2026

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

Name Tested on
OS windows 11
Runtime 20.19.0
Dependency Manager npm10.8.2
Framework version Angular 13.3
Run parameters
Other configuration

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • My changes have been checked, personally or remotely, with @...
  • I have commented my code, particularly in hard-to-understand areas
  • I have resolved all conflicts with the target branch of the PR
  • I have updated and synced my code with the target branch
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing tests pass locally with my changes:
    • Lint test
    • Unit tests
    • Integration tests
  • I have checked my contribution with code analysis tools:
  • I have made corresponding changes to the documentation:
    • Developer documentation
    • User Guides
    • Migration Guides

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Excel export functionality for filtered case selections with loading indicator
    • Expanded pagination page size options to include 100 items per page
    • Implemented lazy loading for image previews to improve performance
  • Improvements

    • Enhanced task execution state tracking for better visibility
    • Improved case modification data tracking and logging
    • Added optional header parameter support for enhanced field filtering
  • Bug Fixes

    • Fixed task reference field update behavior to respect task execution state
    • Corrected button field validation handling during data updates

Review Change Stack

Kovy95 and others added 30 commits January 9, 2025 14:51
- 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
[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
- 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…
# Conflicts:
#	projects/netgrif-components-core/src/lib/task-content/services/task-content.service.ts
Kovy95 and others added 18 commits April 2, 2026 12:14
[NAE-2393] Validation property on set data event
- increment version and changelog
- 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
[NAE-2401] Timestamp of case dataSet change
- fixes according to PR
# 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
@SamuelPalaj SamuelPalaj self-assigned this May 11, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Walkthrough

This 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.

Changes

Data Models and Interfaces

Layer / File(s) Summary
Case Interface
projects/netgrif-components-core/src/lib/resources/interface/case.ts
Add optional lastModifiedDataSet?: NaeDate field.
Task Interface
projects/netgrif-components-core/src/lib/resources/interface/task.ts
Add optional isStillExecutable?: boolean field.
FinishTaskEventOutcome Interface
projects/netgrif-components-core/src/lib/event/model/event-outcomes/task-outcomes/finish-task-event-outcome.ts
Add optional isTaskStillExecutable?: boolean property.

Export Service

Layer / File(s) Summary
Export Service Core
projects/netgrif-components-core/src/lib/export/services/export.service.ts
New ExportService provides downloadExcelFromCurrentSelection(activeFilter, currentHeaders) returning Observable<boolean>, handles merge operators, POSTs filtered selection to /export/filteredCases, creates downloadable Excel file via Blob and temporary anchor, and includes getResourceAddress helper for resource resolution.
Export Service Tests
projects/netgrif-components-core/src/lib/export/services/export.service.spec.ts
Test suite covers instantiation, getResourceAddress resolution from arrays/objects, and downloadExcelFromCurrentSelection success and failure cases.
Export Module Public API
projects/netgrif-components-core/src/lib/export/public-api.ts
Re-export ExportService under /* SERVICES */ section.
Core Library Public API
projects/netgrif-components-core/src/public-api.ts
Export the new lib/export/public-api to expose at top level.
Export i18n Translations
projects/netgrif-components-core/src/assets/i18n/de.json, en.json, sk.json
Add export.errorExportDownload translation keys in German, English, and Slovak for download failure messaging.

Header Approval System

Layer / File(s) Summary
AbstractHeaderComponent Approval Logic
projects/netgrif-components-core/src/lib/header/abstract-header.component.ts
Inject optional CaseViewService and DATA_FIELD_PORTAL_DATA token; add indeterminate() and typeApproval() helpers; implement resolveApprovalDatafields() to synchronize multichoice/enumeration field values with case membership; compute and track approval state via resolveApprovalValue(); manage _subCases subscription lifecycle.
AbstractHeaderComponent Test
projects/netgrif-components-core/src/lib/header/abstract-header.component.spec.ts
Update TestHeaderComponent to inject CaseViewService and DATA_FIELD_PORTAL_DATA; pass both to super().
HeaderComponent Simplification
projects/netgrif-components/src/lib/header/header.component.ts
Remove OnInit implementation, _changeValue property, and duplicate approval logic; pass _caseViewService and _dataFieldPortalData to base class, delegating approval support to AbstractHeaderComponent.

Task Panel and Execution State

Layer / File(s) Summary
AbstractTaskPanelComponent User Comparator
projects/netgrif-components-core/src/lib/panel/task-panel/abstract-task-panel.component.ts
Inject UserComparatorService; compute taskShouldBeBlocked via _callChain.create and compareUsers check; conditionally block fields via blockFields.
AbstractTaskPanelComponent Test
projects/netgrif-components-core/src/lib/panel/task-panel/abstract-task-panel.component.spec.ts
Update TestTaskPanelComponent to inject and pass UserComparatorService to superclass.
TaskContentService Execution State
projects/netgrif-components-core/src/lib/task-content/services/task-content.service.ts
updateStateData accepts optional isStillExecutable flag; updateField only emits reload for TASK_REF when task is executable; findTaskRefId uses for...of for early termination.
FinishTaskService Outcome
projects/netgrif-components-core/src/lib/task/services/finish-task.service.ts
Type outcome as FinishTaskEventOutcome; pass both outcome and isTaskStillExecutable to TaskContentService.updateStateData.
TaskDataService Field Validation
projects/netgrif-components-core/src/lib/task/services/task-data.service.ts
Add specialized ButtonField handling; suppress backend updates when validateData === 'true' and validation fails.
TaskPanelComponent
projects/netgrif-components/src/lib/panel/task-panel/task-panel.component.ts
Inject and forward UserComparatorService to superclass.

Filter Composition and Navigation

Layer / File(s) Summary
FilterExtractionService
projects/netgrif-components-core/src/lib/navigation/utility/filter-extraction.service.ts
Accept optional filterData parameter; merge it with extracted filterSegment via AND merge; thread it through recursive parent-filter extraction.
Navigation Item Task Filter Factory
projects/netgrif-components-core/src/lib/utility/navigation-item-task-filter-factory.ts
Accept optional filterData parameter; pass it to extractCompleteFilterFromData.
DefaultCaseRefListView
projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/.../default-case-ref-list-view/default-case-ref-list-view.component.ts, *.html
Inject optional NAE_BASE_FILTER; derive initFilter from it; add loadFilter method to merge saved filters with initFilter and update search service; bind template filterLoaded event.
Factory Methods
projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/.../model/factory-methods.ts
Pass tabData.loadFilter to navigationItemTaskFilterFactory.
SearchService Filter Update
projects/netgrif-components-core/src/lib/search/search-service/search.service.ts
Add updateWithFullFilter public method to directly update activeFilter observable with a provided Filter.

Case View and Pagination

Layer / File(s) Summary
CaseViewService In-Memory Cache
projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts
Add _cases array field; populate via tap in cases$ pipeline; expose via public cases getter.
Pagination Page Size Options
projects/netgrif-components-core/src/lib/panel/task-panel-list/task-panel-list-pagination/abstract-task-list-pagination.component.ts, .../abstract-case-list-paginator.component.ts
Expand pageSizeOptions to include 100.

Tabbed Case View Export UI

Layer / File(s) Summary
Navigation Constants Export Flag
projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts
Add ITEM_FIELD_ID_CASE_ALLOW_EXPORT = 'case_allow_export' enum member.
Injected Tab Config
projects/netgrif-components/src/lib/navigation/.../model/injected-tabbed-case-view-data-with-navigation-item-task-data.ts
Add caseViewAllowExport: boolean property.
DefaultTabViewComponent
projects/netgrif-components/src/lib/navigation/.../tabbed/default-tab-view/default-tab-view.component.ts, *.spec.ts
Read ITEM_FIELD_ID_CASE_ALLOW_EXPORT field; pass to injected config as caseViewAllowExport; add field to test mock.
DefaultTabbedCaseViewComponent Export Implementation
projects/netgrif-components/src/lib/navigation/.../tabbed/default-tabbed-case-view/default-tabbed-case-view.component.ts
Implement OnDestroy; add allowExport, loading$, _currentHeaders state; inject ExportService, _snackbar, _translate; implement isLoading() and export() methods calling ExportService.downloadExcelFromCurrentSelection, handling errors via LoggerService and translated snackbar; manage subscriptions.
DefaultTabbedCaseViewComponent UI
projects/netgrif-components/src/lib/navigation/.../tabbed/default-tabbed-case-view/default-tabbed-case-view.component.html, *.scss, *.spec.ts
Add export button to toolbar, conditionally visible via allowExport, calls export() on click, disabled during isLoading(), swaps icon based on loading state; add .button-icon and .export-mat-mini-fab styles; update test mock with caseViewAllowExport: true.

Tests, Mocks, and Miscellaneous

Layer / File(s) Summary
Case Model Mocks
projects/nae-example-app/src/app/doc/panels/panels.component.ts, projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-case-resource.service.ts, .../create-mock-case.ts, projects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.spec.ts, projects/netgrif-components-core/src/lib/authorization/permission/permission.service.spec.ts
Update case mock objects to include lastModifiedDataSet: [] or undefined.
Tree Case View Test
projects/netgrif-components/src/lib/view/tree-case-view/tree-component/add-child-node/add-child-node.component.spec.ts
Update CaseTreeNode construction to include lastModifiedDataSet: undefined.
Minor Fixes and Cleanup
projects/netgrif-components-core/src/lib/data-fields/case-ref-field/model/abstract-case-ref-base-field-component.ts, projects/netgrif-components-core/src/lib/data-fields/file-list-field/.../abstract-file-list-default-field.component.ts, projects/netgrif-components/src/lib/data-fields/task-ref-field/.../task-ref-dashboard-field.component.ts, projects/netgrif-components/src/lib/data-fields/file-field/.../file-default-field.component.html
Use optional chaining for headers; clean up unused imports; conditionally initialize taskContentComponentClassReference; add loading="lazy" to image preview.
Changelog
CHANGELOG.md
Add 6.4.2 and 6.4.1 release sections with feature/fix lists and Full Changelog links.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • netgrif/components#323: Implements the same 6.4.2 release changes including export service, public API, i18n keys, Case/Task interface additions, and related wiring.
  • netgrif/components#325: Both PRs modify the Case interface's lastModifiedDataSet field declaration.
  • netgrif/components#320: Related through adding the new Case.lastModifiedDataSet field and propagating it through interfaces, tests, and components.

Suggested labels

improvement, new feature, Large

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'NAE-2060 Merge 6.2.10, 6.4.2 into 6.5.0' clearly and specifically describes the main objective: merging two specific release versions into the target branch.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
36.8% Coverage on New Code (required ≥ 50%)

See analysis details on SonarQube Cloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Consider adding coverage for the new approval synchronization paths.

The existing spec only asserts that the component is created. With CaseViewService and DATA_FIELD_PORTAL_DATA left 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 _changeValue flag protocol, a focused test that provides a stub CaseViewService (with a cases$ BehaviorSubject and a cases array) plus a DATA_FIELD_PORTAL_DATA MultichoiceField 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

📥 Commits

Reviewing files that changed from the base of the PR and between 19f6085 and fb3efe6.

📒 Files selected for processing (47)
  • CHANGELOG.md
  • projects/nae-example-app/src/app/doc/panels/panels.component.ts
  • projects/netgrif-components-core/src/assets/i18n/de.json
  • projects/netgrif-components-core/src/assets/i18n/en.json
  • projects/netgrif-components-core/src/assets/i18n/sk.json
  • projects/netgrif-components-core/src/lib/authorization/permission/permission.service.spec.ts
  • projects/netgrif-components-core/src/lib/data-fields/case-ref-field/model/abstract-case-ref-base-field-component.ts
  • projects/netgrif-components-core/src/lib/data-fields/file-list-field/file-list-default-field/abstract-file-list-default-field.component.ts
  • projects/netgrif-components-core/src/lib/event/model/event-outcomes/task-outcomes/finish-task-event-outcome.ts
  • projects/netgrif-components-core/src/lib/export/public-api.ts
  • projects/netgrif-components-core/src/lib/export/services/export.service.spec.ts
  • projects/netgrif-components-core/src/lib/export/services/export.service.ts
  • projects/netgrif-components-core/src/lib/header/abstract-header.component.spec.ts
  • projects/netgrif-components-core/src/lib/header/abstract-header.component.ts
  • projects/netgrif-components-core/src/lib/navigation/model/group-navigation-constants.ts
  • projects/netgrif-components-core/src/lib/navigation/utility/filter-extraction.service.ts
  • projects/netgrif-components-core/src/lib/panel/case-panel/abstract-case-panel.component.spec.ts
  • projects/netgrif-components-core/src/lib/panel/task-panel-list/task-panel-list-pagination/abstract-task-list-pagination.component.ts
  • projects/netgrif-components-core/src/lib/panel/task-panel/abstract-task-panel.component.spec.ts
  • projects/netgrif-components-core/src/lib/panel/task-panel/abstract-task-panel.component.ts
  • projects/netgrif-components-core/src/lib/resources/interface/case.ts
  • projects/netgrif-components-core/src/lib/resources/interface/task.ts
  • projects/netgrif-components-core/src/lib/search/search-service/search.service.ts
  • projects/netgrif-components-core/src/lib/task-content/services/task-content.service.ts
  • projects/netgrif-components-core/src/lib/task/services/finish-task.service.ts
  • projects/netgrif-components-core/src/lib/task/services/task-data.service.ts
  • projects/netgrif-components-core/src/lib/utility/navigation-item-task-filter-factory.ts
  • projects/netgrif-components-core/src/lib/utility/tests/mocks/mock-case-resource.service.ts
  • projects/netgrif-components-core/src/lib/utility/tests/utility/create-mock-case.ts
  • projects/netgrif-components-core/src/lib/view/case-view/components/case-list-paginator/abstract-case-list-paginator.component.ts
  • projects/netgrif-components-core/src/lib/view/case-view/service/case-view-service.ts
  • projects/netgrif-components-core/src/public-api.ts
  • projects/netgrif-components/src/lib/data-fields/file-field/file-default-field/file-default-field.component.html
  • projects/netgrif-components/src/lib/data-fields/task-ref-field/task-ref-dashboard-field/task-ref-dashboard-field.component.ts
  • projects/netgrif-components/src/lib/header/header.component.ts
  • 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/model/injected-tabbed-case-view-data-with-navigation-item-task-data.ts
  • projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/refs/default-case-ref-list-view/default-case-ref-list-view.component.html
  • 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
  • projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tab-view/default-tab-view.component.spec.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.html
  • projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.scss
  • projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.spec.ts
  • projects/netgrif-components/src/lib/navigation/group-navigation-component-resolver/default-components/tabbed/default-tabbed-case-view/default-tabbed-case-view.component.ts
  • projects/netgrif-components/src/lib/panel/task-panel/task-panel.component.ts
  • projects/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

Comment thread CHANGELOG.md
Comment on lines +47 to +52
### 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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
### 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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 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=ts

Repository: 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.

Comment on lines +43 to +48
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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
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.

Comment on lines +219 to +227
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;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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().

Comment on lines +233 to +260
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;
})
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +140 to +142
public get cases(): Array<Case> {
return this._cases;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Comment on lines 31 to +32
@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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 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' -i

Repository: netgrif/components

Length of output: 1177


🏁 Script executed:

rg -n 'class AbstractHeaderComponent' --type ts -A 15

Repository: netgrif/components

Length of output: 1992


🏁 Script executed:

rg -n 'resolveApprovalDatafields' --type ts -B 2 -A 10

Repository: 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 2

Repository: netgrif/components

Length of output: 2744


🏁 Script executed:

rg -n 'constructor' 'projects/netgrif-components-core/src/lib/header/abstract-header.component.ts' -A 20

Repository: 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.

Comment on lines +86 to +95
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;
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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.

Comment on lines +92 to +94
this._baseFilter.filter.subscribe(observableFilter => {
this.initFilter = observableFilter;
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants