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: