From cc9905abe60c5eea12b6942b90929fa52cdcf722 Mon Sep 17 00:00:00 2001
From: Eugen Ciur <eugen@papermerge.com>
Date: Wed, 9 Mar 2022 21:36:20 +0100
Subject: [PATCH] add delete page frontend UI

---
 app/components/commander/index.hbs            |  2 +-
 app/components/modal/delete_pages.hbs         | 16 ++++++++++
 app/components/modal/delete_pages.js          | 32 +++++++++++++++++++
 .../index.hbs}                                | 11 ++++++-
 app/components/viewer/action_buttons/index.js |  9 ++++++
 app/components/viewer/document/index.hbs      |  4 ++-
 app/components/viewer/index.hbs               | 12 +++++--
 app/components/viewer/index.js                | 31 +++++++++++++++++-
 app/components/viewer/thumbnail/index.hbs     |  7 +++-
 app/components/viewer/thumbnail/index.js      | 29 +++++++++++++++++
 app/components/viewer/thumbnails.hbs          |  4 ++-
 app/services/requests.js                      | 25 +++++++++++++++
 app/styles/document_version.scss              | 17 +++++++++-
 13 files changed, 190 insertions(+), 9 deletions(-)
 create mode 100644 app/components/modal/delete_pages.hbs
 create mode 100644 app/components/modal/delete_pages.js
 rename app/components/viewer/{action_buttons.hbs => action_buttons/index.hbs} (68%)
 create mode 100644 app/components/viewer/action_buttons/index.js

diff --git a/app/components/commander/index.hbs b/app/components/commander/index.hbs
index 6bcd4a6..455f2d2 100644
--- a/app/components/commander/index.hbs
+++ b/app/components/commander/index.hbs
@@ -51,7 +51,7 @@
     {{show-when this.show_rename_node_modal}} />
 
   <Modal::DeleteNode
-    id="rename-node"
+    id="delete-node"
     @selectedNodes={{this.selected_nodes}}
     @onClose={{this.closeConfirmDeletionModal}}
     {{show-when this.show_confirm_deletion_modal}} />
diff --git a/app/components/modal/delete_pages.hbs b/app/components/modal/delete_pages.hbs
new file mode 100644
index 0000000..98d6c94
--- /dev/null
+++ b/app/components/modal/delete_pages.hbs
@@ -0,0 +1,16 @@
+<Modal::Base
+  @title="Confirm Deletion"
+  @actionTitle="Delete"
+  @onClose={{@onClose}}
+  @onSubmit={{this.onSubmit}}
+  @submitButtonClass="btn-danger"
+  @onCancel={{this.onCancel}}
+  ...attributes>
+  <p>
+    You selected {{this.count}} page(s).
+    If you click DELETE button selected page(s) will be deleted.
+  </p>
+  <p>
+    Do you really want to do that?
+  </p>
+</Modal::Base>
\ No newline at end of file
diff --git a/app/components/modal/delete_pages.js b/app/components/modal/delete_pages.js
new file mode 100644
index 0000000..e37de21
--- /dev/null
+++ b/app/components/modal/delete_pages.js
@@ -0,0 +1,32 @@
+import { action } from '@ember/object';
+import { inject as service } from '@ember/service';
+import { A } from '@ember/array';
+
+import BaseComponent from "./base";
+
+
+export default class DeletePagesComponent extends BaseComponent {
+  @service store;
+  @service currentUser;
+
+  get pages() {
+    return this.args.selectedPages;
+  }
+
+  @action
+  onSubmit() {
+    let pages_copy = A(this.pages);
+  
+    this.args.onClose(pages_copy);
+  }
+
+  get count() {
+    return this.pages.length;
+  }
+
+  @action
+  onCancel() {
+    this.args.onClose();
+  }
+
+}
diff --git a/app/components/viewer/action_buttons.hbs b/app/components/viewer/action_buttons/index.hbs
similarity index 68%
rename from app/components/viewer/action_buttons.hbs
rename to app/components/viewer/action_buttons/index.hbs
index 5ab80ed..8ca8a63 100644
--- a/app/components/viewer/action_buttons.hbs
+++ b/app/components/viewer/action_buttons/index.hbs
@@ -19,4 +19,13 @@
         Run OCR
       </button>
     {{/if}}
