Link items it detail view

This commit is contained in:
eikek
2022-03-16 23:15:51 +01:00
parent 232baf5858
commit c7b2a1271a
14 changed files with 942 additions and 11 deletions

View File

@ -13,6 +13,7 @@ module Api exposing
, addCorrPerson
, addDashboard
, addMember
, addRelatedItems
, addShare
, addTag
, addTagsMultiple
@ -91,6 +92,7 @@ module Api exposing
, getPersonFull
, getPersons
, getPersonsLight
, getRelatedItems
, getScanMailbox
, getSentMails
, getShare
@ -130,6 +132,8 @@ module Api exposing
, refreshSession
, register
, removeMember
, removeRelatedItem
, removeRelatedItems
, removeTagsMultiple
, replaceDashboard
, reprocessItem
@ -227,7 +231,9 @@ import Api.Model.ImapSettingsList exposing (ImapSettingsList)
import Api.Model.InviteResult exposing (InviteResult)
import Api.Model.ItemDetail exposing (ItemDetail)
import Api.Model.ItemInsights exposing (ItemInsights)
import Api.Model.ItemLightGroup exposing (ItemLightGroup)
import Api.Model.ItemLightList exposing (ItemLightList)
import Api.Model.ItemLinkData exposing (ItemLinkData)
import Api.Model.ItemProposals exposing (ItemProposals)
import Api.Model.ItemQuery exposing (ItemQuery)
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

View File

@ -48,6 +48,7 @@ import Comp.DatePicker
import Comp.DetailEdit
import Comp.Dropdown
import Comp.Dropzone
import Comp.ItemLinkForm
import Comp.ItemMail
import Comp.KeyInput
import Comp.LinkTarget exposing (LinkTarget)
@ -121,6 +122,7 @@ type alias Model =
, editMenuTabsOpen : Set String
, viewMode : ViewMode
, showQrModel : ShowQrModel
, itemLinkModel : Comp.ItemLinkForm.Model
}
@ -256,6 +258,7 @@ emptyModel =
, editMenuTabsOpen = Set.empty
, viewMode = SimpleView
, showQrModel = initShowQrModel
, itemLinkModel = Comp.ItemLinkForm.emptyModel
}
@ -369,6 +372,7 @@ type Msg
| PrintElement String
| SetNameMsg Comp.SimpleTextInput.Msg
| ToggleSelectItem
| ItemLinkFormMsg Comp.ItemLinkForm.Msg
type SaveNameState

View File

@ -46,6 +46,7 @@ import Comp.ItemDetail.Model
, resultModelCmd
, resultModelCmdSub
)
import Comp.ItemLinkForm
import Comp.ItemMail
import Comp.KeyInput
import Comp.LinkTarget
@ -95,6 +96,13 @@ update inav env msg model =
( cm, cc ) =
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
resultModelCmd
( { model
@ -104,6 +112,7 @@ update inav env msg model =
, visibleAttach = 0
, attachMenuOpen = False
, customFieldsModel = cm
, itemLinkModel = ilm
}
, Cmd.batch
[ getOptions env.flags
@ -111,6 +120,7 @@ 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
]
)
@ -217,6 +227,9 @@ update inav env msg model =
else
Cmd.none
( ilm, ilc ) =
Comp.ItemLinkForm.init env.flags item.id
lastModel =
res9.model
in
@ -237,6 +250,7 @@ update inav env msg model =
, dueDate = item.dueDate
, visibleAttach = 0
, modalEdit = Nothing
, itemLinkModel = ilm
}
, cmd =
Cmd.batch
@ -254,6 +268,7 @@ update inav env msg model =
, Api.getSentMails env.flags item.id SentMailsResp
, Api.getPersons env.flags "" Data.PersonOrder.NameAsc GetPersonResp
, Cmd.map CustomFieldMsg (Comp.CustomFieldMultiInput.initCmd env.flags)
, Cmd.map ItemLinkFormMsg ilc
]
, sub =
Sub.batch
@ -1613,6 +1628,17 @@ update inav env msg model =
in
{ 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

View File

@ -24,6 +24,7 @@ import Comp.ItemDetail.Model
import Comp.ItemDetail.Notes
import Comp.ItemDetail.ShowQrCode
import Comp.ItemDetail.SingleAttachment
import Comp.ItemLinkForm
import Comp.ItemMail
import Comp.MenuBar as MB
import Comp.SentMails
@ -45,8 +46,6 @@ view : Texts -> ItemNav -> Env.View -> Model -> Html Msg
view texts inav env model =
div [ class "flex flex-col h-full" ]
[ header texts inav env model
-- , menuBar texts inav settings model
, body texts env.flags inav env.settings model
, itemModal texts model
]
@ -407,12 +406,18 @@ itemActions texts flags settings model classes =
notesAndSentMails : Texts -> Flags -> UiSettings -> Model -> String -> Html Msg
notesAndSentMails texts _ _ model classes =
notesAndSentMails texts _ settings model classes =
div
[ class "w-full md:mr-2 flex flex-col"
, class classes
]
[ 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
[ classList
[ ( "hidden", Comp.SentMails.isEmpty model.sentMails )

View 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
]

View 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"

View File

@ -119,6 +119,7 @@ type alias Result =
, change : ValueChange
, cmd : Cmd Msg
, sub : Sub Msg
, keyPressed : Maybe KeyCode
}
@ -144,6 +145,7 @@ update msg (Model model) =
, change = ValueUnchanged
, cmd = cmd
, sub = makeSub model newThrottle
, keyPressed = Nothing
}
UpdateThrottle ->
@ -155,6 +157,7 @@ update msg (Model model) =
, change = ValueUnchanged
, cmd = cmd
, sub = makeSub model newThrottle
, keyPressed = Nothing
}
DelayedSet ->
@ -172,14 +175,22 @@ update msg (Model model) =
unit model
KeyPressed (Just Util.Html.Enter) ->
let
res =
if model.cfg.setOnEnter then
publishChange model
else
unit model
in
{ res | keyPressed = Just Util.Html.Enter }
KeyPressed _ ->
KeyPressed kc ->
let
res =
unit model
in
{ res | keyPressed = kc }
publishChange : InnerModel -> Result
@ -192,6 +203,7 @@ publishChange model =
(ValueUpdated model.value)
Cmd.none
(makeSub model model.throttle)
Nothing
unit : InnerModel -> Result
@ -200,6 +212,7 @@ unit model =
, change = ValueUnchanged
, cmd = Cmd.none
, sub = makeSub model model.throttle
, keyPressed = Nothing
}

