mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-04-05 19:09:32 +00:00
Merge pull request #1455 from eikek/improve-item-link
Select linked items from search page
This commit is contained in:
commit
df4d023172
@ -134,8 +134,7 @@ object OItemSearch {
|
||||
|
||||
def findItem(id: Ident, collective: Ident): F[Option[ItemData]] =
|
||||
store
|
||||
.transact(QItem.findItem(id))
|
||||
.map(opt => opt.flatMap(_.filterCollective(collective)))
|
||||
.transact(QItem.findItem(id, collective))
|
||||
|
||||
def findItems(maxNoteLen: Int)(q: Query, batch: Batch): F[Vector[ListItem]] =
|
||||
Timestamp
|
||||
|
@ -2533,6 +2533,8 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/FileIntegrityCheckRequest"
|
||||
responses:
|
||||
422:
|
||||
description: BadRequest
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
@ -7194,6 +7196,7 @@ components:
|
||||
- archives
|
||||
- tags
|
||||
- customfields
|
||||
- relatedItems
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
@ -7257,6 +7260,13 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ItemFieldValue"
|
||||
relatedItems:
|
||||
description: |
|
||||
All related items to this item. The list contains items
|
||||
without more details being resolved.
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ItemLight"
|
||||
Attachment:
|
||||
description: |
|
||||
Information about an attachment to an item.
|
||||
@ -8001,6 +8011,11 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ItemFieldValue"
|
||||
relatedItems:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: ident
|
||||
notes:
|
||||
description: |
|
||||
Some prefix of the item notes.
|
||||
|
@ -139,7 +139,8 @@ trait Conversions {
|
||||
data.sources.map((mkAttachmentSource _).tupled).toList,
|
||||
data.archives.map((mkAttachmentArchive _).tupled).toList,
|
||||
data.tags.map(mkTag).toList,
|
||||
data.customFields.map(mkItemFieldValue).toList
|
||||
data.customFields.map(mkItemFieldValue).toList,
|
||||
data.relatedItems.map(mkItemLight).toList
|
||||
)
|
||||
|
||||
def mkItemFieldValue(v: OItemSearch.ItemFieldValue): ItemFieldValue =
|
||||
@ -239,6 +240,7 @@ trait Conversions {
|
||||
Nil, // attachments
|
||||
Nil, // tags
|
||||
Nil, // customfields
|
||||
Nil, // related items
|
||||
i.notes,
|
||||
Nil // highlight
|
||||
)
|
||||
@ -254,7 +256,8 @@ trait Conversions {
|
||||
.copy(
|
||||
tags = i.tags.map(mkTag),
|
||||
attachments = i.attachments.map(mkAttachmentLight),
|
||||
customfields = i.customfields.map(mkItemFieldValue)
|
||||
customfields = i.customfields.map(mkItemFieldValue),
|
||||
relatedItems = i.relatedItems
|
||||
)
|
||||
|
||||
private def mkAttachmentLight(qa: QAttachmentLight): AttachmentLight =
|
||||
|
@ -21,7 +21,8 @@ case class ItemData(
|
||||
attachments: Vector[(RAttachment, RFileMeta)],
|
||||
sources: Vector[(RAttachmentSource, RFileMeta)],
|
||||
archives: Vector[(RAttachmentArchive, RFileMeta)],
|
||||
customFields: Vector[ItemFieldValue]
|
||||
customFields: Vector[ItemFieldValue],
|
||||
relatedItems: Vector[ListItem]
|
||||
) {
|
||||
|
||||
def filterCollective(coll: Ident): Option[ItemData] =
|
||||
|
@ -6,11 +6,13 @@
|
||||
|
||||
package docspell.store.queries
|
||||
|
||||
import docspell.common.Ident
|
||||
import docspell.store.records.RTag
|
||||
|
||||
case class ListItemWithTags(
|
||||
item: ListItem,
|
||||
tags: List[RTag],
|
||||
attachments: List[AttachmentLight],
|
||||
customfields: List[ItemFieldValue]
|
||||
customfields: List[ItemFieldValue],
|
||||
relatedItems: List[Ident]
|
||||
)
|
||||
|
@ -15,7 +15,8 @@ import cats.implicits._
|
||||
import fs2.Stream
|
||||
|
||||
import docspell.common.{FileKey, IdRef, _}
|
||||
import docspell.query.ItemQuery
|
||||
import docspell.query.ItemQuery.Expr.ValidItemStates
|
||||
import docspell.query.{ItemQuery, ItemQueryDsl}
|
||||
import docspell.store.Store
|
||||
import docspell.store.qb.DSL._
|
||||
import docspell.store.qb._
|
||||
@ -47,7 +48,7 @@ object QItem {
|
||||
.unique
|
||||
.map(_ + items.size)
|
||||
|
||||
def findItem(id: Ident): ConnectionIO[Option[ItemData]] = {
|
||||
def findItem(id: Ident, collective: Ident): ConnectionIO[Option[ItemData]] = {
|
||||
val ref = RItem.as("ref")
|
||||
val cq =
|
||||
Select(
|
||||
@ -85,6 +86,7 @@ object QItem {
|
||||
val archives = RAttachmentArchive.findByItemWithMeta(id)
|
||||
val tags = RTag.findByItem(id)
|
||||
val customfields = findCustomFieldValuesForItem(id)
|
||||
val related = findRelatedItems(id, collective)
|
||||
|
||||
for {
|
||||
data <- q
|
||||
@ -93,11 +95,29 @@ object QItem {
|
||||
arch <- archives
|
||||
ts <- tags
|
||||
cfs <- customfields
|
||||
rel <- related
|
||||
} yield data.map(d =>
|
||||
ItemData(d._1, d._2, d._3, d._4, d._5, d._6, d._7, ts, att, srcs, arch, cfs)
|
||||
ItemData(d._1, d._2, d._3, d._4, d._5, d._6, d._7, ts, att, srcs, arch, cfs, rel)
|
||||
)
|
||||
}
|
||||
|
||||
def findRelatedItems(id: Ident, collective: Ident): ConnectionIO[Vector[ListItem]] =
|
||||
RItemLink
|
||||
.findLinked(collective, id)
|
||||
.map(v => Nel.fromList(v.toList))
|
||||
.flatMap {
|
||||
case None =>
|
||||
Vector.empty[ListItem].pure[ConnectionIO]
|
||||
case Some(nel) =>
|
||||
val expr =
|
||||
ItemQuery.Expr.and(ValidItemStates, ItemQueryDsl.Q.itemIdsIn(nel.map(_.id)))
|
||||
val account = AccountId(collective, Ident.unsafe(""))
|
||||
|
||||
findItemsBase(Query.Fix(account, Some(expr), None), LocalDate.EPOCH, 0).build
|
||||
.query[ListItem]
|
||||
.to[Vector]
|
||||
}
|
||||
|
||||
def findCustomFieldValuesForItem(
|
||||
itemId: Ident
|
||||
): ConnectionIO[Vector[ItemFieldValue]] =
|
||||
@ -440,11 +460,13 @@ object QItem {
|
||||
attachs <- Stream.eval(findAttachmentLight(item.id))
|
||||
ftags = tags.flatten.filter(t => t.collective == collective)
|
||||
cfields <- Stream.eval(findCustomFieldValuesForItem(item.id))
|
||||
related <- Stream.eval(RItemLink.findLinked(collective, item.id))
|
||||
} yield ListItemWithTags(
|
||||
item,
|
||||
RTag.sort(ftags.toList),
|
||||
attachs.sortBy(_.position),
|
||||
cfields.toList
|
||||
cfields.toList,
|
||||
related.toList
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,6 @@ import Html.Events exposing (onClick)
|
||||
import Markdown
|
||||
import Messages.Comp.ItemCard exposing (Texts)
|
||||
import Page exposing (Page(..))
|
||||
import Set exposing (Set)
|
||||
import Styles as S
|
||||
import Util.CustomField
|
||||
import Util.ItemDragDrop as DD
|
||||
@ -336,7 +335,7 @@ viewRow texts cfg settings flags model item =
|
||||
, IT.render subtitleTemplate (templateCtx texts) item |> text
|
||||
]
|
||||
, div [ class "opacity-90" ]
|
||||
[ mainTagsAndFields2 settings "flex truncate overflow-hidden flex-nowrap text-xs justify-start hidden md:flex" item
|
||||
[ mainTagsAndFields2 texts settings "flex truncate overflow-hidden flex-nowrap text-xs justify-start hidden md:flex" item
|
||||
]
|
||||
]
|
||||
]
|
||||
@ -449,7 +448,7 @@ viewRow texts cfg settings flags model item =
|
||||
(IT.render IT.source (templateCtx texts) item)
|
||||
]
|
||||
]
|
||||
, mainTagsAndFields2 settings "justify-start text-sm" item
|
||||
, mainTagsAndFields2 texts settings "justify-start text-sm" item
|
||||
, notesContent2 settings item
|
||||
]
|
||||
]
|
||||
@ -718,13 +717,13 @@ mainContent2 texts _ cardColor isCreated isDeleted settings _ item =
|
||||
, IT.render subtitlePattern (templateCtx texts) item |> text
|
||||
]
|
||||
, div [ class "" ]
|
||||
[ mainTagsAndFields2 settings "justify-end text-xs" item
|
||||
[ mainTagsAndFields2 texts settings "justify-end text-xs" item
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
mainTagsAndFields2 : UiSettings -> String -> ItemLight -> Html Msg
|
||||
mainTagsAndFields2 settings extraCss item =
|
||||
mainTagsAndFields2 : Texts -> UiSettings -> String -> ItemLight -> Html Msg
|
||||
mainTagsAndFields2 texts settings extraCss item =
|
||||
let
|
||||
fieldHidden f =
|
||||
Data.UiSettings.fieldHidden settings f
|
||||
@ -765,6 +764,22 @@ mainTagsAndFields2 settings extraCss item =
|
||||
|
||||
else
|
||||
List.map showTag item.tags
|
||||
|
||||
renderRelated =
|
||||
if List.isEmpty item.relatedItems then
|
||||
[]
|
||||
|
||||
else
|
||||
[ a
|
||||
[ class "label ml-1 mt-1 font-semibold hover:opacity-75 py-1"
|
||||
, class "border-gray-500 dark:border-slate-300"
|
||||
, href "#"
|
||||
, onClick (SetLinkTarget <| Comp.LinkTarget.LinkRelatedItems (item.id :: item.relatedItems))
|
||||
, title texts.showRelatedItems
|
||||
]
|
||||
[ i [ class "fa fa-link" ] []
|
||||
]
|
||||
]
|
||||
in
|
||||
div
|
||||
[ classList
|
||||
@ -773,7 +788,7 @@ mainTagsAndFields2 settings extraCss item =
|
||||
]
|
||||
, class extraCss
|
||||
]
|
||||
(renderFields ++ renderTags)
|
||||
(renderFields ++ renderTags ++ renderRelated)
|
||||
|
||||
|
||||
previewImage2 : Texts -> ViewConfig -> UiSettings -> Model -> ItemLight -> Html Msg
|
||||
|
@ -97,12 +97,12 @@ update inav env msg model =
|
||||
( cm, cc ) =
|
||||
Comp.CustomFieldMultiInput.init env.flags
|
||||
|
||||
( ilm, ilc ) =
|
||||
ilm =
|
||||
if model.item.id == "" then
|
||||
( model.itemLinkModel, Cmd.none )
|
||||
model.itemLinkModel
|
||||
|
||||
else
|
||||
Comp.ItemLinkForm.init env.flags model.item.id
|
||||
Comp.ItemLinkForm.initWith model.item.id model.item.relatedItems
|
||||
in
|
||||
resultModelCmd
|
||||
( { model
|
||||
@ -120,7 +120,6 @@ update inav env msg model =
|
||||
, Cmd.map DueDatePickerMsg dpc
|
||||
, Cmd.map ItemMailMsg ic
|
||||
, Cmd.map CustomFieldMsg cc
|
||||
, Cmd.map ItemLinkFormMsg ilc
|
||||
, Api.getSentMails env.flags model.item.id SentMailsResp
|
||||
]
|
||||
)
|
||||
|
@ -5,7 +5,7 @@
|
||||
-}
|
||||
|
||||
|
||||
module Comp.ItemLinkForm exposing (Model, Msg, emptyModel, init, update, view)
|
||||
module Comp.ItemLinkForm exposing (Model, Msg, emptyModel, init, initWith, update, view)
|
||||
|
||||
import Api
|
||||
import Api.Model.BasicResult exposing (BasicResult)
|
||||
@ -67,6 +67,20 @@ type Msg
|
||||
| ToggleEditMode
|
||||
|
||||
|
||||
initWith : String -> List ItemLight -> Model
|
||||
initWith target related =
|
||||
let
|
||||
cfg =
|
||||
Comp.ItemSearchInput.defaultConfig
|
||||
in
|
||||
{ itemSearchModel = Comp.ItemSearchInput.init cfg
|
||||
, relatedItems = related
|
||||
, targetItemId = target
|
||||
, editMode = AddRelated
|
||||
, formState = FormOk
|
||||
}
|
||||
|
||||
|
||||
init : Flags -> String -> ( Model, Cmd Msg )
|
||||
init flags itemId =
|
||||
let
|
||||
|
@ -36,6 +36,7 @@ type LinkTarget
|
||||
| LinkCustomField ItemFieldValue
|
||||
| LinkSource String
|
||||
| LinkBookmark String
|
||||
| LinkRelatedItems (List String)
|
||||
| LinkNone
|
||||
|
||||
|
||||
|
@ -104,6 +104,7 @@ type alias Model =
|
||||
, sourceModel : Maybe String
|
||||
, allBookmarks : Comp.BookmarkChooser.Model
|
||||
, selectedBookmarks : Comp.BookmarkChooser.Selection
|
||||
, selectedItems : List String
|
||||
, includeSelection : Bool
|
||||
, openTabs : Set String
|
||||
, searchMode : SearchMode
|
||||
@ -152,6 +153,7 @@ init flags =
|
||||
, sourceModel = Nothing
|
||||
, allBookmarks = Comp.BookmarkChooser.init Data.Bookmarks.empty
|
||||
, selectedBookmarks = Comp.BookmarkChooser.emptySelection
|
||||
, selectedItems = []
|
||||
, includeSelection = False
|
||||
, openTabs = Set.fromList [ "Tags", "Inbox" ]
|
||||
, searchMode = Data.SearchMode.Normal
|
||||
@ -311,6 +313,7 @@ getItemQuery selectedItems model =
|
||||
, textSearch.fullText
|
||||
|> Maybe.map Q.Contents
|
||||
, whenNotEmpty bookmarks Q.And
|
||||
, whenNotEmpty model.selectedItems Q.ItemIdIn
|
||||
]
|
||||
|
||||
|
||||
@ -356,6 +359,7 @@ resetModel model =
|
||||
, customValues = Data.CustomFieldChange.emptyCollect
|
||||
, sourceModel = Nothing
|
||||
, selectedBookmarks = Comp.BookmarkChooser.emptySelection
|
||||
, selectedItems = []
|
||||
, includeSelection = False
|
||||
, searchMode = Data.SearchMode.Normal
|
||||
}
|
||||
@ -397,6 +401,7 @@ type Msg
|
||||
| SetFolder IdName
|
||||
| SetTag String
|
||||
| SetBookmark String
|
||||
| SetSelectedItems (List String)
|
||||
| SetCustomField ItemFieldValue
|
||||
| CustomFieldMsg Comp.CustomFieldMultiInput.Msg
|
||||
| SetSource String
|
||||
@ -459,6 +464,9 @@ linkTargetMsg linkTarget =
|
||||
Comp.LinkTarget.LinkBookmark id ->
|
||||
Just <| SetBookmark id
|
||||
|
||||
Comp.LinkTarget.LinkRelatedItems ids ->
|
||||
Just <| SetSelectedItems ids
|
||||
|
||||
|
||||
type alias NextState =
|
||||
{ model : Model
|
||||
@ -611,6 +619,19 @@ updateDrop ddm flags settings msg model =
|
||||
, selectionChange = Data.ItemIds.noChange
|
||||
}
|
||||
|
||||
SetSelectedItems ids ->
|
||||
let
|
||||
nextModel =
|
||||
resetModel model
|
||||
in
|
||||
{ model = { nextModel | selectedItems = ids }
|
||||
, cmd = Cmd.none
|
||||
, sub = Sub.none
|
||||
, stateChange = ids /= model.selectedItems
|
||||
, dragDrop = DD.DragDropData ddm Nothing
|
||||
, selectionChange = Data.ItemIds.noChange
|
||||
}
|
||||
|
||||
GetAllTagsResp (Ok stats) ->
|
||||
let
|
||||
tagSel =
|
||||
|
@ -30,6 +30,7 @@ type alias Texts =
|
||||
, formatDateLong : Int -> String
|
||||
, formatDateShort : Int -> String
|
||||
, directionLabel : Direction -> String
|
||||
, showRelatedItems : String
|
||||
}
|
||||
|
||||
|
||||
@ -44,6 +45,7 @@ gb tz =
|
||||
, formatDateLong = Messages.DateFormat.formatDateLong Messages.UiLanguage.English tz
|
||||
, formatDateShort = Messages.DateFormat.formatDateShort Messages.UiLanguage.English tz
|
||||
, directionLabel = Messages.Data.Direction.gb
|
||||
, showRelatedItems = "Show linked items"
|
||||
}
|
||||
|
||||
|
||||
@ -58,6 +60,7 @@ de tz =
|
||||
, formatDateLong = Messages.DateFormat.formatDateLong Messages.UiLanguage.German tz
|
||||
, formatDateShort = Messages.DateFormat.formatDateShort Messages.UiLanguage.German tz
|
||||
, directionLabel = Messages.Data.Direction.de
|
||||
, showRelatedItems = "Verknüpfte Dokumente anzeigen"
|
||||
}
|
||||
|
||||
|
||||
@ -72,4 +75,5 @@ fr tz =
|
||||
, formatDateLong = Messages.DateFormat.formatDateLong Messages.UiLanguage.French tz
|
||||
, formatDateShort = Messages.DateFormat.formatDateShort Messages.UiLanguage.French tz
|
||||
, directionLabel = Messages.Data.Direction.fr
|
||||
, showRelatedItems = "Afficher les documents liés"
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ toItemLight detail =
|
||||
, tags = detail.tags
|
||||
, customfields = detail.customfields
|
||||
, notes = detail.notes
|
||||
, relatedItems = List.map .id detail.relatedItems
|
||||
, highlighting = []
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user