-</div>
\ No newline at end of file
+  {{#if this.is_any_page_selected }}
+    <button
+      class="btn btn-danger mx-5"
+      type="button"
+      {{on "click" @openConfirmDeletionModal}} >
+      <i class="fa fa-times"></i>
+      Delete Pages
+    </button>
+  {{/if}}
+</div>
diff --git a/app/components/viewer/action_buttons/index.js b/app/components/viewer/action_buttons/index.js
new file mode 100644
index 0000000..fdc931a
--- /dev/null
+++ b/app/components/viewer/action_buttons/index.js
@@ -0,0 +1,9 @@
+import Component from '@glimmer/component';
+
+
+export default class ActionButtonsComponent extends Component {
+
+  get is_any_page_selected() {
+    return this.args.selectedPages.length > 0;
+  }
+}
\ No newline at end of file
diff --git a/app/components/viewer/document/index.hbs b/app/components/viewer/document/index.hbs
index bd599a2..9a7a17b 100644
--- a/app/components/viewer/document/index.hbs
+++ b/app/components/viewer/document/index.hbs
@@ -2,7 +2,9 @@
   {{#if this.thumbnails_visible}}
     <Viewer::Thumbnails
       @pages={{@pages}}
-      @onDblClick={{this.onThumbnailDblClick}} />
+      @selectedPages={{@selectedPages}}
+      @onDblClick={{this.onThumbnailDblClick}}
+      @onCheckboxChange={{@onCheckboxChange}} />
   {{/if}}
   <Viewer::ThumbnailsSwitch
     @onThumbnailsToggle={{this.onThumbnailsToggle}}
diff --git a/app/components/viewer/index.hbs b/app/components/viewer/index.hbs
index e3f3342..33b1f8b 100644
--- a/app/components/viewer/index.hbs
+++ b/app/components/viewer/index.hbs
@@ -4,6 +4,8 @@
       @document_versions={{this.document_versions}}
       @isLocked={{this.isLocked}}
       @ocrStatus={{this.ocrStatus}}
+      @selectedPages={{this.selected_pages}}
+      @openConfirmDeletionModal={{this.openConfirmDeletionModal}}
       @onRunOCR={{this.onRunOCR}} />
 
     <Viewer::ActionModes
@@ -12,6 +14,12 @@
       @hint={{@hint}} />
   </div>
 
+  <Modal::DeletePages
+    id="delete-pages"
+    @selectedPages={{this.selected_pages}}
+    @onClose={{this.closeConfirmDeletionModal}}
+    {{show-when this.show_confirm_pages_deletion_modal}} />
+
   <Breadcrumb
     @node={{@doc}}
     @extranode={{@extranode}}
@@ -21,7 +29,7 @@
 
   <Viewer::Document
     @pages={{this.pages}}
+    @selectedPages={{this.selected_pages}}
     @thumbnails_visible={{this.thumbnails_visible}}
-    @onThumbnailsToggle={{this.onThumbnailsToggle}} />
-
+    @onCheckboxChange={{this.onThumbnailCheckboxChange}} />
 </div>
diff --git a/app/components/viewer/index.js b/app/components/viewer/index.js
index bb30f00..d53b6af 100644
--- a/app/components/viewer/index.js
+++ b/app/components/viewer/index.js
@@ -19,6 +19,7 @@ export default class ViewerComponent extends Component {
   @service websockets;
   @service store;
   @service requests;
+  @service router;
 
   @tracked ocr_status = null;
   @tracked is_locked = false;
@@ -29,6 +30,9 @@ export default class ViewerComponent extends Component {
   @tracked _pages = A([]);
   @tracked __pages__;
 
+  @tracked selected_pages = A([]);
+  @tracked show_confirm_pages_deletion_modal = false;
+
   constructor(owner, args) {
     super(owner, args);
 
@@ -102,7 +106,32 @@ export default class ViewerComponent extends Component {
 
   @action
   onNodeClicked() {
-    console.log('node clicked');
+  }
+
+  @action
+  onThumbnailCheckboxChange({page, is_selected}) {
+    if (is_selected) {
+      this.selected_pages.pushObject(page);
+    } else {
+      this.selected_pages.removeObject(page);
+    }
+  }
+
+  @action
+  openConfirmDeletionModal() {
+    this.show_confirm_pages_deletion_modal = true;
+  }
+
+  @action
+  async closeConfirmDeletionModal() {
+    let page_ids = [];
+
+    page_ids = this.selected_pages.map(page => page.id);
+    await this.requests.deletePages(page_ids);
+
+    this.show_confirm_pages_deletion_modal = false;
+    this.selected_pages = A([]);
+    this.router.refresh();
   }
 
   get versions() {
diff --git a/app/components/viewer/thumbnail/index.hbs b/app/components/viewer/thumbnail/index.hbs
index 87f494b..30de6f9 100644
--- a/app/components/viewer/thumbnail/index.hbs
+++ b/app/components/viewer/thumbnail/index.hbs
@@ -1,6 +1,11 @@
 <div
-  class="thumbnail d-flex flex-column align-items-center"
+  class="thumbnail d-flex flex-column align-items-center px-2 {{if this.is_selected 'checked'}}"
   {{on "dblclick" this.onDblClick}} >
+  <Input
+    class="align-self-start m-1"
+    @type="checkbox"
+    @checked={{this.is_selected}}
+    {{on "change" this.onCheckboxChange}} />
   {{#if @page.svg_image}}
     {{{@page.svg_image}}}
   {{else}}
diff --git a/app/components/viewer/thumbnail/index.js b/app/components/viewer/thumbnail/index.js
index 4a7220b..57474a2 100644
--- a/app/components/viewer/thumbnail/index.js
+++ b/app/components/viewer/thumbnail/index.js
@@ -8,4 +8,33 @@ export default class ViewerThumbnailComponent extends Component {
   onDblClick() {
     this.args.onDblClick(this.args.page);
   }
+
+  @action
+  onCheckboxChange(event) {
+    let is_checked = event.target.checked;
+
+    this.args.onCheckboxChange({
+      page: this.args.page,
+      is_selected: is_checked
+    });
+  }
+
+  get is_selected() {
+    let page = this.args.page,
+      selected_page_ids;
+
+    if (!this.args.selectedPages) {
+      return false;
+    }
+
+    selected_page_ids = this.args.selectedPages.map(
+      page => page.id
+    );
+
+    if (selected_page_ids.includes(page.id)) {
+      return true;
+    }
+
+    return false;
+  }
 }
\ No newline at end of file
diff --git a/app/components/viewer/thumbnails.hbs b/app/components/viewer/thumbnails.hbs
index c3e5aa9..b8fc91c 100644
--- a/app/components/viewer/thumbnails.hbs
+++ b/app/components/viewer/thumbnails.hbs
@@ -4,6 +4,8 @@
   {{#each @pages as |page|}}
     <Viewer::Thumbnail
       @page={{page}}
-      @onDblClick={{@onDblClick}} />
+      @selectedPages={{@selectedPages}}
+      @onDblClick={{@onDblClick}}
+      @onCheckboxChange={{@onCheckboxChange}} />
   {{/each}}
 </div>
diff --git a/app/services/requests.js b/app/services/requests.js
index bba8b64..2c4ea02 100644
--- a/app/services/requests.js
+++ b/app/services/requests.js
@@ -33,6 +33,10 @@ export default class Requests extends Service {
     });
   }
 
+  async deletePages(page_ids) {
+    return this._delete('/pages/', {'pages': page_ids});
+  }
+
   /**
   *  `document_version` contains following attributes:
   *    id
@@ -150,6 +154,27 @@ export default class Requests extends Service {
     });
   }
 
+  async _delete(url, data) {
+    let url_with_base,
+      body_data = '',
+      headers_copy = {};
+
+    url_with_base = `${base_url()}${url}`;
+
+    Object.assign(headers_copy, this.headers);
+    headers_copy['Content-Type'] = 'application/json';
+
+    if (data) {
+      body_data = JSON.stringify(data);
+    }
+
+    return fetch(url_with_base, {
+      method: 'DELETE',
+      headers: headers_copy,
+      body: body_data,
+    });
+  }
+
   async _get(url, params_str) {
     let url_with_base,
       headers_copy = {};
diff --git a/app/styles/document_version.scss b/app/styles/document_version.scss
index 7e2c22e..db9a087 100644
--- a/app/styles/document_version.scss
+++ b/app/styles/document_version.scss
@@ -5,10 +5,25 @@
   overflow: scroll;
 
   .thumbnail {
-    margin: 1rem;
+    margin: 0.25rem;
     img {
       width: 6rem;
     }
+    input[type=checkbox] {
+      visibility: hidden;
+    }
+    &:hover {
+      input[type=checkbox] {
+        visibility: visible;
+      }
+    }
+    &.checked {
+      input[type=checkbox] {
+        visibility: visible;
+      }
+      background-color:  #e6f5ff;
+      outline: 1px solid #A6DAFF;
+    }
   }
 }
 
-- 
GitLab