From d4043634ac42e68228d147a52d63c010f9ceff6f Mon Sep 17 00:00:00 2001
From: Eike Kettner <eike.kettner@posteo.de>
Date: Mon, 26 Oct 2020 12:48:15 +0100
Subject: [PATCH] Edit direction of multiple items

---
 .../scala/docspell/backend/ops/OItem.scala     | 18 +++++++++++-------
 .../restserver/routes/ItemMultiRoutes.scala    | 13 +++++++------
 .../restserver/routes/ItemRoutes.scala         |  9 +++++++--
 .../scala/docspell/store/records/RItem.scala   |  8 ++++++--
 modules/webapp/src/main/elm/Api.elm            | 16 ++++++++++++++++
 .../main/elm/Comp/ItemDetail/FormChange.elm    |  8 ++++++++
 6 files changed, 55 insertions(+), 17 deletions(-)

diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OItem.scala b/modules/backend/src/main/scala/docspell/backend/ops/OItem.scala
index f8e8b4a7..e9fb35b9 100644
--- a/modules/backend/src/main/scala/docspell/backend/ops/OItem.scala
+++ b/modules/backend/src/main/scala/docspell/backend/ops/OItem.scala
@@ -49,7 +49,11 @@ trait OItem[F[_]] {
   /** Toggles tags of the given item. Tags must exist, but can be IDs or names. */
   def toggleTags(item: Ident, tags: List[String], collective: Ident): F[UpdateResult]
 
-  def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult]
+  def setDirection(
+      item: NonEmptyList[Ident],
+      direction: Direction,
+      collective: Ident
+  ): F[UpdateResult]
 
   def setFolder(item: Ident, folder: Option[Ident], collective: Ident): F[UpdateResult]
 
@@ -252,14 +256,14 @@ object OItem {
             .getOrElse(AddResult.Failure(new Exception("Collective mismatch")))
 
         def setDirection(
-            item: Ident,
+            items: NonEmptyList[Ident],
             direction: Direction,
             collective: Ident
-        ): F[AddResult] =
-          store
-            .transact(RItem.updateDirection(item, collective, direction))
-            .attempt
-            .map(AddResult.fromUpdate)
+        ): F[UpdateResult] =
+          UpdateResult.fromUpdate(
+            store
+              .transact(RItem.updateDirection(items, collective, direction))
+          )
 
         def setFolder(
             item: Ident,
diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala
index 2c691c80..4114b9bd 100644
--- a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala
+++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala
@@ -94,12 +94,13 @@ object ItemMultiRoutes {
           resp  <- Ok(Conversions.basicResult(res, "Folder updated"))
         } yield resp
 
-      // case req @ PUT -> Root / "direction" =>
-      //   for {
-      //     dir  <- req.as[DirectionValue]
-      //     res  <- backend.item.setDirection(id, dir.direction, user.account.collective)
-      //     resp <- Ok(Conversions.basicResult(res, "Direction updated"))
-      //   } yield resp
+      case req @ PUT -> Root / "direction" =>
+        for {
+          json  <- req.as[ItemsAndDirection]
+          items <- readIds[F](json.items)
+          res   <- backend.item.setDirection(items, json.direction, user.account.collective)
+          resp  <- Ok(Conversions.basicResult(res, "Direction updated"))
+        } yield resp
 
       // case req @ PUT -> Root / "corrOrg" =>
       //   for {
diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala
index a033791d..3cbb0c3f 100644
--- a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala
+++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala
@@ -1,5 +1,6 @@
 package docspell.restserver.routes
 
+import cats.data.NonEmptyList
 import cats.effect._
 import cats.implicits._
 
@@ -165,8 +166,12 @@ object ItemRoutes {
 
       case req @ PUT -> Root / Ident(id) / "direction" =>
         for {
-          dir  <- req.as[DirectionValue]
-          res  <- backend.item.setDirection(id, dir.direction, user.account.collective)
+          dir <- req.as[DirectionValue]
+          res <- backend.item.setDirection(
+            NonEmptyList.of(id),
+            dir.direction,
+            user.account.collective
+          )
           resp <- Ok(Conversions.basicResult(res, "Direction updated"))
         } yield resp
 
diff --git a/modules/store/src/main/scala/docspell/store/records/RItem.scala b/modules/store/src/main/scala/docspell/store/records/RItem.scala
index fddf12e3..88599a4f 100644
--- a/modules/store/src/main/scala/docspell/store/records/RItem.scala
+++ b/modules/store/src/main/scala/docspell/store/records/RItem.scala
@@ -145,12 +145,16 @@ object RItem {
       ).update.run
     } yield n
 
-  def updateDirection(itemId: Ident, coll: Ident, dir: Direction): ConnectionIO[Int] =
+  def updateDirection(
+      itemIds: NonEmptyList[Ident],
+      coll: Ident,
+      dir: Direction
+  ): ConnectionIO[Int] =
     for {
       t <- currentTime
       n <- updateRow(
         table,
-        and(id.is(itemId), cid.is(coll)),
+        and(id.isIn(itemIds), cid.is(coll)),
         commas(incoming.setTo(dir), updated.setTo(t))
       ).update.run
     } yield n
diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm
index 077975ca..bb0ad81b 100644
--- a/modules/webapp/src/main/elm/Api.elm
+++ b/modules/webapp/src/main/elm/Api.elm
@@ -82,6 +82,7 @@ module Api exposing
     , setCorrOrg
     , setCorrPerson
     , setDirection
+    , setDirectionMultiple
     , setFolder
     , setFolderMultiple
     , setItemDate
@@ -134,6 +135,7 @@ import Api.Model.ItemLightList exposing (ItemLightList)
 import Api.Model.ItemProposals exposing (ItemProposals)
 import Api.Model.ItemSearch exposing (ItemSearch)
 import Api.Model.ItemUploadMeta exposing (ItemUploadMeta)
+import Api.Model.ItemsAndDirection exposing (ItemsAndDirection)
 import Api.Model.ItemsAndName exposing (ItemsAndName)
 import Api.Model.ItemsAndRef exposing (ItemsAndRef)
 import Api.Model.ItemsAndRefs exposing (ItemsAndRefs)
@@ -1328,6 +1330,20 @@ setFolderMultiple flags data receive =
         }
 
 
+setDirectionMultiple :
+    Flags
+    -> ItemsAndDirection
+    -> (Result Http.Error BasicResult -> msg)
+    -> Cmd msg
+setDirectionMultiple flags data receive =
+    Http2.authPut
+        { url = flags.config.baseUrl ++ "/api/v1/sec/items/direction"
+        , account = getAccount flags
+        , body = Http.jsonBody (Api.Model.ItemsAndDirection.encode data)
+        , expect = Http.expectJson receive Api.Model.BasicResult.decoder
+        }
+
+
 
 --- Item
 
diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/FormChange.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/FormChange.elm
index 8ac01514..53f491a7 100644
--- a/modules/webapp/src/main/elm/Comp/ItemDetail/FormChange.elm
+++ b/modules/webapp/src/main/elm/Comp/ItemDetail/FormChange.elm
@@ -6,6 +6,7 @@ module Comp.ItemDetail.FormChange exposing
 import Api
 import Api.Model.BasicResult exposing (BasicResult)
 import Api.Model.IdName exposing (IdName)
+import Api.Model.ItemsAndDirection exposing (ItemsAndDirection)
 import Api.Model.ItemsAndName exposing (ItemsAndName)
 import Api.Model.ItemsAndRef exposing (ItemsAndRef)
 import Api.Model.ItemsAndRefs exposing (ItemsAndRefs)
@@ -63,5 +64,12 @@ multiUpdate flags ids change receive =
             in
             Api.setFolderMultiple flags data receive
 
+        DirectionChange dir ->
+            let
+                data =
+                    ItemsAndDirection items (Data.Direction.toString dir)
+            in
+            Api.setDirectionMultiple flags data receive
+
         _ ->
             Cmd.none