View File

@ -22,6 +22,7 @@ import Api.Model.CustomFieldValue exposing (CustomFieldValue)
import Api.Model.ItemQuery as RQ
import Data.Direction exposing (Direction)
import Data.SearchMode exposing (SearchMode)
import Util.String
type TagMatch
@ -58,6 +59,7 @@ type ItemQuery
| Source AttrMatch String
| Dir Direction
| ItemIdIn (List String)
| ItemIdMatch String
| ItemName AttrMatch String
| AllNames String
| Contents String
@ -207,6 +209,13 @@ render q =
ItemIdIn 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 ->
"name" ++ attrMatch m ++ quoteStr str

View File

@ -13,6 +13,7 @@ module Data.ItemTemplate exposing
, concat
, concerning
, corrOrg
, corrOrgOrPerson
, corrPerson
, correspondent
, dateLong
@ -229,6 +230,11 @@ correspondent =
combine ", " corrOrg corrPerson
corrOrgOrPerson : ItemTemplate
corrOrgOrPerson =
firstNonEmpty [ corrOrg, corrPerson ]
concPerson : ItemTemplate
concPerson =
from (.concPerson >> getName)

View File

@ -21,6 +21,7 @@ import Messages.Comp.ItemDetail.ConfirmModal
import Messages.Comp.ItemDetail.ItemInfoHeader
import Messages.Comp.ItemDetail.Notes
import Messages.Comp.ItemDetail.SingleAttachment
import Messages.Comp.ItemLinkForm
import Messages.Comp.ItemMail
import Messages.Comp.SentMails
import Messages.DateFormat as DF
@ -36,6 +37,7 @@ type alias Texts =
, itemMail : Messages.Comp.ItemMail.Texts
, detailEdit : Messages.Comp.DetailEdit.Texts
, confirmModal : Messages.Comp.ItemDetail.ConfirmModal.Texts
, itemLinkForm : Messages.Comp.ItemLinkForm.Texts
, httpError : Http.Error -> String
, key : String
, backToSearchResults : String
@ -61,6 +63,7 @@ type alias Texts =
, close : String
, selectItem : String
, deselectItem : String
, relatedItems : String
}
@ -74,6 +77,7 @@ gb tz =
, itemMail = Messages.Comp.ItemMail.gb
, detailEdit = Messages.Comp.DetailEdit.gb
, confirmModal = Messages.Comp.ItemDetail.ConfirmModal.gb
, itemLinkForm = Messages.Comp.ItemLinkForm.gb tz
, httpError = Messages.Comp.HttpError.gb
, key = "Key"
, backToSearchResults = "Back to search results"
@ -99,6 +103,7 @@ gb tz =
, close = "Close"
, selectItem = "Select this item"
, deselectItem = "Deselect this item"
, relatedItems = "Linked items"
}
@ -112,6 +117,7 @@ de tz =
, itemMail = Messages.Comp.ItemMail.de
, detailEdit = Messages.Comp.DetailEdit.de
, confirmModal = Messages.Comp.ItemDetail.ConfirmModal.de
, itemLinkForm = Messages.Comp.ItemLinkForm.de tz
, httpError = Messages.Comp.HttpError.de
, key = "Taste"
, backToSearchResults = "Zurück zur Suche"
@ -137,6 +143,7 @@ de tz =
, close = "Schließen"
, selectItem = "Zur Auswahl hinzufügen"
, deselectItem = "Aus Auswahl entfernen"
, relatedItems = "Verknüpfte Dokumente"
}
@ -150,6 +157,7 @@ fr tz =
, itemMail = Messages.Comp.ItemMail.fr
, detailEdit = Messages.Comp.DetailEdit.fr
, confirmModal = Messages.Comp.ItemDetail.ConfirmModal.fr
, itemLinkForm = Messages.Comp.ItemLinkForm.fr tz
, httpError = Messages.Comp.HttpError.fr
, key = "Clé"
, backToSearchResults = "Retour aux résultat de recherche"
@ -175,4 +183,5 @@ fr tz =
, close = "Fermer"
, selectItem = "Sélectionner ce document"
, deselectItem = "Désélectionner ce document"
, relatedItems = "Documents associés"
}

View 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
}

View File

@ -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
}

View File

@ -233,9 +233,14 @@ deleteButton =
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 =
" 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

View File

@ -6,7 +6,8 @@
module Util.String exposing
( crazyEncode
( appendIfAbsent
, crazyEncode
, ellipsis
, isBlank
, isNothingOrBlank
@ -15,6 +16,7 @@ module Util.String exposing
)
import Base64
import Html exposing (strong)
crazyEncode : String -> String
@ -66,3 +68,12 @@ isNothingOrBlank : Maybe String -> Bool
isNothingOrBlank ms =
Maybe.map isBlank ms
|> Maybe.withDefault True
appendIfAbsent : String -> String -> String
appendIfAbsent suffix str =
if String.endsWith suffix str then
str
else
str ++ suffix