From ab548e36c7f255a145908322c533080539798dcf Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:09:02 -0800 Subject: [PATCH] Change: make saved views manage its own component (#8423) --- src-ui/e2e/admin/settings.spec.ts | 21 - src-ui/messages.xlf | 944 +++++++++--------- src-ui/src/app/app-routing.module.ts | 16 + src-ui/src/app/app.component.ts | 4 +- src-ui/src/app/app.module.ts | 4 + .../admin/settings/settings.component.html | 91 +- .../admin/settings/settings.component.spec.ts | 113 --- .../admin/settings/settings.component.ts | 107 +- .../app-frame/app-frame.component.html | 7 + .../document-list.component.html | 1 + .../saved-views/saved-views.component.html | 75 ++ .../saved-views/saved-views.component.scss | 3 + .../saved-views/saved-views.component.spec.ts | 165 +++ .../saved-views/saved-views.component.ts | 150 +++ 14 files changed, 922 insertions(+), 779 deletions(-) create mode 100644 src-ui/src/app/components/manage/saved-views/saved-views.component.html create mode 100644 src-ui/src/app/components/manage/saved-views/saved-views.component.scss create mode 100644 src-ui/src/app/components/manage/saved-views/saved-views.component.spec.ts create mode 100644 src-ui/src/app/components/manage/saved-views/saved-views.component.ts diff --git a/src-ui/e2e/admin/settings.spec.ts b/src-ui/e2e/admin/settings.spec.ts index 92c6918d9..e90bd7d82 100644 --- a/src-ui/e2e/admin/settings.spec.ts +++ b/src-ui/e2e/admin/settings.spec.ts @@ -33,24 +33,3 @@ test('should apply appearance changes when set', async ({ page }) => { await page.getByLabel('Enable dark mode').click() await expect(page.locator('html')).toHaveAttribute('data-bs-theme', /dark/) }) - -test('should toggle saved view options when set & saved', async ({ page }) => { - await page.routeFromHAR(REQUESTS_HAR, { notFound: 'fallback' }) - await page.goto('/settings/savedviews') - await page.getByLabel('Show on dashboard').first().click() - await page.getByLabel('Show in sidebar').first().click() - const updatePromise = page.waitForRequest((request) => { - if (!request.url().includes('8')) return true // skip other saved views - const data = request.postDataJSON() - const isValid = - data['show_on_dashboard'] === true && data['show_in_sidebar'] === true - return ( - isValid && - request.method() === 'PATCH' && - request.url().includes('/api/saved_views/') - ) - }) - await page.getByRole('button', { name: 'Save' }).scrollIntoViewIfNeeded() - await page.getByRole('button', { name: 'Save' }).click() - await updatePromise -}) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 9f8cf01b1..932a7318b 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -356,21 +356,17 @@ src/app/components/admin/settings/settings.component.html 2 - - src/app/components/admin/settings/settings.component.html - 344 - src/app/components/app-frame/app-frame.component.html 50 src/app/components/app-frame/app-frame.component.html - 237 + 244 src/app/components/app-frame/app-frame.component.html - 239 + 246 @@ -398,8 +394,8 @@ 162 - - The dashboard can be used to show saved views, such as an 'Inbox'. Those settings are found under Settings > Saved Views once you have created some. + + The dashboard can be used to show saved views, such as an 'Inbox'. Views are found under Manage > Saved Views once you have created some. src/app/app.component.ts 168 @@ -469,8 +465,8 @@ 4 - - Check out the settings for various tweaks to the web app and toggle settings for saved views. + + Check out the settings for various tweaks to the web app. src/app/app.component.ts 230 @@ -548,7 +544,7 @@ src/app/components/admin/settings/settings.component.html - 424 + 349 src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html @@ -602,6 +598,10 @@ src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html 21 + + src/app/components/manage/saved-views/saved-views.component.html + 73 + Error retrieving config @@ -653,11 +653,11 @@ src/app/components/app-frame/app-frame.component.html - 272 + 279 src/app/components/app-frame/app-frame.component.html - 275 + 282 @@ -688,10 +688,6 @@ src/app/components/admin/logs/logs.component.html 36 - - src/app/components/admin/settings/settings.component.html - 412 - src/app/components/admin/tasks/tasks.component.html 48 @@ -746,7 +742,7 @@ src/app/components/document-list/document-list.component.html - 109 + 110 src/app/components/manage/mail/mail.component.html @@ -768,9 +764,13 @@ src/app/components/manage/management-list/management-list.component.html 51 + + src/app/components/manage/saved-views/saved-views.component.html + 68 + - - Options to customize appearance, notifications, saved views and more. Settings apply to the <strong>current user only</strong>. + + Options to customize appearance, notifications and more. Settings apply to the <strong>current user only</strong>. src/app/components/admin/settings/settings.component.html 4 @@ -1073,15 +1073,37 @@ 152 - - Notes + + Saved Views src/app/components/admin/settings/settings.component.html 229 + + src/app/components/app-frame/app-frame.component.html + 206 + + + src/app/components/manage/saved-views/saved-views.component.html + 2 + + + + Show warning when closing saved views with unsaved changes + + src/app/components/admin/settings/settings.component.html + 232 + + + + Notes + + src/app/components/admin/settings/settings.component.html + 236 + src/app/components/document-list/document-list.component.html - 212 + 213 src/app/data/document.ts @@ -1096,14 +1118,14 @@ Enable notes src/app/components/admin/settings/settings.component.html - 233 + 239 Permissions src/app/components/admin/settings/settings.component.html - 241 + 247 src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html @@ -1166,28 +1188,28 @@ Default Permissions src/app/components/admin/settings/settings.component.html - 244 + 250 Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI src/app/components/admin/settings/settings.component.html - 248,250 + 254,256 Default Owner src/app/components/admin/settings/settings.component.html - 255 + 261 Objects without an owner can be viewed and edited by all users src/app/components/admin/settings/settings.component.html - 259 + 265 src/app/components/common/input/permissions/permissions-form/permissions-form.component.html @@ -1198,18 +1220,18 @@ Default View Permissions src/app/components/admin/settings/settings.component.html - 264 + 270 Users: src/app/components/admin/settings/settings.component.html - 269 + 275 src/app/components/admin/settings/settings.component.html - 296 + 302 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -1240,11 +1262,11 @@ Groups: src/app/components/admin/settings/settings.component.html - 279 + 285 src/app/components/admin/settings/settings.component.html - 306 + 312 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -1275,14 +1297,14 @@ Default Edit Permissions src/app/components/admin/settings/settings.component.html - 291 + 297 Edit permissions also grant viewing permissions src/app/components/admin/settings/settings.component.html - 315 + 321 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -1301,389 +1323,56 @@ Notifications src/app/components/admin/settings/settings.component.html - 323 + 329 Document processing src/app/components/admin/settings/settings.component.html - 326 + 332 Show notifications when new documents are detected src/app/components/admin/settings/settings.component.html - 330 + 336 Show notifications when document processing completes successfully src/app/components/admin/settings/settings.component.html - 331 + 337 Show notifications when document processing fails src/app/components/admin/settings/settings.component.html - 332 + 338 Suppress notifications on dashboard src/app/components/admin/settings/settings.component.html - 333 + 339 This will suppress all messages about document processing status on the dashboard. src/app/components/admin/settings/settings.component.html - 333 - - - - Saved views - - src/app/components/admin/settings/settings.component.html - 341 - - - src/app/components/app-frame/app-frame.component.html - 98 - - - src/app/components/app-frame/app-frame.component.html - 103 - - - - Show warning when closing saved views with unsaved changes - - src/app/components/admin/settings/settings.component.html - 347 - - - - Views - - src/app/components/admin/settings/settings.component.html - 351 - - - src/app/components/document-list/document-list.component.html - 70 - - - - Show on dashboard - - src/app/components/admin/settings/settings.component.html - 364 - - - src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html - 10 - - - - Show in sidebar - - src/app/components/admin/settings/settings.component.html - 368 - - - src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html - 9 - - - - Actions - - src/app/components/admin/settings/settings.component.html - 372 - - - src/app/components/admin/tasks/tasks.component.html - 67 - - - src/app/components/admin/trash/trash.component.html - 37 - - - src/app/components/admin/users-groups/users-groups.component.html - 23 - - - src/app/components/admin/users-groups/users-groups.component.html - 61 - - - src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 67 - - - src/app/components/document-detail/document-detail.component.html - 45 - - - src/app/components/document-list/bulk-editor/bulk-editor.component.html - 107 - - - src/app/components/manage/custom-fields/custom-fields.component.html - 19 - - - src/app/components/manage/mail/mail.component.html - 33 - - - src/app/components/manage/mail/mail.component.html - 100 - - - src/app/components/manage/management-list/management-list.component.html - 43 - - - src/app/components/manage/management-list/management-list.component.html - 43 - - - src/app/components/manage/management-list/management-list.component.html - 43 - - - src/app/components/manage/management-list/management-list.component.html - 43 - - - src/app/components/manage/workflows/workflows.component.html - 21 - - - - Delete - - src/app/components/admin/settings/settings.component.html - 374 - - - src/app/components/admin/trash/trash.component.html - 72 - - - src/app/components/admin/trash/trash.component.html - 81 - - - src/app/components/admin/trash/trash.component.ts - 59 - - - src/app/components/admin/trash/trash.component.ts - 88 - - - src/app/components/admin/users-groups/users-groups.component.html - 38 - - - src/app/components/admin/users-groups/users-groups.component.html - 76 - - - src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html - 27 - - - src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 53 - - - src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 45 - - - src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html - 87 - - - src/app/components/common/permissions-select/permissions-select.component.html - 19 - - - src/app/components/common/share-links-dropdown/share-links-dropdown.component.html - 37 - - - src/app/components/document-detail/document-detail.component.html - 24 - - - src/app/components/document-list/bulk-editor/bulk-editor.component.html - 161 - - - src/app/components/manage/custom-fields/custom-fields.component.html - 36 - - - src/app/components/manage/custom-fields/custom-fields.component.html - 48 - - - src/app/components/manage/mail/mail.component.html - 60 - - - src/app/components/manage/mail/mail.component.html - 72 - - - src/app/components/manage/mail/mail.component.html - 127 - - - src/app/components/manage/mail/mail.component.html - 141 - - - src/app/components/manage/management-list/management-list.component.html - 9 - - - src/app/components/manage/management-list/management-list.component.html - 9 - - - src/app/components/manage/management-list/management-list.component.html - 9 - - - src/app/components/manage/management-list/management-list.component.html - 9 - - - src/app/components/manage/management-list/management-list.component.html - 83 - - - src/app/components/manage/management-list/management-list.component.html - 83 - - - src/app/components/manage/management-list/management-list.component.html - 83 - - - src/app/components/manage/management-list/management-list.component.html - 83 - - - src/app/components/manage/management-list/management-list.component.html - 95 - - - src/app/components/manage/management-list/management-list.component.html - 95 - - - src/app/components/manage/management-list/management-list.component.html - 95 - - - src/app/components/manage/management-list/management-list.component.html - 95 - - - src/app/components/manage/management-list/management-list.component.ts - 210 - - - src/app/components/manage/workflows/workflows.component.html - 48 - - - src/app/components/manage/workflows/workflows.component.html - 59 - - - - Documents page size - - src/app/components/admin/settings/settings.component.html - 385 - - - - Display as - - src/app/components/admin/settings/settings.component.html - 388 - - - - Table - - src/app/components/admin/settings/settings.component.html - 390 - - - - Small Cards - - src/app/components/admin/settings/settings.component.html - 391 - - - - Large Cards - - src/app/components/admin/settings/settings.component.html - 392 - - - - Show - - src/app/components/admin/settings/settings.component.html - 396 - - - src/app/components/document-list/document-list.component.html - 17 - - - - Default - - src/app/components/admin/settings/settings.component.html - 396 - - - src/app/components/document-detail/document-detail.component.html - 117 - - - - No saved views defined. - - src/app/components/admin/settings/settings.component.html - 405 + 339 Cancel src/app/components/admin/settings/settings.component.html - 425 + 350 src/app/components/common/confirm-dialog/confirm-dialog.component.ts @@ -1749,26 +1438,30 @@ src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html 20 + + src/app/components/manage/saved-views/saved-views.component.html + 74 + Use system language src/app/components/admin/settings/settings.component.ts - 62 + 59 Use date format of display language src/app/components/admin/settings/settings.component.ts - 65 + 62 Error retrieving users src/app/components/admin/settings/settings.component.ts - 193 + 181 src/app/components/admin/users-groups/users-groups.component.ts @@ -1779,59 +1472,45 @@ Error retrieving groups src/app/components/admin/settings/settings.component.ts - 212 + 200 src/app/components/admin/users-groups/users-groups.component.ts 63 - - Saved view "" deleted. - - src/app/components/admin/settings/settings.component.ts - 431 - - Settings were saved successfully. src/app/components/admin/settings/settings.component.ts - 569 + 489 Settings were saved successfully. Reload is required to apply some changes. src/app/components/admin/settings/settings.component.ts - 573 + 493 Reload now src/app/components/admin/settings/settings.component.ts - 574 + 494 An error occurred while saving settings. src/app/components/admin/settings/settings.component.ts - 584 + 504 src/app/components/app-frame/app-frame.component.ts 125 - - Error while storing settings on server. - - src/app/components/admin/settings/settings.component.ts - 618 - - File Tasks @@ -1840,11 +1519,11 @@ src/app/components/app-frame/app-frame.component.html - 260 + 267 src/app/components/app-frame/app-frame.component.html - 262 + 269 @@ -2008,7 +1687,7 @@ src/app/components/document-list/document-list.component.html - 239 + 240 src/app/data/document.ts @@ -2033,6 +1712,73 @@ 66 + + Actions + + src/app/components/admin/tasks/tasks.component.html + 67 + + + src/app/components/admin/trash/trash.component.html + 37 + + + src/app/components/admin/users-groups/users-groups.component.html + 23 + + + src/app/components/admin/users-groups/users-groups.component.html + 61 + + + src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html + 67 + + + src/app/components/document-detail/document-detail.component.html + 45 + + + src/app/components/document-list/bulk-editor/bulk-editor.component.html + 107 + + + src/app/components/manage/custom-fields/custom-fields.component.html + 19 + + + src/app/components/manage/mail/mail.component.html + 33 + + + src/app/components/manage/mail/mail.component.html + 100 + + + src/app/components/manage/management-list/management-list.component.html + 43 + + + src/app/components/manage/management-list/management-list.component.html + 43 + + + src/app/components/manage/management-list/management-list.component.html + 43 + + + src/app/components/manage/management-list/management-list.component.html + 43 + + + src/app/components/manage/saved-views/saved-views.component.html + 28 + + + src/app/components/manage/workflows/workflows.component.html + 21 + + click for full output @@ -2171,11 +1917,11 @@ src/app/components/app-frame/app-frame.component.html - 220 + 227 src/app/components/app-frame/app-frame.component.html - 223 + 230 @@ -2231,6 +1977,153 @@ 78 + + Delete + + src/app/components/admin/trash/trash.component.html + 72 + + + src/app/components/admin/trash/trash.component.html + 81 + + + src/app/components/admin/trash/trash.component.ts + 59 + + + src/app/components/admin/trash/trash.component.ts + 88 + + + src/app/components/admin/users-groups/users-groups.component.html + 38 + + + src/app/components/admin/users-groups/users-groups.component.html + 76 + + + src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.html + 27 + + + src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts + 53 + + + src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html + 45 + + + src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html + 87 + + + src/app/components/common/permissions-select/permissions-select.component.html + 19 + + + src/app/components/common/share-links-dropdown/share-links-dropdown.component.html + 37 + + + src/app/components/document-detail/document-detail.component.html + 24 + + + src/app/components/document-list/bulk-editor/bulk-editor.component.html + 161 + + + src/app/components/manage/custom-fields/custom-fields.component.html + 36 + + + src/app/components/manage/custom-fields/custom-fields.component.html + 48 + + + src/app/components/manage/mail/mail.component.html + 60 + + + src/app/components/manage/mail/mail.component.html + 72 + + + src/app/components/manage/mail/mail.component.html + 127 + + + src/app/components/manage/mail/mail.component.html + 141 + + + src/app/components/manage/management-list/management-list.component.html + 9 + + + src/app/components/manage/management-list/management-list.component.html + 9 + + + src/app/components/manage/management-list/management-list.component.html + 9 + + + src/app/components/manage/management-list/management-list.component.html + 9 + + + src/app/components/manage/management-list/management-list.component.html + 83 + + + src/app/components/manage/management-list/management-list.component.html + 83 + + + src/app/components/manage/management-list/management-list.component.html + 83 + + + src/app/components/manage/management-list/management-list.component.html + 83 + + + src/app/components/manage/management-list/management-list.component.html + 95 + + + src/app/components/manage/management-list/management-list.component.html + 95 + + + src/app/components/manage/management-list/management-list.component.html + 95 + + + src/app/components/manage/management-list/management-list.component.html + 95 + + + src/app/components/manage/management-list/management-list.component.ts + 210 + + + src/app/components/manage/saved-views/saved-views.component.html + 30 + + + src/app/components/manage/workflows/workflows.component.html + 48 + + + src/app/components/manage/workflows/workflows.component.html + 59 + + {VAR_PLURAL, plural, =1 {One document in trash} other { total documents in trash}} @@ -2385,11 +2278,11 @@ src/app/components/app-frame/app-frame.component.html - 251 + 258 src/app/components/app-frame/app-frame.component.html - 253 + 260 @@ -2734,11 +2627,22 @@ src/app/components/app-frame/app-frame.component.html - 281 + 288 src/app/components/app-frame/app-frame.component.html - 284 + 291 + + + + Saved views + + src/app/components/app-frame/app-frame.component.html + 98 + + + src/app/components/app-frame/app-frame.component.html + 103 @@ -2805,7 +2709,7 @@ src/app/components/document-list/document-list.component.html - 194 + 195 src/app/components/document-list/filter-editor/filter-editor.component.html @@ -2856,6 +2760,10 @@ src/app/components/app-frame/app-frame.component.html 199 + + src/app/components/app-frame/app-frame.component.html + 204 + src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.html 4 @@ -2869,11 +2777,11 @@ Workflows src/app/components/app-frame/app-frame.component.html - 206 + 213 src/app/components/app-frame/app-frame.component.html - 208 + 215 src/app/components/manage/workflows/workflows.component.html @@ -2884,71 +2792,71 @@ Mail src/app/components/app-frame/app-frame.component.html - 213 + 220 src/app/components/app-frame/app-frame.component.html - 216 + 223 Administration src/app/components/app-frame/app-frame.component.html - 231 + 238 Configuration src/app/components/app-frame/app-frame.component.html - 244 + 251 src/app/components/app-frame/app-frame.component.html - 246 + 253 GitHub src/app/components/app-frame/app-frame.component.html - 291 + 298 is available. src/app/components/app-frame/app-frame.component.html - 300,301 + 307,308 Click to view. src/app/components/app-frame/app-frame.component.html - 301 + 308 Paperless-ngx can automatically check for updates src/app/components/app-frame/app-frame.component.html - 305 + 312 How does this work? src/app/components/app-frame/app-frame.component.html - 312,314 + 319,321 Update available src/app/components/app-frame/app-frame.component.html - 325 + 332 @@ -3510,7 +3418,7 @@ src/app/components/document-list/document-list.component.html - 248 + 249 src/app/data/document.ts @@ -6021,7 +5929,7 @@ src/app/components/document-list/document-list.component.html - 288 + 289 @@ -6036,7 +5944,7 @@ src/app/components/document-list/document-list.component.html - 328 + 329 @@ -6051,7 +5959,7 @@ src/app/components/document-list/document-list.component.html - 335 + 336 @@ -6341,7 +6249,7 @@ src/app/components/document-list/document-list.component.html - 191 + 192 src/app/components/document-list/filter-editor/filter-editor.component.ts @@ -6382,7 +6290,7 @@ src/app/components/document-list/document-list.component.html - 181 + 182 src/app/components/document-list/filter-editor/filter-editor.component.html @@ -6409,7 +6317,7 @@ src/app/components/document-list/document-list.component.html - 221 + 222 src/app/components/document-list/filter-editor/filter-editor.component.html @@ -6436,7 +6344,7 @@ src/app/components/document-list/document-list.component.html - 230 + 231 src/app/components/document-list/filter-editor/filter-editor.component.html @@ -6447,6 +6355,17 @@ 59 + + Default + + src/app/components/document-detail/document-detail.component.html + 117 + + + src/app/components/manage/saved-views/saved-views.component.html + 52 + + Content @@ -7244,7 +7163,7 @@ src/app/components/document-list/document-list.component.html - 304 + 305 @@ -7393,6 +7312,17 @@ 248 + + Show + + src/app/components/document-list/document-list.component.html + 17 + + + src/app/components/manage/saved-views/saved-views.component.html + 52 + + Sort @@ -7400,6 +7330,13 @@ 46 + + Views + + src/app/components/document-list/document-list.component.html + 70 + + Save "" @@ -7414,32 +7351,39 @@ 92 + + All saved views + + src/app/components/document-list/document-list.component.html + 93 + + {VAR_PLURAL, plural, =1 {Selected of one document} other {Selected of documents}} src/app/components/document-list/document-list.component.html - 112 + 113 {VAR_PLURAL, plural, =1 {One document} other { documents}} src/app/components/document-list/document-list.component.html - 116 + 117 (filtered) src/app/components/document-list/document-list.component.html - 118 + 119 Reset filters src/app/components/document-list/document-list.component.html - 123 + 124 src/app/components/document-list/filter-editor/filter-editor.component.html @@ -7450,21 +7394,21 @@ Error while loading documents src/app/components/document-list/document-list.component.html - 139 + 140 Sort by ASN src/app/components/document-list/document-list.component.html - 168 + 169 ASN src/app/components/document-list/document-list.component.html - 172 + 173 src/app/components/document-list/filter-editor/filter-editor.component.ts @@ -7483,28 +7427,28 @@ Sort by correspondent src/app/components/document-list/document-list.component.html - 177 + 178 Sort by title src/app/components/document-list/document-list.component.html - 186 + 187 Sort by owner src/app/components/document-list/document-list.component.html - 199 + 200 Owner src/app/components/document-list/document-list.component.html - 203 + 204 src/app/data/document.ts @@ -7519,49 +7463,49 @@ Sort by notes src/app/components/document-list/document-list.component.html - 208 + 209 Sort by document type src/app/components/document-list/document-list.component.html - 217 + 218 Sort by storage path src/app/components/document-list/document-list.component.html - 226 + 227 Sort by created date src/app/components/document-list/document-list.component.html - 235 + 236 Sort by added date src/app/components/document-list/document-list.component.html - 244 + 245 Sort by number of pages src/app/components/document-list/document-list.component.html - 253 + 254 Pages src/app/components/document-list/document-list.component.html - 257 + 258 src/app/data/document.ts @@ -7580,28 +7524,28 @@ Shared src/app/components/document-list/document-list.component.html - 260,262 + 261,263 Edit document src/app/components/document-list/document-list.component.html - 296 + 297 Preview document src/app/components/document-list/document-list.component.html - 297 + 298 Yes src/app/components/document-list/document-list.component.html - 356 + 357 src/app/pipes/yes-no.pipe.ts @@ -7612,7 +7556,7 @@ No src/app/components/document-list/document-list.component.html - 356 + 357 src/app/pipes/yes-no.pipe.ts @@ -7822,6 +7766,28 @@ 3 + + Show in sidebar + + src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html + 9 + + + src/app/components/manage/saved-views/saved-views.component.html + 24 + + + + Show on dashboard + + src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html + 10 + + + src/app/components/manage/saved-views/saved-views.component.html + 20 + + Filter rules error occurred while saving this view @@ -8431,6 +8397,76 @@ 344 + + Customize the views of your documents. + + src/app/components/manage/saved-views/saved-views.component.html + 4 + + + + Documents page size + + src/app/components/manage/saved-views/saved-views.component.html + 41 + + + + Display as + + src/app/components/manage/saved-views/saved-views.component.html + 44 + + + + Table + + src/app/components/manage/saved-views/saved-views.component.html + 46 + + + + Small Cards + + src/app/components/manage/saved-views/saved-views.component.html + 47 + + + + Large Cards + + src/app/components/manage/saved-views/saved-views.component.html + 48 + + + + No saved views defined. + + src/app/components/manage/saved-views/saved-views.component.html + 61 + + + + Saved view "" deleted. + + src/app/components/manage/saved-views/saved-views.component.ts + 113 + + + + Views saved successfully. + + src/app/components/manage/saved-views/saved-views.component.ts + 138 + + + + Error while saving views. + + src/app/components/manage/saved-views/saved-views.component.ts + 143 + + storage path diff --git a/src-ui/src/app/app-routing.module.ts b/src-ui/src/app/app-routing.module.ts index 8aacba8c8..c2e86208c 100644 --- a/src-ui/src/app/app-routing.module.ts +++ b/src-ui/src/app/app-routing.module.ts @@ -27,6 +27,7 @@ import { UsersAndGroupsComponent } from './components/admin/users-groups/users-g import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component' import { ConfigComponent } from './components/admin/config/config.component' import { TrashComponent } from './components/admin/trash/trash.component' +import { SavedViewsComponent } from './components/manage/saved-views/saved-views.component' export const routes: Routes = [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, @@ -165,6 +166,10 @@ export const routes: Routes = [ path: 'settings/usersgroups', redirectTo: '/usersgroups', }, + { + path: 'settings/savedviews', + redirectTo: '/savedviews', + }, { path: 'settings', component: SettingsComponent, @@ -255,6 +260,17 @@ export const routes: Routes = [ }, }, }, + { + path: 'savedviews', + component: SavedViewsComponent, + canActivate: [PermissionsGuard], + data: { + requiredPermission: { + action: PermissionAction.View, + type: PermissionType.SavedView, + }, + }, + }, ], }, diff --git a/src-ui/src/app/app.component.ts b/src-ui/src/app/app.component.ts index 7ce6d9494..594f972a9 100644 --- a/src-ui/src/app/app.component.ts +++ b/src-ui/src/app/app.component.ts @@ -165,7 +165,7 @@ export class AppComponent implements OnInit, OnDestroy { [ { anchorId: 'tour.dashboard', - content: $localize`The dashboard can be used to show saved views, such as an 'Inbox'. Those settings are found under Settings > Saved Views once you have created some.`, + content: $localize`The dashboard can be used to show saved views, such as an 'Inbox'. Views are found under Manage > Saved Views once you have created some.`, route: '/dashboard', delayAfterNavigation: 500, isOptional: false, @@ -227,7 +227,7 @@ export class AppComponent implements OnInit, OnDestroy { }, { anchorId: 'tour.settings', - content: $localize`Check out the settings for various tweaks to the web app and toggle settings for saved views.`, + content: $localize`Check out the settings for various tweaks to the web app.`, route: '/settings', backdropConfig: { offset: 0, diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index 702a8dc6a..68124d541 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -132,6 +132,7 @@ import { HotkeyDialogComponent } from './components/common/hotkey-dialog/hotkey- import { DeletePagesConfirmDialogComponent } from './components/common/confirm-dialog/delete-pages-confirm-dialog/delete-pages-confirm-dialog.component' import { TrashComponent } from './components/admin/trash/trash.component' import { EntriesComponent } from './components/common/input/entries/entries.component' +import { SavedViewsComponent } from './components/manage/saved-views/saved-views.component' import { airplane, archive, @@ -235,6 +236,7 @@ import { trash, uiRadios, upcScan, + windowStack, x, xCircle, xLg, @@ -343,6 +345,7 @@ const icons = { trash, uiRadios, upcScan, + windowStack, x, xCircle, xLg, @@ -524,6 +527,7 @@ function initializeApp(settings: SettingsService) { DeletePagesConfirmDialogComponent, TrashComponent, EntriesComponent, + SavedViewsComponent, ], bootstrap: [AppComponent], imports: [ diff --git a/src-ui/src/app/components/admin/settings/settings.component.html b/src-ui/src/app/components/admin/settings/settings.component.html index 827f61d78..d798e1def 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.html +++ b/src-ui/src/app/components/admin/settings/settings.component.html @@ -1,7 +1,7 @@ + All saved views diff --git a/src-ui/src/app/components/manage/saved-views/saved-views.component.html b/src-ui/src/app/components/manage/saved-views/saved-views.component.html new file mode 100644 index 000000000..58111fa5e --- /dev/null +++ b/src-ui/src/app/components/manage/saved-views/saved-views.component.html @@ -0,0 +1,75 @@ + + +
+
    + @for (view of savedViews; track view) { +
  • +
    +
    +
    + +
    +
    +
    + + +
    +
    + + +
    +
    +
    + + + +
    +
    +
    +
    + +
    +
    + + +
    + @if (displayFields) { + + } +
    +
    +
  • + } + + @if (savedViews && savedViews.length === 0) { +
  • +
    No saved views defined.
    +
  • + } + + @if (!savedViews) { +
  • +
    +
    Loading...
    +
  • + } +
+ + + +
diff --git a/src-ui/src/app/components/manage/saved-views/saved-views.component.scss b/src-ui/src/app/components/manage/saved-views/saved-views.component.scss new file mode 100644 index 000000000..5d4e87f30 --- /dev/null +++ b/src-ui/src/app/components/manage/saved-views/saved-views.component.scss @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/src-ui/src/app/components/manage/saved-views/saved-views.component.spec.ts b/src-ui/src/app/components/manage/saved-views/saved-views.component.spec.ts new file mode 100644 index 000000000..aaa77439a --- /dev/null +++ b/src-ui/src/app/components/manage/saved-views/saved-views.component.spec.ts @@ -0,0 +1,165 @@ +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' +import { provideHttpClientTesting } from '@angular/common/http/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' +import { ReactiveFormsModule, FormsModule } from '@angular/forms' +import { By } from '@angular/platform-browser' +import { NgbModule } from '@ng-bootstrap/ng-bootstrap' +import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { of, throwError } from 'rxjs' +import { SavedView } from 'src/app/data/saved-view' +import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' +import { PermissionsGuard } from 'src/app/guards/permissions.guard' +import { PermissionsService } from 'src/app/services/permissions.service' +import { SavedViewService } from 'src/app/services/rest/saved-view.service' +import { ToastService } from 'src/app/services/toast.service' +import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component' +import { CheckComponent } from '../../common/input/check/check.component' +import { DragDropSelectComponent } from '../../common/input/drag-drop-select/drag-drop-select.component' +import { NumberComponent } from '../../common/input/number/number.component' +import { SelectComponent } from '../../common/input/select/select.component' +import { TextComponent } from '../../common/input/text/text.component' +import { PageHeaderComponent } from '../../common/page-header/page-header.component' +import { SavedViewsComponent } from './saved-views.component' +import { DragDropModule } from '@angular/cdk/drag-drop' + +const savedViews = [ + { id: 1, name: 'view1', show_in_sidebar: true, show_on_dashboard: true }, + { id: 2, name: 'view2', show_in_sidebar: false, show_on_dashboard: false }, +] + +describe('SavedViewsComponent', () => { + let component: SavedViewsComponent + let fixture: ComponentFixture + let savedViewService: SavedViewService + let toastService: ToastService + + beforeEach(async () => { + TestBed.configureTestingModule({ + declarations: [ + SavedViewsComponent, + PageHeaderComponent, + IfPermissionsDirective, + CheckComponent, + SelectComponent, + TextComponent, + NumberComponent, + ConfirmButtonComponent, + DragDropSelectComponent, + ], + imports: [ + NgbModule, + NgxBootstrapIconsModule.pick(allIcons), + ReactiveFormsModule, + FormsModule, + DragDropModule, + ], + providers: [ + { + provide: PermissionsService, + useValue: { + currentUserCan: () => true, + }, + }, + PermissionsGuard, + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting(), + ], + }).compileComponents() + + savedViewService = TestBed.inject(SavedViewService) + toastService = TestBed.inject(ToastService) + fixture = TestBed.createComponent(SavedViewsComponent) + component = fixture.componentInstance + + jest.spyOn(savedViewService, 'listAll').mockReturnValue( + of({ + all: savedViews.map((v) => v.id), + count: savedViews.length, + results: (savedViews as SavedView[]).concat([]), + }) + ) + + fixture.detectChanges() + }) + + it('should support save saved views, show error', () => { + const toastErrorSpy = jest.spyOn(toastService, 'showError') + const toastSpy = jest.spyOn(toastService, 'show') + const savedViewPatchSpy = jest.spyOn(savedViewService, 'patchMany') + + const toggle = fixture.debugElement.query( + By.css('.form-check.form-switch input') + ) + toggle.nativeElement.checked = true + toggle.nativeElement.dispatchEvent(new Event('change')) + + // saved views error first + savedViewPatchSpy.mockReturnValueOnce( + throwError(() => new Error('unable to save saved views')) + ) + component.save() + expect(toastErrorSpy).toHaveBeenCalled() + expect(savedViewPatchSpy).toHaveBeenCalled() + toastSpy.mockClear() + toastErrorSpy.mockClear() + savedViewPatchSpy.mockClear() + + // succeed saved views + savedViewPatchSpy.mockReturnValueOnce(of(savedViews as SavedView[])) + component.save() + expect(toastErrorSpy).not.toHaveBeenCalled() + expect(savedViewPatchSpy).toHaveBeenCalled() + }) + + it('should update only patch saved views that have changed', () => { + const patchSpy = jest.spyOn(savedViewService, 'patchMany') + component.save() + expect(patchSpy).not.toHaveBeenCalled() + + const view = savedViews[0] + const toggle = fixture.debugElement.query( + By.css('.form-check.form-switch input') + ) + toggle.nativeElement.checked = true + toggle.nativeElement.dispatchEvent(new Event('change')) + // register change + component.savedViewsForm.get('savedViews').get(view.id.toString()).value[ + 'show_on_dashboard' + ] = !view.show_on_dashboard + fixture.detectChanges() + + component.save() + expect(patchSpy).toHaveBeenCalledWith([ + { + id: view.id, + name: view.name, + show_in_sidebar: view.show_in_sidebar, + show_on_dashboard: !view.show_on_dashboard, + }, + ]) + }) + + it('should support delete saved view', () => { + const toastSpy = jest.spyOn(toastService, 'showInfo') + const deleteSpy = jest.spyOn(savedViewService, 'delete') + deleteSpy.mockReturnValue(of(true)) + component.deleteSavedView(savedViews[0] as SavedView) + expect(deleteSpy).toHaveBeenCalled() + expect(toastSpy).toHaveBeenCalledWith( + `Saved view "${savedViews[0].name}" deleted.` + ) + }) + + it('should support reset', () => { + const view = savedViews[0] + component.savedViewsForm.get('savedViews').get(view.id.toString()).value[ + 'show_on_dashboard' + ] = !view.show_on_dashboard + component.reset() + expect( + component.savedViewsForm.get('savedViews').get(view.id.toString()).value[ + 'show_on_dashboard' + ] + ).toEqual(view.show_on_dashboard) + }) +}) diff --git a/src-ui/src/app/components/manage/saved-views/saved-views.component.ts b/src-ui/src/app/components/manage/saved-views/saved-views.component.ts new file mode 100644 index 000000000..dc09c71e4 --- /dev/null +++ b/src-ui/src/app/components/manage/saved-views/saved-views.component.ts @@ -0,0 +1,150 @@ +import { Component, OnDestroy, OnInit } from '@angular/core' +import { FormControl, FormGroup } from '@angular/forms' +import { SavedView } from 'src/app/data/saved-view' +import { SavedViewService } from 'src/app/services/rest/saved-view.service' +import { SettingsService } from 'src/app/services/settings.service' +import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component' +import { DisplayMode } from 'src/app/data/document' +import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs' +import { dirtyCheck } from '@ngneat/dirty-check-forms' +import { ToastService } from 'src/app/services/toast.service' + +@Component({ + selector: 'pngx-saved-views', + templateUrl: './saved-views.component.html', + styleUrl: './saved-views.component.scss', +}) +export class SavedViewsComponent + extends ComponentWithPermissions + implements OnInit, OnDestroy +{ + DisplayMode = DisplayMode + + public savedViews: SavedView[] + private savedViewsGroup = new FormGroup({}) + public savedViewsForm: FormGroup = new FormGroup({ + savedViews: this.savedViewsGroup, + }) + + private store: BehaviorSubject + private storeSub: Subscription + public isDirty$: Observable + private isDirty: boolean = false + private unsubscribeNotifier: Subject = new Subject() + private savePending: boolean = false + + get displayFields() { + return this.settings.allDisplayFields + } + + constructor( + private savedViewService: SavedViewService, + private settings: SettingsService, + private toastService: ToastService + ) { + super() + } + + ngOnInit(): void { + this.settings.organizingSidebarSavedViews = true + + this.savedViewService.listAll().subscribe((r) => { + this.savedViews = r.results + this.initialize() + }) + } + + ngOnDestroy(): void { + this.settings.organizingSidebarSavedViews = false + this.unsubscribeNotifier.next(this) + this.unsubscribeNotifier.complete() + this.storeSub.unsubscribe() + } + + private initialize() { + this.emptyGroup(this.savedViewsGroup) + + let storeData = { + savedViews: {}, + } + + for (let view of this.savedViews) { + storeData.savedViews[view.id.toString()] = { + id: view.id, + name: view.name, + show_on_dashboard: view.show_on_dashboard, + show_in_sidebar: view.show_in_sidebar, + page_size: view.page_size, + display_mode: view.display_mode, + display_fields: view.display_fields, + } + this.savedViewsGroup.addControl( + view.id.toString(), + new FormGroup({ + id: new FormControl(null), + name: new FormControl(null), + show_on_dashboard: new FormControl(null), + show_in_sidebar: new FormControl(null), + page_size: new FormControl(null), + display_mode: new FormControl(null), + display_fields: new FormControl([]), + }) + ) + } + + this.store = new BehaviorSubject(storeData) + this.storeSub = this.store.asObservable().subscribe((state) => { + this.savedViewsForm.patchValue(state, { emitEvent: false }) + }) + + // Initialize dirtyCheck + this.isDirty$ = dirtyCheck(this.savedViewsForm, this.store.asObservable()) + } + + public reset() { + this.savedViewsForm.patchValue(this.store.getValue()) + } + + public deleteSavedView(savedView: SavedView) { + this.savedViewService.delete(savedView).subscribe(() => { + this.savedViewsGroup.removeControl(savedView.id.toString()) + this.savedViews.splice(this.savedViews.indexOf(savedView), 1) + this.toastService.showInfo( + $localize`Saved view "${savedView.name}" deleted.` + ) + this.savedViewService.clearCache() + this.savedViewService.listAll().subscribe((r) => { + this.savedViews = r.results + this.initialize() + }) + }) + } + + private emptyGroup(group: FormGroup) { + Object.keys(group.controls).forEach((key) => group.removeControl(key)) + } + + public save() { + // only patch views that have actually changed + const changed: SavedView[] = [] + Object.values(this.savedViewsGroup.controls) + .filter((g: FormGroup) => !g.pristine) + .forEach((group: FormGroup) => { + changed.push(group.value) + }) + if (changed.length) { + this.savedViewService.patchMany(changed).subscribe({ + next: () => { + this.toastService.showInfo($localize`Views saved successfully.`) + this.store.next(this.savedViewsForm.value) + }, + error: (error) => { + this.toastService.showError( + $localize`Error while saving views.`, + error + ) + }, + }) + } + } +}