mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 10:28:27 +00:00
Link items it detail view
This commit is contained in:
@ -13,6 +13,7 @@ module Api exposing
|
|||||||
, addCorrPerson
|
, addCorrPerson
|
||||||
, addDashboard
|
, addDashboard
|
||||||
, addMember
|
, addMember
|
||||||
|
, addRelatedItems
|
||||||
, addShare
|
, addShare
|
||||||
, addTag
|
, addTag
|
||||||
, addTagsMultiple
|
, addTagsMultiple
|
||||||
@ -91,6 +92,7 @@ module Api exposing
|
|||||||
, getPersonFull
|
, getPersonFull
|
||||||
, getPersons
|
, getPersons
|
||||||
, getPersonsLight
|
, getPersonsLight
|
||||||
|
, getRelatedItems
|
||||||
, getScanMailbox
|
, getScanMailbox
|
||||||
, getSentMails
|
, getSentMails
|
||||||
, getShare
|
, getShare
|
||||||
@ -130,6 +132,8 @@ module Api exposing
|
|||||||
, refreshSession
|
, refreshSession
|
||||||
, register
|
, register
|
||||||
, removeMember
|
, removeMember
|
||||||
|
, removeRelatedItem
|
||||||
|
, removeRelatedItems
|
||||||
, removeTagsMultiple
|
, removeTagsMultiple
|
||||||
, replaceDashboard
|
, replaceDashboard
|
||||||
, reprocessItem
|
, reprocessItem
|
||||||
@ -227,7 +231,9 @@ import Api.Model.ImapSettingsList exposing (ImapSettingsList)
|
|||||||
import Api.Model.InviteResult exposing (InviteResult)
|
import Api.Model.InviteResult exposing (InviteResult)
|
||||||
import Api.Model.ItemDetail exposing (ItemDetail)
|
import Api.Model.ItemDetail exposing (ItemDetail)
|
||||||
import Api.Model.ItemInsights exposing (ItemInsights)
|
import Api.Model.ItemInsights exposing (ItemInsights)
|
||||||
|
import Api.Model.ItemLightGroup exposing (ItemLightGroup)
|
||||||
import Api.Model.ItemLightList exposing (ItemLightList)
|
import Api.Model.ItemLightList exposing (ItemLightList)
|
||||||
|
import Api.Model.ItemLinkData exposing (ItemLinkData)
|
||||||
import Api.Model.ItemProposals exposing (ItemProposals)
|
import Api.Model.ItemProposals exposing (ItemProposals)
|
||||||
import Api.Model.ItemQuery exposing (ItemQuery)
|
import Api.Model.ItemQuery exposing (ItemQuery)
|
||||||
import Api.Model.ItemUploadMeta exposing (ItemUploadMeta)
|
import Api.Model.ItemUploadMeta exposing (ItemUploadMeta)
|
||||||
@ -3007,6 +3013,48 @@ verifyJsonFilter flags query receive =
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Item Links
|
||||||
|
|
||||||
|
|
||||||
|
getRelatedItems : Flags -> String -> (Result Http.Error ItemLightGroup -> msg) -> Cmd msg
|
||||||
|
getRelatedItems flags itemId receive =
|
||||||
|
Http2.authGet
|
||||||
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/itemlink/" ++ itemId
|
||||||
|
, account = getAccount flags
|
||||||
|
, expect = Http.expectJson receive Api.Model.ItemLightGroup.decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addRelatedItems : Flags -> ItemLinkData -> (Result Http.Error BasicResult -> msg) -> Cmd msg
|
||||||
|
addRelatedItems flags data receive =
|
||||||
|
Http2.authPost
|
||||||
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/itemlink/addAll"
|
||||||
|
, account = getAccount flags
|
||||||
|
, body = Http.jsonBody (Api.Model.ItemLinkData.encode data)
|
||||||
|
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
removeRelatedItems : Flags -> ItemLinkData -> (Result Http.Error BasicResult -> msg) -> Cmd msg
|
||||||
|
removeRelatedItems flags data receive =
|
||||||
|
Http2.authPost
|
||||||
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/itemlink/removeAll"
|
||||||
|
, account = getAccount flags
|
||||||
|
, body = Http.jsonBody (Api.Model.ItemLinkData.encode data)
|
||||||
|
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
removeRelatedItem : Flags -> String -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg
|
||||||
|
removeRelatedItem flags item1 item2 receive =
|
||||||
|
Http2.authDelete
|
||||||
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/itemlink/" ++ item1 ++ "/" ++ item2
|
||||||
|
, account = getAccount flags
|
||||||
|
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Helper
|
--- Helper
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ import Comp.DatePicker
|
|||||||
import Comp.DetailEdit
|
import Comp.DetailEdit
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
import Comp.Dropzone
|
import Comp.Dropzone
|
||||||
|
import Comp.ItemLinkForm
|
||||||
import Comp.ItemMail
|
import Comp.ItemMail
|
||||||
import Comp.KeyInput
|
import Comp.KeyInput
|
||||||
import Comp.LinkTarget exposing (LinkTarget)
|
import Comp.LinkTarget exposing (LinkTarget)
|
||||||
@ -121,6 +122,7 @@ type alias Model =
|
|||||||
, editMenuTabsOpen : Set String
|
, editMenuTabsOpen : Set String
|
||||||
, viewMode : ViewMode
|
, viewMode : ViewMode
|
||||||
, showQrModel : ShowQrModel
|
, showQrModel : ShowQrModel
|
||||||
|
, itemLinkModel : Comp.ItemLinkForm.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -256,6 +258,7 @@ emptyModel =
|
|||||||
, editMenuTabsOpen = Set.empty
|
, editMenuTabsOpen = Set.empty
|
||||||
, viewMode = SimpleView
|
, viewMode = SimpleView
|
||||||
, showQrModel = initShowQrModel
|
, showQrModel = initShowQrModel
|
||||||
|
, itemLinkModel = Comp.ItemLinkForm.emptyModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -369,6 +372,7 @@ type Msg
|
|||||||
| PrintElement String
|
| PrintElement String
|
||||||
| SetNameMsg Comp.SimpleTextInput.Msg
|
| SetNameMsg Comp.SimpleTextInput.Msg
|
||||||
| ToggleSelectItem
|
| ToggleSelectItem
|
||||||
|
| ItemLinkFormMsg Comp.ItemLinkForm.Msg
|
||||||
|
|
||||||
|
|
||||||
type SaveNameState
|
type SaveNameState
|
||||||
|
@ -46,6 +46,7 @@ import Comp.ItemDetail.Model
|
|||||||
, resultModelCmd
|
, resultModelCmd
|
||||||
, resultModelCmdSub
|
, resultModelCmdSub
|
||||||
)
|
)
|
||||||
|
import Comp.ItemLinkForm
|
||||||
import Comp.ItemMail
|
import Comp.ItemMail
|
||||||
import Comp.KeyInput
|
import Comp.KeyInput
|
||||||
import Comp.LinkTarget
|
import Comp.LinkTarget
|
||||||
@ -95,6 +96,13 @@ update inav env msg model =
|
|||||||
|
|
||||||
( cm, cc ) =
|
( cm, cc ) =
|
||||||
Comp.CustomFieldMultiInput.init env.flags
|
Comp.CustomFieldMultiInput.init env.flags
|
||||||
|
|
||||||
|
( ilm, ilc ) =
|
||||||
|
if model.item.id == "" then
|
||||||
|
( model.itemLinkModel, Cmd.none )
|
||||||
|
|
||||||
|
else
|
||||||
|
Comp.ItemLinkForm.init env.flags model.item.id
|
||||||
in
|
in
|
||||||
resultModelCmd
|
resultModelCmd
|
||||||
( { model
|
( { model
|
||||||
@ -104,6 +112,7 @@ update inav env msg model =
|
|||||||
, visibleAttach = 0
|
, visibleAttach = 0
|
||||||
, attachMenuOpen = False
|
, attachMenuOpen = False
|
||||||
, customFieldsModel = cm
|
, customFieldsModel = cm
|
||||||
|
, itemLinkModel = ilm
|
||||||
}
|
}
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ getOptions env.flags
|
[ getOptions env.flags
|
||||||
@ -111,6 +120,7 @@ update inav env msg model =
|
|||||||
, Cmd.map DueDatePickerMsg dpc
|
, Cmd.map DueDatePickerMsg dpc
|
||||||
, Cmd.map ItemMailMsg ic
|
, Cmd.map ItemMailMsg ic
|
||||||
, Cmd.map CustomFieldMsg cc
|
, Cmd.map CustomFieldMsg cc
|
||||||
|
, Cmd.map ItemLinkFormMsg ilc
|
||||||
, Api.getSentMails env.flags model.item.id SentMailsResp
|
, Api.getSentMails env.flags model.item.id SentMailsResp
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -217,6 +227,9 @@ update inav env msg model =
|
|||||||
else
|
else
|
||||||
Cmd.none
|
Cmd.none
|
||||||
|
|
||||||
|
( ilm, ilc ) =
|
||||||
|
Comp.ItemLinkForm.init env.flags item.id
|
||||||
|
|
||||||
lastModel =
|
lastModel =
|
||||||
res9.model
|
res9.model
|
||||||
in
|
in
|
||||||
@ -237,6 +250,7 @@ update inav env msg model =
|
|||||||
, dueDate = item.dueDate
|
, dueDate = item.dueDate
|
||||||
, visibleAttach = 0
|
, visibleAttach = 0
|
||||||
, modalEdit = Nothing
|
, modalEdit = Nothing
|
||||||
|
, itemLinkModel = ilm
|
||||||
}
|
}
|
||||||
, cmd =
|
, cmd =
|
||||||
Cmd.batch
|
Cmd.batch
|
||||||
@ -254,6 +268,7 @@ update inav env msg model =
|
|||||||
, Api.getSentMails env.flags item.id SentMailsResp
|
, Api.getSentMails env.flags item.id SentMailsResp
|
||||||
, Api.getPersons env.flags "" Data.PersonOrder.NameAsc GetPersonResp
|
, Api.getPersons env.flags "" Data.PersonOrder.NameAsc GetPersonResp
|
||||||
, Cmd.map CustomFieldMsg (Comp.CustomFieldMultiInput.initCmd env.flags)
|
, Cmd.map CustomFieldMsg (Comp.CustomFieldMultiInput.initCmd env.flags)
|
||||||
|
, Cmd.map ItemLinkFormMsg ilc
|
||||||
]
|
]
|
||||||
, sub =
|
, sub =
|
||||||
Sub.batch
|
Sub.batch
|
||||||
@ -1613,6 +1628,17 @@ update inav env msg model =
|
|||||||
in
|
in
|
||||||
{ res | selectionChange = newSelection }
|
{ res | selectionChange = newSelection }
|
||||||
|
|
||||||
|
ItemLinkFormMsg lm ->
|
||||||
|
let
|
||||||
|
( ilm, ilc, ils ) =
|
||||||
|
Comp.ItemLinkForm.update env.flags lm model.itemLinkModel
|
||||||
|
in
|
||||||
|
resultModelCmdSub
|
||||||
|
( { model | itemLinkModel = ilm }
|
||||||
|
, Cmd.map ItemLinkFormMsg ilc
|
||||||
|
, Sub.map ItemLinkFormMsg ils
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Helper
|
--- Helper
|
||||||
|
@ -24,6 +24,7 @@ import Comp.ItemDetail.Model
|
|||||||
import Comp.ItemDetail.Notes
|
import Comp.ItemDetail.Notes
|
||||||
import Comp.ItemDetail.ShowQrCode
|
import Comp.ItemDetail.ShowQrCode
|
||||||
import Comp.ItemDetail.SingleAttachment
|
import Comp.ItemDetail.SingleAttachment
|
||||||
|
import Comp.ItemLinkForm
|
||||||
import Comp.ItemMail
|
import Comp.ItemMail
|
||||||
import Comp.MenuBar as MB
|
import Comp.MenuBar as MB
|
||||||
import Comp.SentMails
|
import Comp.SentMails
|
||||||
@ -45,8 +46,6 @@ view : Texts -> ItemNav -> Env.View -> Model -> Html Msg
|
|||||||
view texts inav env model =
|
view texts inav env model =
|
||||||
div [ class "flex flex-col h-full" ]
|
div [ class "flex flex-col h-full" ]
|
||||||
[ header texts inav env model
|
[ header texts inav env model
|
||||||
|
|
||||||
-- , menuBar texts inav settings model
|
|
||||||
, body texts env.flags inav env.settings model
|
, body texts env.flags inav env.settings model
|
||||||
, itemModal texts model
|
, itemModal texts model
|
||||||
]
|
]
|
||||||
@ -407,12 +406,18 @@ itemActions texts flags settings model classes =
|
|||||||
|
|
||||||
|
|
||||||
notesAndSentMails : Texts -> Flags -> UiSettings -> Model -> String -> Html Msg
|
notesAndSentMails : Texts -> Flags -> UiSettings -> Model -> String -> Html Msg
|
||||||
notesAndSentMails texts _ _ model classes =
|
notesAndSentMails texts _ settings model classes =
|
||||||
div
|
div
|
||||||
[ class "w-full md:mr-2 flex flex-col"
|
[ class "w-full md:mr-2 flex flex-col"
|
||||||
, class classes
|
, class classes
|
||||||
]
|
]
|
||||||
[ Comp.ItemDetail.Notes.view texts.notes model
|
[ Comp.ItemDetail.Notes.view texts.notes model
|
||||||
|
, div [ class "mb-4 mt-4" ]
|
||||||
|
[ div [ class "font-bold text-lg" ]
|
||||||
|
[ text texts.relatedItems
|
||||||
|
]
|
||||||
|
, Html.map ItemLinkFormMsg (Comp.ItemLinkForm.view texts.itemLinkForm settings model.itemLinkModel)
|
||||||
|
]
|
||||||
, div
|
, div
|
||||||
[ classList
|
[ classList
|
||||||
[ ( "hidden", Comp.SentMails.isEmpty model.sentMails )
|
[ ( "hidden", Comp.SentMails.isEmpty model.sentMails )
|
||||||
|
304
modules/webapp/src/main/elm/Comp/ItemLinkForm.elm
Normal file
304
modules/webapp/src/main/elm/Comp/ItemLinkForm.elm
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
module Comp.ItemLinkForm exposing (Model, Msg, emptyModel, init, update, view)
|
||||||
|
|
||||||
|
import Api
|
||||||
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
|
import Api.Model.ItemLight exposing (ItemLight)
|
||||||
|
import Api.Model.ItemLightGroup exposing (ItemLightGroup)
|
||||||
|
import Comp.ItemSearchInput
|
||||||
|
import Data.Flags exposing (Flags)
|
||||||
|
import Data.ItemQuery as IQ
|
||||||
|
import Data.ItemTemplate as IT
|
||||||
|
import Data.UiSettings exposing (UiSettings)
|
||||||
|
import Html exposing (Html, a, div, i, text)
|
||||||
|
import Html.Attributes exposing (class, classList, href, title)
|
||||||
|
import Html.Events exposing (onClick)
|
||||||
|
import Http
|
||||||
|
import Messages.Comp.ItemLinkForm exposing (Texts)
|
||||||
|
import Page exposing (Page(..))
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ itemSearchModel : Comp.ItemSearchInput.Model
|
||||||
|
, relatedItems : List ItemLight
|
||||||
|
, targetItemId : String
|
||||||
|
, editMode : EditMode
|
||||||
|
, formState : FormState
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type EditMode
|
||||||
|
= AddRelated
|
||||||
|
| RemoveRelated
|
||||||
|
|
||||||
|
|
||||||
|
type FormState
|
||||||
|
= FormOk
|
||||||
|
| FormHttpError Http.Error
|
||||||
|
| FormError String
|
||||||
|
|
||||||
|
|
||||||
|
emptyModel : Model
|
||||||
|
emptyModel =
|
||||||
|
let
|
||||||
|
cfg =
|
||||||
|
Comp.ItemSearchInput.defaultConfig
|
||||||
|
in
|
||||||
|
{ itemSearchModel = Comp.ItemSearchInput.init cfg
|
||||||
|
, relatedItems = []
|
||||||
|
, targetItemId = ""
|
||||||
|
, editMode = AddRelated
|
||||||
|
, formState = FormOk
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= ItemSearchMsg Comp.ItemSearchInput.Msg
|
||||||
|
| RelatedItemsResp (Result Http.Error ItemLightGroup)
|
||||||
|
| UpdateRelatedResp (Result Http.Error BasicResult)
|
||||||
|
| DeleteRelatedItem ItemLight
|
||||||
|
| ToggleEditMode
|
||||||
|
|
||||||
|
|
||||||
|
init : Flags -> String -> ( Model, Cmd Msg )
|
||||||
|
init flags itemId =
|
||||||
|
let
|
||||||
|
searchCfg =
|
||||||
|
Comp.ItemSearchInput.defaultConfig
|
||||||
|
in
|
||||||
|
( { itemSearchModel = Comp.ItemSearchInput.init searchCfg
|
||||||
|
, relatedItems = []
|
||||||
|
, targetItemId = itemId
|
||||||
|
, editMode = AddRelated
|
||||||
|
, formState = FormOk
|
||||||
|
}
|
||||||
|
, initCmd flags itemId
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
initCmd : Flags -> String -> Cmd Msg
|
||||||
|
initCmd flags itemId =
|
||||||
|
Api.getRelatedItems flags itemId RelatedItemsResp
|
||||||
|
|
||||||
|
|
||||||
|
excludeResults : Model -> Maybe IQ.ItemQuery
|
||||||
|
excludeResults model =
|
||||||
|
let
|
||||||
|
relatedIds =
|
||||||
|
List.map .id model.relatedItems
|
||||||
|
|
||||||
|
all =
|
||||||
|
if model.targetItemId == "" then
|
||||||
|
relatedIds
|
||||||
|
|
||||||
|
else
|
||||||
|
model.targetItemId :: relatedIds
|
||||||
|
in
|
||||||
|
case all of
|
||||||
|
[] ->
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
ids ->
|
||||||
|
Just <| IQ.Not (IQ.ItemIdIn ids)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Update
|
||||||
|
|
||||||
|
|
||||||
|
update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
|
||||||
|
update flags msg model =
|
||||||
|
case msg of
|
||||||
|
ItemSearchMsg lm ->
|
||||||
|
case model.editMode of
|
||||||
|
AddRelated ->
|
||||||
|
let
|
||||||
|
result =
|
||||||
|
Comp.ItemSearchInput.update flags (excludeResults model) lm model.itemSearchModel
|
||||||
|
|
||||||
|
cmd =
|
||||||
|
case result.selected of
|
||||||
|
Just item ->
|
||||||
|
if model.targetItemId == "" then
|
||||||
|
Cmd.none
|
||||||
|
|
||||||
|
else
|
||||||
|
Api.addRelatedItems flags
|
||||||
|
{ item = model.targetItemId
|
||||||
|
, related = [ item.id ]
|
||||||
|
}
|
||||||
|
UpdateRelatedResp
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
Cmd.none
|
||||||
|
in
|
||||||
|
( { model | itemSearchModel = result.model }
|
||||||
|
, Cmd.batch
|
||||||
|
[ Cmd.map ItemSearchMsg result.cmd
|
||||||
|
, cmd
|
||||||
|
]
|
||||||
|
, Sub.map ItemSearchMsg result.sub
|
||||||
|
)
|
||||||
|
|
||||||
|
RemoveRelated ->
|
||||||
|
( model, Cmd.none, Sub.none )
|
||||||
|
|
||||||
|
RelatedItemsResp (Ok list) ->
|
||||||
|
( { model
|
||||||
|
| relatedItems = list.items
|
||||||
|
, formState = FormOk
|
||||||
|
, editMode =
|
||||||
|
if List.isEmpty list.items then
|
||||||
|
AddRelated
|
||||||
|
|
||||||
|
else
|
||||||
|
model.editMode
|
||||||
|
}
|
||||||
|
, Cmd.none
|
||||||
|
, Sub.none
|
||||||
|
)
|
||||||
|
|
||||||
|
RelatedItemsResp (Err err) ->
|
||||||
|
( { model | formState = FormHttpError err }, Cmd.none, Sub.none )
|
||||||
|
|
||||||
|
UpdateRelatedResp (Ok res) ->
|
||||||
|
if res.success then
|
||||||
|
( { model | formState = FormOk }
|
||||||
|
, initCmd flags model.targetItemId
|
||||||
|
, Sub.none
|
||||||
|
)
|
||||||
|
|
||||||
|
else
|
||||||
|
( { model | formState = FormError res.message }, Cmd.none, Sub.none )
|
||||||
|
|
||||||
|
UpdateRelatedResp (Err err) ->
|
||||||
|
( { model | formState = FormHttpError err }, Cmd.none, Sub.none )
|
||||||
|
|
||||||
|
ToggleEditMode ->
|
||||||
|
let
|
||||||
|
next =
|
||||||
|
if model.editMode == RemoveRelated then
|
||||||
|
AddRelated
|
||||||
|
|
||||||
|
else
|
||||||
|
RemoveRelated
|
||||||
|
in
|
||||||
|
( { model | editMode = next }, Cmd.none, Sub.none )
|
||||||
|
|
||||||
|
DeleteRelatedItem item ->
|
||||||
|
case model.editMode of
|
||||||
|
RemoveRelated ->
|
||||||
|
if model.targetItemId == "" then
|
||||||
|
( model, Cmd.none, Sub.none )
|
||||||
|
|
||||||
|
else
|
||||||
|
( model, Api.removeRelatedItem flags model.targetItemId item.id UpdateRelatedResp, Sub.none )
|
||||||
|
|
||||||
|
AddRelated ->
|
||||||
|
( model, Cmd.none, Sub.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
|
view : Texts -> UiSettings -> Model -> Html Msg
|
||||||
|
view texts settings model =
|
||||||
|
div
|
||||||
|
[ classList
|
||||||
|
[ ( "bg-red-100 bg-opacity-25", model.editMode == RemoveRelated )
|
||||||
|
, ( "dark:bg-orange-80 dark:bg-opacity-10", model.editMode == RemoveRelated )
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ div [ class "relative" ]
|
||||||
|
[ Html.map ItemSearchMsg
|
||||||
|
(Comp.ItemSearchInput.view texts.itemSearchInput
|
||||||
|
settings
|
||||||
|
model.itemSearchModel
|
||||||
|
[ class "text-sm py-1 pr-6"
|
||||||
|
, classList [ ( "disabled", model.editMode == RemoveRelated ) ]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
, a
|
||||||
|
[ classList
|
||||||
|
[ ( "hidden", Comp.ItemSearchInput.hasFocus model.itemSearchModel )
|
||||||
|
, ( "bg-red-600 text-white dark:bg-orange-500 dark:text-slate-900 ", model.editMode == RemoveRelated )
|
||||||
|
, ( "opacity-50", model.editMode == AddRelated )
|
||||||
|
, ( S.deleteButtonBase, model.editMode == AddRelated )
|
||||||
|
]
|
||||||
|
, class " absolute right-0 top-0 rounded-r py-1 px-2 h-full block text-sm"
|
||||||
|
, href "#"
|
||||||
|
, onClick ToggleEditMode
|
||||||
|
]
|
||||||
|
[ i [ class "fa fa-trash " ] []
|
||||||
|
]
|
||||||
|
, div
|
||||||
|
[ class "absolute right-0 top-0 py-1 mr-1 w-4"
|
||||||
|
, classList [ ( "hidden", not (Comp.ItemSearchInput.isSearching model.itemSearchModel) ) ]
|
||||||
|
]
|
||||||
|
[ i [ class "fa fa-circle-notch animate-spin" ] []
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, case model.formState of
|
||||||
|
FormOk ->
|
||||||
|
viewRelatedItems texts settings model
|
||||||
|
|
||||||
|
FormHttpError err ->
|
||||||
|
div [ class S.errorText ]
|
||||||
|
[ text <| texts.httpError err
|
||||||
|
]
|
||||||
|
|
||||||
|
FormError msg ->
|
||||||
|
div [ class S.errorText ]
|
||||||
|
[ text msg
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewRelatedItems : Texts -> UiSettings -> Model -> Html Msg
|
||||||
|
viewRelatedItems texts settings model =
|
||||||
|
div [ class "px-1.5 pb-0.5" ]
|
||||||
|
(List.map (viewItem texts settings model) model.relatedItems)
|
||||||
|
|
||||||
|
|
||||||
|
viewItem : Texts -> UiSettings -> Model -> ItemLight -> Html Msg
|
||||||
|
viewItem texts _ model item =
|
||||||
|
let
|
||||||
|
mainTpl =
|
||||||
|
IT.name
|
||||||
|
|
||||||
|
tooltipTpl =
|
||||||
|
IT.concat
|
||||||
|
[ IT.dateShort
|
||||||
|
, IT.literal ", "
|
||||||
|
, IT.correspondent
|
||||||
|
]
|
||||||
|
|
||||||
|
tctx =
|
||||||
|
{ dateFormatLong = texts.dateFormatLong
|
||||||
|
, dateFormatShort = texts.dateFormatShort
|
||||||
|
, directionLabel = texts.directionLabel
|
||||||
|
}
|
||||||
|
in
|
||||||
|
case model.editMode of
|
||||||
|
AddRelated ->
|
||||||
|
a
|
||||||
|
[ class "flex items-center my-2"
|
||||||
|
, class S.link
|
||||||
|
, Page.href (ItemDetailPage item.id)
|
||||||
|
, title <| IT.render tooltipTpl tctx item
|
||||||
|
]
|
||||||
|
[ i [ class "fa fa-link text-xs mr-1" ] []
|
||||||
|
, IT.render mainTpl tctx item |> text
|
||||||
|
]
|
||||||
|
|
||||||
|
RemoveRelated ->
|
||||||
|
a
|
||||||
|
[ class "flex items-center my-2"
|
||||||
|
, class " text-red-600 hover:text-red-500 dark:text-orange-400 dark:hover:text-orange-300 "
|
||||||
|
, href "#"
|
||||||
|
, onClick (DeleteRelatedItem item)
|
||||||
|
]
|
||||||
|
[ i [ class "fa fa-trash mr-2" ] []
|
||||||
|
, IT.render mainTpl tctx item |> text
|
||||||
|
]
|
406
modules/webapp/src/main/elm/Comp/ItemSearchInput.elm
Normal file
406
modules/webapp/src/main/elm/Comp/ItemSearchInput.elm
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
module Comp.ItemSearchInput exposing (Config, Model, Msg, defaultConfig, hasFocus, init, isSearching, update, view)
|
||||||
|
|
||||||
|
import Api
|
||||||
|
import Api.Model.ItemLight exposing (ItemLight)
|
||||||
|
import Api.Model.ItemLightList exposing (ItemLightList)
|
||||||
|
import Comp.SimpleTextInput
|
||||||
|
import Data.Flags exposing (Flags)
|
||||||
|
import Data.ItemQuery as IQ
|
||||||
|
import Data.Items
|
||||||
|
import Data.UiSettings exposing (UiSettings)
|
||||||
|
import Html exposing (Attribute, Html, a, div, span, text)
|
||||||
|
import Html.Attributes exposing (class, classList, href, placeholder)
|
||||||
|
import Html.Events exposing (onBlur, onClick, onFocus)
|
||||||
|
import Http
|
||||||
|
import Messages.Comp.ItemSearchInput exposing (Texts)
|
||||||
|
import Process
|
||||||
|
import Styles as S
|
||||||
|
import Task
|
||||||
|
import Util.Html
|
||||||
|
import Util.List
|
||||||
|
import Util.String
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ searchModel : Comp.SimpleTextInput.Model
|
||||||
|
, config : Config
|
||||||
|
, results : List ItemLight
|
||||||
|
, searchProgress : Bool
|
||||||
|
, menuState : MenuState
|
||||||
|
, focus : Bool
|
||||||
|
, errorState : ErrorState
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias MenuState =
|
||||||
|
{ open : Bool
|
||||||
|
, active : Maybe String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type ErrorState
|
||||||
|
= NoError
|
||||||
|
| HttpError Http.Error
|
||||||
|
|
||||||
|
|
||||||
|
type alias Config =
|
||||||
|
{ makeQuery : String -> IQ.ItemQuery
|
||||||
|
, limit : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defaultConfig : Config
|
||||||
|
defaultConfig =
|
||||||
|
{ limit = 15
|
||||||
|
, makeQuery = defaultMakeQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defaultMakeQuery : String -> IQ.ItemQuery
|
||||||
|
defaultMakeQuery str =
|
||||||
|
let
|
||||||
|
qstr =
|
||||||
|
Util.String.appendIfAbsent "*" str
|
||||||
|
in
|
||||||
|
IQ.Or
|
||||||
|
[ IQ.ItemIdMatch qstr
|
||||||
|
, IQ.AllNames qstr
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
init : Config -> Model
|
||||||
|
init cfg =
|
||||||
|
let
|
||||||
|
textCfg =
|
||||||
|
{ delay = 200
|
||||||
|
, setOnTyping = True
|
||||||
|
, setOnEnter = True
|
||||||
|
, setOnBlur = False
|
||||||
|
}
|
||||||
|
in
|
||||||
|
{ searchModel = Comp.SimpleTextInput.init textCfg Nothing
|
||||||
|
, config = cfg
|
||||||
|
, results = []
|
||||||
|
, searchProgress = False
|
||||||
|
, menuState =
|
||||||
|
{ open = False
|
||||||
|
, active = Nothing
|
||||||
|
}
|
||||||
|
, errorState = NoError
|
||||||
|
, focus = False
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= SetSearchMsg Comp.SimpleTextInput.Msg
|
||||||
|
| SearchResultResp (Result Http.Error ItemLightList)
|
||||||
|
| SelectItem ItemLight
|
||||||
|
| FocusGained
|
||||||
|
| FocusRemoved Bool
|
||||||
|
|
||||||
|
|
||||||
|
type alias UpdateResult =
|
||||||
|
{ model : Model
|
||||||
|
, cmd : Cmd Msg
|
||||||
|
, sub : Sub Msg
|
||||||
|
, selected : Maybe ItemLight
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isSearching : Model -> Bool
|
||||||
|
isSearching model =
|
||||||
|
model.searchProgress
|
||||||
|
|
||||||
|
|
||||||
|
hasFocus : Model -> Bool
|
||||||
|
hasFocus model =
|
||||||
|
model.focus
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Update
|
||||||
|
|
||||||
|
|
||||||
|
unit : Model -> UpdateResult
|
||||||
|
unit model =
|
||||||
|
UpdateResult model Cmd.none Sub.none Nothing
|
||||||
|
|
||||||
|
|
||||||
|
update : Flags -> Maybe IQ.ItemQuery -> Msg -> Model -> UpdateResult
|
||||||
|
update flags addQuery msg model =
|
||||||
|
case msg of
|
||||||
|
SetSearchMsg lm ->
|
||||||
|
let
|
||||||
|
res =
|
||||||
|
Comp.SimpleTextInput.update lm model.searchModel
|
||||||
|
|
||||||
|
findActiveItem results =
|
||||||
|
Maybe.andThen (\id -> List.filter (\e -> e.id == id) results |> List.head) model.menuState.active
|
||||||
|
|
||||||
|
( mm, selectAction ) =
|
||||||
|
case res.keyPressed of
|
||||||
|
Just Util.Html.ESC ->
|
||||||
|
( setMenuOpen False model, False )
|
||||||
|
|
||||||
|
Just Util.Html.Enter ->
|
||||||
|
if model.menuState.open then
|
||||||
|
( model, True )
|
||||||
|
|
||||||
|
else
|
||||||
|
( setMenuOpen True model, False )
|
||||||
|
|
||||||
|
Just Util.Html.Up ->
|
||||||
|
( setActivePrev model, False )
|
||||||
|
|
||||||
|
Just Util.Html.Down ->
|
||||||
|
( setActiveNext model, False )
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( model, False )
|
||||||
|
|
||||||
|
( model_, searchCmd ) =
|
||||||
|
case res.change of
|
||||||
|
Comp.SimpleTextInput.ValueUnchanged ->
|
||||||
|
( mm, Cmd.none )
|
||||||
|
|
||||||
|
Comp.SimpleTextInput.ValueUpdated v ->
|
||||||
|
let
|
||||||
|
cmd =
|
||||||
|
makeSearchCmd flags model addQuery v
|
||||||
|
in
|
||||||
|
( { mm | searchProgress = cmd /= Cmd.none }, cmd )
|
||||||
|
in
|
||||||
|
if selectAction then
|
||||||
|
findActiveItem model.results
|
||||||
|
|> Maybe.map SelectItem
|
||||||
|
|> Maybe.map (\m -> update flags addQuery m model)
|
||||||
|
|> Maybe.withDefault (unit model)
|
||||||
|
|
||||||
|
else
|
||||||
|
{ model = { model_ | searchModel = res.model }
|
||||||
|
, cmd = Cmd.batch [ Cmd.map SetSearchMsg res.cmd, searchCmd ]
|
||||||
|
, sub = Sub.map SetSearchMsg res.sub
|
||||||
|
, selected = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchResultResp (Ok list) ->
|
||||||
|
unit
|
||||||
|
{ model
|
||||||
|
| results = Data.Items.flatten list
|
||||||
|
, errorState = NoError
|
||||||
|
, searchProgress = False
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchResultResp (Err err) ->
|
||||||
|
unit { model | errorState = HttpError err, searchProgress = False }
|
||||||
|
|
||||||
|
SelectItem item ->
|
||||||
|
let
|
||||||
|
ms =
|
||||||
|
model.menuState
|
||||||
|
|
||||||
|
( searchModel, sub ) =
|
||||||
|
Comp.SimpleTextInput.setValue model.searchModel ""
|
||||||
|
|
||||||
|
res =
|
||||||
|
unit
|
||||||
|
{ model
|
||||||
|
| menuState = { ms | open = False }
|
||||||
|
, searchModel = searchModel
|
||||||
|
}
|
||||||
|
in
|
||||||
|
{ res | selected = Just item, sub = Sub.map SetSearchMsg sub }
|
||||||
|
|
||||||
|
FocusGained ->
|
||||||
|
unit (setMenuOpen True model |> setFocus True)
|
||||||
|
|
||||||
|
FocusRemoved flag ->
|
||||||
|
if flag then
|
||||||
|
unit (setMenuOpen False model |> setFocus False)
|
||||||
|
|
||||||
|
else
|
||||||
|
{ model = model
|
||||||
|
, cmd =
|
||||||
|
Process.sleep 100
|
||||||
|
|> Task.perform (\_ -> FocusRemoved True)
|
||||||
|
, sub = Sub.none
|
||||||
|
, selected = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
makeSearchCmd : Flags -> Model -> Maybe IQ.ItemQuery -> Maybe String -> Cmd Msg
|
||||||
|
makeSearchCmd flags model addQuery str =
|
||||||
|
let
|
||||||
|
itemQuery =
|
||||||
|
IQ.and
|
||||||
|
[ addQuery
|
||||||
|
, Maybe.map model.config.makeQuery str
|
||||||
|
]
|
||||||
|
|
||||||
|
qstr =
|
||||||
|
IQ.renderMaybe itemQuery
|
||||||
|
|
||||||
|
q =
|
||||||
|
{ offset = Nothing
|
||||||
|
, limit = Just model.config.limit
|
||||||
|
, withDetails = Just False
|
||||||
|
, searchMode = Nothing
|
||||||
|
, query = qstr
|
||||||
|
}
|
||||||
|
in
|
||||||
|
if str == Nothing then
|
||||||
|
Cmd.none
|
||||||
|
|
||||||
|
else
|
||||||
|
Api.itemSearch flags q SearchResultResp
|
||||||
|
|
||||||
|
|
||||||
|
setMenuOpen : Bool -> Model -> Model
|
||||||
|
setMenuOpen flag model =
|
||||||
|
let
|
||||||
|
ms =
|
||||||
|
model.menuState
|
||||||
|
in
|
||||||
|
{ model | menuState = { ms | open = flag } }
|
||||||
|
|
||||||
|
|
||||||
|
setFocus : Bool -> Model -> Model
|
||||||
|
setFocus flag model =
|
||||||
|
{ model | focus = flag }
|
||||||
|
|
||||||
|
|
||||||
|
setActiveNext : Model -> Model
|
||||||
|
setActiveNext model =
|
||||||
|
let
|
||||||
|
find ms =
|
||||||
|
case ms.active of
|
||||||
|
Just id ->
|
||||||
|
Util.List.findNext (\e -> e.id == id) model.results
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
List.head model.results
|
||||||
|
|
||||||
|
set ms act =
|
||||||
|
{ ms | active = act }
|
||||||
|
|
||||||
|
updateMs =
|
||||||
|
find >> Maybe.map .id >> set model.menuState
|
||||||
|
in
|
||||||
|
if model.menuState.open then
|
||||||
|
{ model | menuState = updateMs model.menuState }
|
||||||
|
|
||||||
|
else
|
||||||
|
model
|
||||||
|
|
||||||
|
|
||||||
|
setActivePrev : Model -> Model
|
||||||
|
setActivePrev model =
|
||||||
|
let
|
||||||
|
find ms =
|
||||||
|
case ms.active of
|
||||||
|
Just id ->
|
||||||
|
Util.List.findPrev (\e -> e.id == id) model.results
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
List.reverse model.results |> List.head
|
||||||
|
|
||||||
|
set ms act =
|
||||||
|
{ ms | active = act }
|
||||||
|
|
||||||
|
updateMs =
|
||||||
|
find >> Maybe.map .id >> set model.menuState
|
||||||
|
in
|
||||||
|
if model.menuState.open then
|
||||||
|
{ model | menuState = updateMs model.menuState }
|
||||||
|
|
||||||
|
else
|
||||||
|
model
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
|
view : Texts -> UiSettings -> Model -> List (Attribute Msg) -> Html Msg
|
||||||
|
view texts settings model attrs =
|
||||||
|
let
|
||||||
|
inputAttrs =
|
||||||
|
[ class S.textInput
|
||||||
|
, onFocus FocusGained
|
||||||
|
, onBlur (FocusRemoved False)
|
||||||
|
, placeholder texts.placeholder
|
||||||
|
]
|
||||||
|
in
|
||||||
|
div
|
||||||
|
[ class "relative"
|
||||||
|
]
|
||||||
|
[ Comp.SimpleTextInput.viewMap SetSearchMsg
|
||||||
|
(inputAttrs ++ attrs)
|
||||||
|
model.searchModel
|
||||||
|
, renderResultMenu texts settings model
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
renderResultMenu : Texts -> UiSettings -> Model -> Html Msg
|
||||||
|
renderResultMenu texts _ model =
|
||||||
|
div
|
||||||
|
[ class "z-50 max-h-96 overflow-y-auto"
|
||||||
|
, class dropdownMenu
|
||||||
|
, classList [ ( "hidden", not model.menuState.open ) ]
|
||||||
|
]
|
||||||
|
(case model.errorState of
|
||||||
|
HttpError err ->
|
||||||
|
[ div
|
||||||
|
[ class dropdownItem
|
||||||
|
, class S.errorText
|
||||||
|
]
|
||||||
|
[ text <| texts.httpError err
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
NoError ->
|
||||||
|
case model.results of
|
||||||
|
[] ->
|
||||||
|
[ div [ class dropdownItem ]
|
||||||
|
[ span [ class "italic" ]
|
||||||
|
[ text texts.noResults
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
List.map (renderResultItem model) model.results
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
renderResultItem : Model -> ItemLight -> Html Msg
|
||||||
|
renderResultItem model item =
|
||||||
|
let
|
||||||
|
active =
|
||||||
|
model.menuState.active == Just item.id
|
||||||
|
in
|
||||||
|
a
|
||||||
|
[ classList
|
||||||
|
[ ( dropdownItem, not active )
|
||||||
|
, ( activeItem, active )
|
||||||
|
]
|
||||||
|
, href "#"
|
||||||
|
, onClick (SelectItem item)
|
||||||
|
]
|
||||||
|
[ text item.name
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
dropdownMenu : String
|
||||||
|
dropdownMenu =
|
||||||
|
" absolute left-0 bg-white dark:bg-slate-800 border dark:border-slate-700 dark:text-slate-300 shadow-lg opacity-1 transition duration-200 w-full "
|
||||||
|
|
||||||
|
|
||||||
|
dropdownItem : String
|
||||||
|
dropdownItem =
|
||||||
|
"transition-colors duration-200 items-center block px-4 py-2 text-normal hover:bg-gray-200 dark:hover:bg-slate-700 dark:hover:text-slate-50"
|
||||||
|
|
||||||
|
|
||||||
|
activeItem : String
|
||||||
|
activeItem =
|
||||||
|
"transition-colors duration-200 items-center block px-4 py-2 text-normal bg-gray-200 dark:bg-slate-700 dark:text-slate-50"
|
@ -119,6 +119,7 @@ type alias Result =
|
|||||||
, change : ValueChange
|
, change : ValueChange
|
||||||
, cmd : Cmd Msg
|
, cmd : Cmd Msg
|
||||||
, sub : Sub Msg
|
, sub : Sub Msg
|
||||||
|
, keyPressed : Maybe KeyCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -144,6 +145,7 @@ update msg (Model model) =
|
|||||||
, change = ValueUnchanged
|
, change = ValueUnchanged
|
||||||
, cmd = cmd
|
, cmd = cmd
|
||||||
, sub = makeSub model newThrottle
|
, sub = makeSub model newThrottle
|
||||||
|
, keyPressed = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateThrottle ->
|
UpdateThrottle ->
|
||||||
@ -155,6 +157,7 @@ update msg (Model model) =
|
|||||||
, change = ValueUnchanged
|
, change = ValueUnchanged
|
||||||
, cmd = cmd
|
, cmd = cmd
|
||||||
, sub = makeSub model newThrottle
|
, sub = makeSub model newThrottle
|
||||||
|
, keyPressed = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
DelayedSet ->
|
DelayedSet ->
|
||||||
@ -172,14 +175,22 @@ update msg (Model model) =
|
|||||||
unit model
|
unit model
|
||||||
|
|
||||||
KeyPressed (Just Util.Html.Enter) ->
|
KeyPressed (Just Util.Html.Enter) ->
|
||||||
|
let
|
||||||
|
res =
|
||||||
if model.cfg.setOnEnter then
|
if model.cfg.setOnEnter then
|
||||||
publishChange model
|
publishChange model
|
||||||
|
|
||||||
else
|
else
|
||||||
unit model
|
unit model
|
||||||
|
in
|
||||||
|
{ res | keyPressed = Just Util.Html.Enter }
|
||||||
|
|
||||||
KeyPressed _ ->
|
KeyPressed kc ->
|
||||||
|
let
|
||||||
|
res =
|
||||||
unit model
|
unit model
|
||||||
|
in
|
||||||
|
{ res | keyPressed = kc }
|
||||||
|
|
||||||
|
|
||||||
publishChange : InnerModel -> Result
|
publishChange : InnerModel -> Result
|
||||||
@ -192,6 +203,7 @@ publishChange model =
|
|||||||
(ValueUpdated model.value)
|
(ValueUpdated model.value)
|
||||||
Cmd.none
|
Cmd.none
|
||||||
(makeSub model model.throttle)
|
(makeSub model model.throttle)
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
|
||||||
unit : InnerModel -> Result
|
unit : InnerModel -> Result
|
||||||
@ -200,6 +212,7 @@ unit model =
|
|||||||
, change = ValueUnchanged
|
, change = ValueUnchanged
|
||||||
, cmd = Cmd.none
|
, cmd = Cmd.none
|
||||||
, sub = makeSub model model.throttle
|
, sub = makeSub model model.throttle
|
||||||
|
, keyPressed = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import Api.Model.CustomFieldValue exposing (CustomFieldValue)
|
|||||||
import Api.Model.ItemQuery as RQ
|
import Api.Model.ItemQuery as RQ
|
||||||
import Data.Direction exposing (Direction)
|
import Data.Direction exposing (Direction)
|
||||||
import Data.SearchMode exposing (SearchMode)
|
import Data.SearchMode exposing (SearchMode)
|
||||||
|
import Util.String
|
||||||
|
|
||||||
|
|
||||||
type TagMatch
|
type TagMatch
|
||||||
@ -58,6 +59,7 @@ type ItemQuery
|
|||||||
| Source AttrMatch String
|
| Source AttrMatch String
|
||||||
| Dir Direction
|
| Dir Direction
|
||||||
| ItemIdIn (List String)
|
| ItemIdIn (List String)
|
||||||
|
| ItemIdMatch String
|
||||||
| ItemName AttrMatch String
|
| ItemName AttrMatch String
|
||||||
| AllNames String
|
| AllNames String
|
||||||
| Contents String
|
| Contents String
|
||||||
@ -207,6 +209,13 @@ render q =
|
|||||||
ItemIdIn ids ->
|
ItemIdIn ids ->
|
||||||
"id~=" ++ String.join "," ids
|
"id~=" ++ String.join "," ids
|
||||||
|
|
||||||
|
ItemIdMatch id ->
|
||||||
|
if String.length id == 47 then
|
||||||
|
"id" ++ attrMatch Eq ++ id
|
||||||
|
|
||||||
|
else
|
||||||
|
"id" ++ attrMatch Like ++ Util.String.appendIfAbsent "*" id
|
||||||
|
|
||||||
ItemName m str ->
|
ItemName m str ->
|
||||||
"name" ++ attrMatch m ++ quoteStr str
|
"name" ++ attrMatch m ++ quoteStr str
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ module Data.ItemTemplate exposing
|
|||||||
, concat
|
, concat
|
||||||
, concerning
|
, concerning
|
||||||
, corrOrg
|
, corrOrg
|
||||||
|
, corrOrgOrPerson
|
||||||
, corrPerson
|
, corrPerson
|
||||||
, correspondent
|
, correspondent
|
||||||
, dateLong
|
, dateLong
|
||||||
@ -229,6 +230,11 @@ correspondent =
|
|||||||
combine ", " corrOrg corrPerson
|
combine ", " corrOrg corrPerson
|
||||||
|
|
||||||
|
|
||||||
|
corrOrgOrPerson : ItemTemplate
|
||||||
|
corrOrgOrPerson =
|
||||||
|
firstNonEmpty [ corrOrg, corrPerson ]
|
||||||
|
|
||||||
|
|
||||||
concPerson : ItemTemplate
|
concPerson : ItemTemplate
|
||||||
concPerson =
|
concPerson =
|
||||||
from (.concPerson >> getName)
|
from (.concPerson >> getName)
|
||||||
|
@ -21,6 +21,7 @@ import Messages.Comp.ItemDetail.ConfirmModal
|
|||||||
import Messages.Comp.ItemDetail.ItemInfoHeader
|
import Messages.Comp.ItemDetail.ItemInfoHeader
|
||||||
import Messages.Comp.ItemDetail.Notes
|
import Messages.Comp.ItemDetail.Notes
|
||||||
import Messages.Comp.ItemDetail.SingleAttachment
|
import Messages.Comp.ItemDetail.SingleAttachment
|
||||||
|
import Messages.Comp.ItemLinkForm
|
||||||
import Messages.Comp.ItemMail
|
import Messages.Comp.ItemMail
|
||||||
import Messages.Comp.SentMails
|
import Messages.Comp.SentMails
|
||||||
import Messages.DateFormat as DF
|
import Messages.DateFormat as DF
|
||||||
@ -36,6 +37,7 @@ type alias Texts =
|
|||||||
, itemMail : Messages.Comp.ItemMail.Texts
|
, itemMail : Messages.Comp.ItemMail.Texts
|
||||||
, detailEdit : Messages.Comp.DetailEdit.Texts
|
, detailEdit : Messages.Comp.DetailEdit.Texts
|
||||||
, confirmModal : Messages.Comp.ItemDetail.ConfirmModal.Texts
|
, confirmModal : Messages.Comp.ItemDetail.ConfirmModal.Texts
|
||||||
|
, itemLinkForm : Messages.Comp.ItemLinkForm.Texts
|
||||||
, httpError : Http.Error -> String
|
, httpError : Http.Error -> String
|
||||||
, key : String
|
, key : String
|
||||||
, backToSearchResults : String
|
, backToSearchResults : String
|
||||||
@ -61,6 +63,7 @@ type alias Texts =
|
|||||||
, close : String
|
, close : String
|
||||||
, selectItem : String
|
, selectItem : String
|
||||||
, deselectItem : String
|
, deselectItem : String
|
||||||
|
, relatedItems : String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -74,6 +77,7 @@ gb tz =
|
|||||||
, itemMail = Messages.Comp.ItemMail.gb
|
, itemMail = Messages.Comp.ItemMail.gb
|
||||||
, detailEdit = Messages.Comp.DetailEdit.gb
|
, detailEdit = Messages.Comp.DetailEdit.gb
|
||||||
, confirmModal = Messages.Comp.ItemDetail.ConfirmModal.gb
|
, confirmModal = Messages.Comp.ItemDetail.ConfirmModal.gb
|
||||||
|
, itemLinkForm = Messages.Comp.ItemLinkForm.gb tz
|
||||||
, httpError = Messages.Comp.HttpError.gb
|
, httpError = Messages.Comp.HttpError.gb
|
||||||
, key = "Key"
|
, key = "Key"
|
||||||
, backToSearchResults = "Back to search results"
|
, backToSearchResults = "Back to search results"
|
||||||
@ -99,6 +103,7 @@ gb tz =
|
|||||||
, close = "Close"
|
, close = "Close"
|
||||||
, selectItem = "Select this item"
|
, selectItem = "Select this item"
|
||||||
, deselectItem = "Deselect this item"
|
, deselectItem = "Deselect this item"
|
||||||
|
, relatedItems = "Linked items"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -112,6 +117,7 @@ de tz =
|
|||||||
, itemMail = Messages.Comp.ItemMail.de
|
, itemMail = Messages.Comp.ItemMail.de
|
||||||
, detailEdit = Messages.Comp.DetailEdit.de
|
, detailEdit = Messages.Comp.DetailEdit.de
|
||||||
, confirmModal = Messages.Comp.ItemDetail.ConfirmModal.de
|
, confirmModal = Messages.Comp.ItemDetail.ConfirmModal.de
|
||||||
|
, itemLinkForm = Messages.Comp.ItemLinkForm.de tz
|
||||||
, httpError = Messages.Comp.HttpError.de
|
, httpError = Messages.Comp.HttpError.de
|
||||||
, key = "Taste"
|
, key = "Taste"
|
||||||
, backToSearchResults = "Zurück zur Suche"
|
, backToSearchResults = "Zurück zur Suche"
|
||||||
@ -137,6 +143,7 @@ de tz =
|
|||||||
, close = "Schließen"
|
, close = "Schließen"
|
||||||
, selectItem = "Zur Auswahl hinzufügen"
|
, selectItem = "Zur Auswahl hinzufügen"
|
||||||
, deselectItem = "Aus Auswahl entfernen"
|
, deselectItem = "Aus Auswahl entfernen"
|
||||||
|
, relatedItems = "Verknüpfte Dokumente"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -150,6 +157,7 @@ fr tz =
|
|||||||
, itemMail = Messages.Comp.ItemMail.fr
|
, itemMail = Messages.Comp.ItemMail.fr
|
||||||
, detailEdit = Messages.Comp.DetailEdit.fr
|
, detailEdit = Messages.Comp.DetailEdit.fr
|
||||||
, confirmModal = Messages.Comp.ItemDetail.ConfirmModal.fr
|
, confirmModal = Messages.Comp.ItemDetail.ConfirmModal.fr
|
||||||
|
, itemLinkForm = Messages.Comp.ItemLinkForm.fr tz
|
||||||
, httpError = Messages.Comp.HttpError.fr
|
, httpError = Messages.Comp.HttpError.fr
|
||||||
, key = "Clé"
|
, key = "Clé"
|
||||||
, backToSearchResults = "Retour aux résultat de recherche"
|
, backToSearchResults = "Retour aux résultat de recherche"
|
||||||
@ -175,4 +183,5 @@ fr tz =
|
|||||||
, close = "Fermer"
|
, close = "Fermer"
|
||||||
, selectItem = "Sélectionner ce document"
|
, selectItem = "Sélectionner ce document"
|
||||||
, deselectItem = "Désélectionner ce document"
|
, deselectItem = "Désélectionner ce document"
|
||||||
|
, relatedItems = "Documents associés"
|
||||||
}
|
}
|
||||||
|
49
modules/webapp/src/main/elm/Messages/Comp/ItemLinkForm.elm
Normal file
49
modules/webapp/src/main/elm/Messages/Comp/ItemLinkForm.elm
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
module Messages.Comp.ItemLinkForm exposing (Texts, de, fr, gb)
|
||||||
|
|
||||||
|
import Data.Direction exposing (Direction)
|
||||||
|
import Data.TimeZone exposing (TimeZone)
|
||||||
|
import Http
|
||||||
|
import Messages.Comp.HttpError
|
||||||
|
import Messages.Comp.ItemSearchInput
|
||||||
|
import Messages.Data.Direction
|
||||||
|
import Messages.DateFormat as DF
|
||||||
|
import Messages.UiLanguage exposing (UiLanguage(..))
|
||||||
|
|
||||||
|
|
||||||
|
type alias Texts =
|
||||||
|
{ dateFormatLong : Int -> String
|
||||||
|
, dateFormatShort : Int -> String
|
||||||
|
, directionLabel : Direction -> String
|
||||||
|
, itemSearchInput : Messages.Comp.ItemSearchInput.Texts
|
||||||
|
, httpError : Http.Error -> String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gb : TimeZone -> Texts
|
||||||
|
gb tz =
|
||||||
|
{ dateFormatLong = DF.formatDateLong English tz
|
||||||
|
, dateFormatShort = DF.formatDateShort English tz
|
||||||
|
, directionLabel = Messages.Data.Direction.gb
|
||||||
|
, itemSearchInput = Messages.Comp.ItemSearchInput.gb
|
||||||
|
, httpError = Messages.Comp.HttpError.gb
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
de : TimeZone -> Texts
|
||||||
|
de tz =
|
||||||
|
{ dateFormatLong = DF.formatDateLong German tz
|
||||||
|
, dateFormatShort = DF.formatDateShort German tz
|
||||||
|
, directionLabel = Messages.Data.Direction.de
|
||||||
|
, itemSearchInput = Messages.Comp.ItemSearchInput.de
|
||||||
|
, httpError = Messages.Comp.HttpError.de
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fr : TimeZone -> Texts
|
||||||
|
fr tz =
|
||||||
|
{ dateFormatLong = DF.formatDateLong French tz
|
||||||
|
, dateFormatShort = DF.formatDateShort French tz
|
||||||
|
, directionLabel = Messages.Data.Direction.fr
|
||||||
|
, itemSearchInput = Messages.Comp.ItemSearchInput.fr
|
||||||
|
, httpError = Messages.Comp.HttpError.fr
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
module Messages.Comp.ItemSearchInput exposing (Texts, de, fr, gb)
|
||||||
|
|
||||||
|
import Http
|
||||||
|
import Messages.Basics
|
||||||
|
import Messages.Comp.HttpError
|
||||||
|
|
||||||
|
|
||||||
|
type alias Texts =
|
||||||
|
{ noResults : String
|
||||||
|
, placeholder : String
|
||||||
|
, httpError : Http.Error -> String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gb : Texts
|
||||||
|
gb =
|
||||||
|
{ noResults = "No results"
|
||||||
|
, placeholder = Messages.Basics.gb.searchPlaceholder
|
||||||
|
, httpError = Messages.Comp.HttpError.gb
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
de : Texts
|
||||||
|
de =
|
||||||
|
{ noResults = "Keine Resultate"
|
||||||
|
, placeholder = Messages.Basics.de.searchPlaceholder
|
||||||
|
, httpError = Messages.Comp.HttpError.de
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fr : Texts
|
||||||
|
fr =
|
||||||
|
{ noResults = "Aucun document trouvé"
|
||||||
|
, placeholder = Messages.Basics.fr.searchPlaceholder
|
||||||
|
, httpError = Messages.Comp.HttpError.fr
|
||||||
|
}
|
@ -233,9 +233,14 @@ deleteButton =
|
|||||||
deleteButtonMain ++ deleteButtonHover
|
deleteButtonMain ++ deleteButtonHover
|
||||||
|
|
||||||
|
|
||||||
|
deleteButtonBase : String
|
||||||
|
deleteButtonBase =
|
||||||
|
" my-auto whitespace-nowrap border border-red-500 dark:border-lightred-500 text-red-500 dark:text-orange-500 text-center "
|
||||||
|
|
||||||
|
|
||||||
deleteButtonMain : String
|
deleteButtonMain : String
|
||||||
deleteButtonMain =
|
deleteButtonMain =
|
||||||
" rounded my-auto whitespace-nowrap border border-red-500 dark:border-lightred-500 text-red-500 dark:text-orange-500 text-center px-4 py-2 shadow-none focus:outline-none focus:ring focus:ring-opacity-75 "
|
" rounded px-4 py-2 shadow-none focus:outline-none focus:ring focus:ring-opacity-75 " ++ deleteButtonBase
|
||||||
|
|
||||||
|
|
||||||
deleteButtonHover : String
|
deleteButtonHover : String
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
|
|
||||||
module Util.String exposing
|
module Util.String exposing
|
||||||
( crazyEncode
|
( appendIfAbsent
|
||||||
|
, crazyEncode
|
||||||
, ellipsis
|
, ellipsis
|
||||||
, isBlank
|
, isBlank
|
||||||
, isNothingOrBlank
|
, isNothingOrBlank
|
||||||
@ -15,6 +16,7 @@ module Util.String exposing
|
|||||||
)
|
)
|
||||||
|
|
||||||
import Base64
|
import Base64
|
||||||
|
import Html exposing (strong)
|
||||||
|
|
||||||
|
|
||||||
crazyEncode : String -> String
|
crazyEncode : String -> String
|
||||||
@ -66,3 +68,12 @@ isNothingOrBlank : Maybe String -> Bool
|
|||||||
isNothingOrBlank ms =
|
isNothingOrBlank ms =
|
||||||
Maybe.map isBlank ms
|
Maybe.map isBlank ms
|
||||||
|> Maybe.withDefault True
|
|> Maybe.withDefault True
|
||||||
|
|
||||||
|
|
||||||
|
appendIfAbsent : String -> String -> String
|
||||||
|
appendIfAbsent suffix str =
|
||||||
|
if String.endsWith suffix str then
|
||||||
|
str
|
||||||
|
|
||||||
|
else
|
||||||
|
str ++ suffix
|
||||||
|
Reference in New Issue
Block a user