From 3bf64ae7da076904d700d9df577f26d4b85fdfe9 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 19 Feb 2025 15:44:48 -0800 Subject: [PATCH] Fix: saved views do not return to default display fields after setting and then removing (#9168) --- src-ui/messages.xlf | 16 +++---- .../saved-view-widget.component.ts | 5 ++- .../services/rest/saved-view.service.spec.ts | 42 +++++++++++++++++++ .../app/services/rest/saved-view.service.ts | 15 +++++-- src/documents/serialisers.py | 9 ++++ src/documents/tests/test_api_documents.py | 13 ++++++ 6 files changed, 88 insertions(+), 12 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index b9af58af3..7d42085c3 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -2230,7 +2230,7 @@ src/app/components/manage/custom-fields/custom-fields.component.ts - 103 + 106 src/app/components/manage/mail/mail.component.ts @@ -2565,7 +2565,7 @@ src/app/components/manage/custom-fields/custom-fields.component.ts - 105 + 108 src/app/components/manage/mail/mail.component.ts @@ -3322,7 +3322,7 @@ src/app/components/manage/custom-fields/custom-fields.component.ts - 85 + 87 @@ -3333,7 +3333,7 @@ src/app/components/manage/custom-fields/custom-fields.component.ts - 93 + 96 @@ -8160,28 +8160,28 @@ Confirm delete field src/app/components/manage/custom-fields/custom-fields.component.ts - 101 + 104 This operation will permanently delete this field. src/app/components/manage/custom-fields/custom-fields.component.ts - 102 + 105 Deleted field "" src/app/components/manage/custom-fields/custom-fields.component.ts - 111 + 114 Error deleting field "". src/app/components/manage/custom-fields/custom-fields.component.ts - 118 + 122 diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts index 32bf7a004..8399b06e1 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts @@ -145,7 +145,10 @@ export class SavedViewWidgetComponent }) } - if (this.savedView.display_fields) { + if ( + this.savedView.display_fields && + this.savedView.display_fields.length > 0 + ) { this.displayFields = this.savedView.display_fields } diff --git a/src-ui/src/app/services/rest/saved-view.service.spec.ts b/src-ui/src/app/services/rest/saved-view.service.spec.ts index 9a84fbd2c..7b12e8533 100644 --- a/src-ui/src/app/services/rest/saved-view.service.spec.ts +++ b/src-ui/src/app/services/rest/saved-view.service.spec.ts @@ -114,6 +114,48 @@ describe(`Additional service tests for SavedViewService`, () => { ]) }) + it('should treat empty display_fields as null', () => { + subscription = service + .patch({ + id: 1, + name: 'Saved View', + show_on_dashboard: true, + show_in_sidebar: true, + sort_field: 'name', + sort_reverse: true, + filter_rules: [], + display_fields: [], + }) + .subscribe() + const req = httpTestingController.expectOne( + `${environment.apiBaseUrl}${endpoint}/1/` + ) + expect(req.request.body.display_fields).toBeNull() + }) + + it('should support patch without reload', () => { + subscription = service + .patch( + { + id: 1, + name: 'Saved View', + show_on_dashboard: true, + show_in_sidebar: true, + sort_field: 'name', + sort_reverse: true, + filter_rules: [], + }, + false + ) + .subscribe() + const req = httpTestingController.expectOne( + `${environment.apiBaseUrl}${endpoint}/1/` + ) + expect(req.request.method).toEqual('PATCH') + req.flush({}) + httpTestingController.verify() // no reload + }) + beforeEach(() => { // Dont need to setup again diff --git a/src-ui/src/app/services/rest/saved-view.service.ts b/src-ui/src/app/services/rest/saved-view.service.ts index a23de7cd1..ef794ae06 100644 --- a/src-ui/src/app/services/rest/saved-view.service.ts +++ b/src-ui/src/app/services/rest/saved-view.service.ts @@ -87,12 +87,21 @@ export class SavedViewService extends AbstractPaperlessService { return super.create(o).pipe(tap(() => this.reload())) } - update(o: SavedView) { - return super.update(o).pipe(tap(() => this.reload())) + patch(o: SavedView, reload: boolean = false): Observable { + if (o.display_fields?.length === 0) { + o.display_fields = null + } + return super.patch(o).pipe( + tap(() => { + if (reload) { + this.reload() + } + }) + ) } patchMany(objects: SavedView[]): Observable { - return combineLatest(objects.map((o) => super.patch(o))).pipe( + return combineLatest(objects.map((o) => this.patch(o, false))).pipe( tap(() => this.reload()) ) } diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 84894bff1..75896ecdd 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -1147,6 +1147,15 @@ class SavedViewSerializer(OwnedObjectSerializer): if "user" in validated_data: # backwards compatibility validated_data["owner"] = validated_data.pop("user") + if ( + "display_fields" in validated_data + and isinstance( + validated_data["display_fields"], + list, + ) + and len(validated_data["display_fields"]) == 0 + ): + validated_data["display_fields"] = None super().update(instance, validated_data) if rules_data is not None: SavedViewFilterRule.objects.filter(saved_view=instance).delete() diff --git a/src/documents/tests/test_api_documents.py b/src/documents/tests/test_api_documents.py index 7010c5095..a1bea944a 100644 --- a/src/documents/tests/test_api_documents.py +++ b/src/documents/tests/test_api_documents.py @@ -1815,6 +1815,19 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase): ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + # empty display fields treated as none + response = self.client.patch( + f"/api/saved_views/{v1.id}/", + { + "display_fields": [], + }, + format="json", + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + v1.refresh_from_db() + self.assertEqual(v1.display_fields, None) + def test_saved_view_display_customfields(self): """ GIVEN: