mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-03-29 02:05:07 +00:00
Merge pull request #415 from eikek/fix-multi-tag-edit
Fix multi tag edit
This commit is contained in:
commit
17e8fb9b65
@ -8,7 +8,7 @@ This release contains many bug fixes, thank you all so much for
|
|||||||
helping out! There is also a new feature and some more scripts in
|
helping out! There is also a new feature and some more scripts in
|
||||||
tools.
|
tools.
|
||||||
|
|
||||||
- Edit/delete multiple items at once (#253)
|
- Edit/delete multiple items at once (#253, #412)
|
||||||
- Show/hide side menus via ui settings (#351)
|
- Show/hide side menus via ui settings (#351)
|
||||||
- Adds two more scripts to the `tools/` section (thanks to
|
- Adds two more scripts to the `tools/` section (thanks to
|
||||||
@totti4ever):
|
@totti4ever):
|
||||||
@ -36,6 +36,7 @@ tools.
|
|||||||
- Routes for managing multiple items:
|
- Routes for managing multiple items:
|
||||||
- `/sec/items/deleteAll`
|
- `/sec/items/deleteAll`
|
||||||
- `/sec/items/tags`
|
- `/sec/items/tags`
|
||||||
|
- `/sec/items/tagsremove`
|
||||||
- `/sec/items/name`
|
- `/sec/items/name`
|
||||||
- `/sec/items/folder`
|
- `/sec/items/folder`
|
||||||
- `/sec/items/direction`
|
- `/sec/items/direction`
|
||||||
|
@ -46,6 +46,12 @@ trait OItem[F[_]] {
|
|||||||
collective: Ident
|
collective: Ident
|
||||||
): F[UpdateResult]
|
): F[UpdateResult]
|
||||||
|
|
||||||
|
def removeTagsMultipleItems(
|
||||||
|
items: NonEmptyList[Ident],
|
||||||
|
tags: List[String],
|
||||||
|
collective: Ident
|
||||||
|
): F[UpdateResult]
|
||||||
|
|
||||||
/** Toggles tags of the given item. Tags must exist, but can be IDs or names. */
|
/** 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 toggleTags(item: Ident, tags: List[String], collective: Ident): F[UpdateResult]
|
||||||
|
|
||||||
@ -225,6 +231,29 @@ object OItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def removeTagsMultipleItems(
|
||||||
|
items: NonEmptyList[Ident],
|
||||||
|
tags: List[String],
|
||||||
|
collective: Ident
|
||||||
|
): F[UpdateResult] =
|
||||||
|
tags.distinct match {
|
||||||
|
case Nil => UpdateResult.success.pure[F]
|
||||||
|
case ws =>
|
||||||
|
store.transact {
|
||||||
|
(for {
|
||||||
|
itemIds <- OptionT
|
||||||
|
.liftF(RItem.filterItems(items, collective))
|
||||||
|
.filter(_.nonEmpty)
|
||||||
|
given <- OptionT.liftF(RTag.findAllByNameOrId(ws, collective))
|
||||||
|
_ <- OptionT.liftF(
|
||||||
|
itemIds.traverse(item =>
|
||||||
|
RTagItem.removeAllTags(item, given.map(_.tagId).toList)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} yield UpdateResult.success).getOrElse(UpdateResult.notFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def toggleTags(
|
def toggleTags(
|
||||||
item: Ident,
|
item: Ident,
|
||||||
tags: List[String],
|
tags: List[String],
|
||||||
|
@ -1945,6 +1945,29 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/BasicResult"
|
$ref: "#/components/schemas/BasicResult"
|
||||||
|
|
||||||
|
/sec/items/tagsremove:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- Item (Multi Edit)
|
||||||
|
summary: Remove tags from multiple items
|
||||||
|
description: |
|
||||||
|
Remove the given tags from all given items.
|
||||||
|
security:
|
||||||
|
- authTokenHeader: []
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ItemsAndRefs"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/BasicResult"
|
||||||
|
|
||||||
put:
|
put:
|
||||||
tags:
|
tags:
|
||||||
- Item (Multi Edit)
|
- Item (Multi Edit)
|
||||||
|
@ -73,6 +73,18 @@ object ItemMultiRoutes {
|
|||||||
resp <- Ok(Conversions.basicResult(res, "Tags added."))
|
resp <- Ok(Conversions.basicResult(res, "Tags added."))
|
||||||
} yield resp
|
} yield resp
|
||||||
|
|
||||||
|
case req @ POST -> Root / "tagsremove" =>
|
||||||
|
for {
|
||||||
|
json <- req.as[ItemsAndRefs]
|
||||||
|
items <- readIds[F](json.items)
|
||||||
|
res <- backend.item.removeTagsMultipleItems(
|
||||||
|
items,
|
||||||
|
json.refs,
|
||||||
|
user.account.collective
|
||||||
|
)
|
||||||
|
resp <- Ok(Conversions.basicResult(res, "Tags removed"))
|
||||||
|
} yield resp
|
||||||
|
|
||||||
case req @ PUT -> Root / "name" =>
|
case req @ PUT -> Root / "name" =>
|
||||||
for {
|
for {
|
||||||
json <- req.as[ItemsAndName]
|
json <- req.as[ItemsAndName]
|
||||||
|
@ -75,6 +75,7 @@ module Api exposing
|
|||||||
, refreshSession
|
, refreshSession
|
||||||
, register
|
, register
|
||||||
, removeMember
|
, removeMember
|
||||||
|
, removeTagsMultiple
|
||||||
, sendMail
|
, sendMail
|
||||||
, setAttachmentName
|
, setAttachmentName
|
||||||
, setCollectiveSettings
|
, setCollectiveSettings
|
||||||
@ -1342,6 +1343,20 @@ addTagsMultiple flags data receive =
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
removeTagsMultiple :
|
||||||
|
Flags
|
||||||
|
-> ItemsAndRefs
|
||||||
|
-> (Result Http.Error BasicResult -> msg)
|
||||||
|
-> Cmd msg
|
||||||
|
removeTagsMultiple flags data receive =
|
||||||
|
Http2.authPost
|
||||||
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/items/tagsremove"
|
||||||
|
, account = getAccount flags
|
||||||
|
, body = Http.jsonBody (Api.Model.ItemsAndRefs.encode data)
|
||||||
|
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
setNameMultiple :
|
setNameMultiple :
|
||||||
Flags
|
Flags
|
||||||
-> ItemsAndName
|
-> ItemsAndName
|
||||||
|
@ -53,6 +53,12 @@ type SaveNameState
|
|||||||
| SaveFailed
|
| SaveFailed
|
||||||
|
|
||||||
|
|
||||||
|
type TagEditMode
|
||||||
|
= AddTags
|
||||||
|
| RemoveTags
|
||||||
|
| ReplaceTags
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ tagModel : Comp.Dropdown.Model Tag
|
{ tagModel : Comp.Dropdown.Model Tag
|
||||||
, nameModel : String
|
, nameModel : String
|
||||||
@ -70,6 +76,7 @@ type alias Model =
|
|||||||
, concPersonModel : Comp.Dropdown.Model IdName
|
, concPersonModel : Comp.Dropdown.Model IdName
|
||||||
, concEquipModel : Comp.Dropdown.Model IdName
|
, concEquipModel : Comp.Dropdown.Model IdName
|
||||||
, modalEdit : Maybe Comp.DetailEdit.Model
|
, modalEdit : Maybe Comp.DetailEdit.Model
|
||||||
|
, tagEditMode : TagEditMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -82,6 +89,7 @@ type Msg
|
|||||||
| RemoveDueDate
|
| RemoveDueDate
|
||||||
| RemoveDate
|
| RemoveDate
|
||||||
| ConfirmMsg Bool
|
| ConfirmMsg Bool
|
||||||
|
| ToggleTagEditMode
|
||||||
| FolderDropdownMsg (Comp.Dropdown.Msg IdName)
|
| FolderDropdownMsg (Comp.Dropdown.Msg IdName)
|
||||||
| TagDropdownMsg (Comp.Dropdown.Msg Tag)
|
| TagDropdownMsg (Comp.Dropdown.Msg Tag)
|
||||||
| DirDropdownMsg (Comp.Dropdown.Msg Direction)
|
| DirDropdownMsg (Comp.Dropdown.Msg Direction)
|
||||||
@ -146,6 +154,7 @@ init =
|
|||||||
, dueDate = Nothing
|
, dueDate = Nothing
|
||||||
, dueDatePicker = Comp.DatePicker.emptyModel
|
, dueDatePicker = Comp.DatePicker.emptyModel
|
||||||
, modalEdit = Nothing
|
, modalEdit = Nothing
|
||||||
|
, tagEditMode = AddTags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -213,19 +222,48 @@ update flags msg model =
|
|||||||
newModel =
|
newModel =
|
||||||
{ model | tagModel = m2 }
|
{ model | tagModel = m2 }
|
||||||
|
|
||||||
|
mkChange list =
|
||||||
|
case model.tagEditMode of
|
||||||
|
AddTags ->
|
||||||
|
AddTagChange list
|
||||||
|
|
||||||
|
RemoveTags ->
|
||||||
|
RemoveTagChange list
|
||||||
|
|
||||||
|
ReplaceTags ->
|
||||||
|
ReplaceTagChange list
|
||||||
|
|
||||||
change =
|
change =
|
||||||
if isDropdownChangeMsg m then
|
if isDropdownChangeMsg m then
|
||||||
Comp.Dropdown.getSelected newModel.tagModel
|
Comp.Dropdown.getSelected newModel.tagModel
|
||||||
|> Util.List.distinct
|
|> Util.List.distinct
|
||||||
|> List.map (\t -> IdName t.id t.name)
|
|> List.map (\t -> IdName t.id t.name)
|
||||||
|> ReferenceList
|
|> ReferenceList
|
||||||
|> TagChange
|
|> mkChange
|
||||||
|
|
||||||
else
|
else
|
||||||
NoFormChange
|
NoFormChange
|
||||||
in
|
in
|
||||||
resultNoCmd change newModel
|
resultNoCmd change newModel
|
||||||
|
|
||||||
|
ToggleTagEditMode ->
|
||||||
|
let
|
||||||
|
( m2, _ ) =
|
||||||
|
Comp.Dropdown.update (Comp.Dropdown.SetSelection []) model.tagModel
|
||||||
|
|
||||||
|
newModel =
|
||||||
|
{ model | tagModel = m2 }
|
||||||
|
in
|
||||||
|
case model.tagEditMode of
|
||||||
|
AddTags ->
|
||||||
|
resultNone { newModel | tagEditMode = RemoveTags }
|
||||||
|
|
||||||
|
RemoveTags ->
|
||||||
|
resultNone { newModel | tagEditMode = ReplaceTags }
|
||||||
|
|
||||||
|
ReplaceTags ->
|
||||||
|
resultNone { newModel | tagEditMode = AddTags }
|
||||||
|
|
||||||
GetTagsResp (Ok tags) ->
|
GetTagsResp (Ok tags) ->
|
||||||
let
|
let
|
||||||
tagList =
|
tagList =
|
||||||
@ -554,6 +592,28 @@ renderEditForm cfg settings model =
|
|||||||
|
|
||||||
else
|
else
|
||||||
span [ class "invisible hidden" ] []
|
span [ class "invisible hidden" ] []
|
||||||
|
|
||||||
|
tagModeIcon =
|
||||||
|
case model.tagEditMode of
|
||||||
|
AddTags ->
|
||||||
|
i [ class "grey plus link icon" ] []
|
||||||
|
|
||||||
|
RemoveTags ->
|
||||||
|
i [ class "grey eraser link icon" ] []
|
||||||
|
|
||||||
|
ReplaceTags ->
|
||||||
|
i [ class "grey redo alternate link icon" ] []
|
||||||
|
|
||||||
|
tagModeMsg =
|
||||||
|
case model.tagEditMode of
|
||||||
|
AddTags ->
|
||||||
|
"Tags chosen here are *added* to all selected items."
|
||||||
|
|
||||||
|
RemoveTags ->
|
||||||
|
"Tags chosen here are *removed* from all selected items."
|
||||||
|
|
||||||
|
ReplaceTags ->
|
||||||
|
"Tags chosen here *replace* those on selected items."
|
||||||
in
|
in
|
||||||
div [ class cfg.menuClass ]
|
div [ class cfg.menuClass ]
|
||||||
[ div [ class "ui form warning" ]
|
[ div [ class "ui form warning" ]
|
||||||
@ -581,8 +641,17 @@ renderEditForm cfg settings model =
|
|||||||
[ label []
|
[ label []
|
||||||
[ Icons.tagsIcon "grey"
|
[ Icons.tagsIcon "grey"
|
||||||
, text "Tags"
|
, text "Tags"
|
||||||
|
, a
|
||||||
|
[ class "right-float"
|
||||||
|
, href "#"
|
||||||
|
, title "Change tag edit mode"
|
||||||
|
, onClick ToggleTagEditMode
|
||||||
|
]
|
||||||
|
[ tagModeIcon
|
||||||
|
]
|
||||||
]
|
]
|
||||||
, Html.map TagDropdownMsg (Comp.Dropdown.view settings model.tagModel)
|
, Html.map TagDropdownMsg (Comp.Dropdown.view settings model.tagModel)
|
||||||
|
, Markdown.toHtml [ class "small-info" ] tagModeMsg
|
||||||
]
|
]
|
||||||
, div [ class " field" ]
|
, div [ class " field" ]
|
||||||
[ label [] [ text "Name" ]
|
[ label [] [ text "Name" ]
|
||||||
|
@ -20,7 +20,9 @@ import Set exposing (Set)
|
|||||||
|
|
||||||
type FormChange
|
type FormChange
|
||||||
= NoFormChange
|
= NoFormChange
|
||||||
| TagChange ReferenceList
|
| AddTagChange ReferenceList
|
||||||
|
| ReplaceTagChange ReferenceList
|
||||||
|
| RemoveTagChange ReferenceList
|
||||||
| FolderChange (Maybe IdName)
|
| FolderChange (Maybe IdName)
|
||||||
| DirectionChange Direction
|
| DirectionChange Direction
|
||||||
| OrgChange (Maybe IdName)
|
| OrgChange (Maybe IdName)
|
||||||
@ -45,13 +47,27 @@ multiUpdate flags ids change receive =
|
|||||||
Set.toList ids
|
Set.toList ids
|
||||||
in
|
in
|
||||||
case change of
|
case change of
|
||||||
TagChange tags ->
|
ReplaceTagChange tags ->
|
||||||
let
|
let
|
||||||
data =
|
data =
|
||||||
ItemsAndRefs items (List.map .id tags.items)
|
ItemsAndRefs items (List.map .id tags.items)
|
||||||
in
|
in
|
||||||
Api.setTagsMultiple flags data receive
|
Api.setTagsMultiple flags data receive
|
||||||
|
|
||||||
|
AddTagChange tags ->
|
||||||
|
let
|
||||||
|
data =
|
||||||
|
ItemsAndRefs items (List.map .id tags.items)
|
||||||
|
in
|
||||||
|
Api.addTagsMultiple flags data receive
|
||||||
|
|
||||||
|
RemoveTagChange tags ->
|
||||||
|
let
|
||||||
|
data =
|
||||||
|
ItemsAndRefs items (List.map .id tags.items)
|
||||||
|
in
|
||||||
|
Api.removeTagsMultiple flags data receive
|
||||||
|
|
||||||
NameChange name ->
|
NameChange name ->
|
||||||
let
|
let
|
||||||
data =
|
data =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user