mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-04-04 10:29:34 +00:00
SearchMenu uses query string instead of json form
This commit is contained in:
parent
f8307f77c6
commit
889e4f4fb0
@ -1696,22 +1696,22 @@ itemIndexSearch flags query receive =
|
||||
}
|
||||
|
||||
|
||||
itemSearch : Flags -> ItemSearch -> (Result Http.Error ItemLightList -> msg) -> Cmd msg
|
||||
itemSearch : Flags -> ItemQuery -> (Result Http.Error ItemLightList -> msg) -> Cmd msg
|
||||
itemSearch flags search receive =
|
||||
Http2.authPost
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/item/searchFormWithTags"
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/item/search"
|
||||
, account = getAccount flags
|
||||
, body = Http.jsonBody (Api.Model.ItemSearch.encode search)
|
||||
, body = Http.jsonBody (Api.Model.ItemQuery.encode search)
|
||||
, expect = Http.expectJson receive Api.Model.ItemLightList.decoder
|
||||
}
|
||||
|
||||
|
||||
itemSearchStats : Flags -> ItemSearch -> (Result Http.Error SearchStats -> msg) -> Cmd msg
|
||||
itemSearchStats : Flags -> ItemQuery -> (Result Http.Error SearchStats -> msg) -> Cmd msg
|
||||
itemSearchStats flags search receive =
|
||||
Http2.authPost
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/item/searchFormStats"
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/item/searchStats"
|
||||
, account = getAccount flags
|
||||
, body = Http.jsonBody (Api.Model.ItemSearch.encode search)
|
||||
, body = Http.jsonBody (Api.Model.ItemQuery.encode search)
|
||||
, expect = Http.expectJson receive Api.Model.SearchStats.decoder
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ module Comp.SearchMenu exposing
|
||||
, Msg(..)
|
||||
, NextState
|
||||
, TextSearchModel
|
||||
, getItemSearch
|
||||
, getItemQuery
|
||||
, init
|
||||
, isFulltextSearch
|
||||
, isNamesSearch
|
||||
@ -21,7 +21,7 @@ import Api.Model.EquipmentList exposing (EquipmentList)
|
||||
import Api.Model.FolderStats exposing (FolderStats)
|
||||
import Api.Model.IdName exposing (IdName)
|
||||
import Api.Model.ItemFieldValue exposing (ItemFieldValue)
|
||||
import Api.Model.ItemSearch exposing (ItemSearch)
|
||||
import Api.Model.ItemQuery exposing (ItemQuery)
|
||||
import Api.Model.PersonList exposing (PersonList)
|
||||
import Api.Model.ReferenceList exposing (ReferenceList)
|
||||
import Api.Model.SearchStats exposing (SearchStats)
|
||||
@ -38,6 +38,7 @@ import Data.DropdownStyle as DS
|
||||
import Data.Fields
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.Icons as Icons
|
||||
import Data.ItemQuery as Q exposing (ItemQuery)
|
||||
import Data.PersonUse
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import DatePicker exposing (DatePicker)
|
||||
@ -234,11 +235,21 @@ getDirection model =
|
||||
Nothing
|
||||
|
||||
|
||||
getItemSearch : Model -> ItemSearch
|
||||
getItemSearch model =
|
||||
getItemQuery : Model -> Maybe ItemQuery
|
||||
getItemQuery model =
|
||||
let
|
||||
e =
|
||||
Api.Model.ItemSearch.empty
|
||||
when flag body =
|
||||
if flag then
|
||||
Just body
|
||||
|
||||
else
|
||||
Nothing
|
||||
|
||||
whenNot flag body =
|
||||
when (not flag) body
|
||||
|
||||
whenNotEmpty list f =
|
||||
whenNot (List.isEmpty list) (f list)
|
||||
|
||||
amendWildcards s =
|
||||
if String.startsWith "\"" s && String.endsWith "\"" s then
|
||||
@ -254,35 +265,52 @@ getItemSearch model =
|
||||
textSearch =
|
||||
textSearchValue model.textSearchModel
|
||||
in
|
||||
{ e
|
||||
| tagsInclude = model.tagSelection.includeTags |> List.map .tag |> List.map .id
|
||||
, tagsExclude = model.tagSelection.excludeTags |> List.map .tag |> List.map .id
|
||||
, corrPerson = Comp.Dropdown.getSelected model.corrPersonModel |> List.map .id |> List.head
|
||||
, corrOrg = Comp.Dropdown.getSelected model.orgModel |> List.map .id |> List.head
|
||||
, concPerson = Comp.Dropdown.getSelected model.concPersonModel |> List.map .id |> List.head
|
||||
, concEquip = Comp.Dropdown.getSelected model.concEquipmentModel |> List.map .id |> List.head
|
||||
, folder = model.selectedFolder |> Maybe.map .id
|
||||
, direction =
|
||||
Comp.Dropdown.getSelected model.directionModel
|
||||
|> List.head
|
||||
|> Maybe.map Data.Direction.toString
|
||||
, inbox = model.inboxCheckbox
|
||||
, dateFrom = model.fromDate
|
||||
, dateUntil = model.untilDate
|
||||
, dueDateFrom = model.fromDueDate
|
||||
, dueDateUntil = model.untilDueDate
|
||||
, name =
|
||||
model.nameModel
|
||||
|> Maybe.map amendWildcards
|
||||
, allNames =
|
||||
textSearch.nameSearch
|
||||
|> Maybe.map amendWildcards
|
||||
, fullText = textSearch.fullText
|
||||
, tagCategoriesInclude = model.tagSelection.includeCats |> List.map .name
|
||||
, tagCategoriesExclude = model.tagSelection.excludeCats |> List.map .name
|
||||
, customValues = Data.CustomFieldChange.toFieldValues model.customValues
|
||||
, source = model.sourceModel
|
||||
}
|
||||
Q.and
|
||||
[ when model.inboxCheckbox (Q.Inbox True)
|
||||
, whenNotEmpty (model.tagSelection.includeTags |> List.map (.tag >> .id))
|
||||
(Q.TagIds Q.AllMatch)
|
||||
, whenNotEmpty (model.tagSelection.excludeTags |> List.map (.tag >> .id))
|
||||
(\ids -> Q.Not (Q.TagIds Q.AnyMatch ids))
|
||||
, whenNotEmpty (model.tagSelection.includeCats |> List.map .name)
|
||||
(Q.CatNames Q.AllMatch)
|
||||
, whenNotEmpty (model.tagSelection.excludeCats |> List.map .name)
|
||||
(\ids -> Q.Not <| Q.CatNames Q.AnyMatch ids)
|
||||
, model.selectedFolder |> Maybe.map .id |> Maybe.map (Q.FolderId Q.Eq)
|
||||
, Comp.Dropdown.getSelected model.orgModel
|
||||
|> List.map .id
|
||||
|> List.head
|
||||
|> Maybe.map (Q.CorrOrgId Q.Eq)
|
||||
, Comp.Dropdown.getSelected model.corrPersonModel
|
||||
|> List.map .id
|
||||
|> List.head
|
||||
|> Maybe.map (Q.CorrPersId Q.Eq)
|
||||
, Comp.Dropdown.getSelected model.concPersonModel
|
||||
|> List.map .id
|
||||
|> List.head
|
||||
|> Maybe.map (Q.ConcPersId Q.Eq)
|
||||
, Comp.Dropdown.getSelected model.concEquipmentModel
|
||||
|> List.map .id
|
||||
|> List.head
|
||||
|> Maybe.map (Q.ConcEquipId Q.Eq)
|
||||
, whenNotEmpty (Data.CustomFieldChange.toFieldValues model.customValues)
|
||||
(List.map (Q.CustomField Q.Like) >> Q.And)
|
||||
, Maybe.map (Q.DateMs Q.Gte) model.fromDate
|
||||
, Maybe.map (Q.DateMs Q.Lte) model.untilDate
|
||||
, Maybe.map (Q.DueDateMs Q.Gte) model.fromDueDate
|
||||
, Maybe.map (Q.DueDateMs Q.Lte) model.untilDueDate
|
||||
, Maybe.map (Q.Source Q.Like) model.sourceModel
|
||||
, model.nameModel
|
||||
|> Maybe.map amendWildcards
|
||||
|> Maybe.map (Q.ItemName Q.Like)
|
||||
, textSearch.nameSearch
|
||||
|> Maybe.map amendWildcards
|
||||
|> Maybe.map Q.AllNames
|
||||
, Comp.Dropdown.getSelected model.directionModel
|
||||
|> List.head
|
||||
|> Maybe.map Q.Dir
|
||||
, textSearch.fullText
|
||||
|> Maybe.map Q.Contents
|
||||
]
|
||||
|
||||
|
||||
resetModel : Model -> Model
|
||||
@ -437,7 +465,7 @@ updateDrop ddm flags settings msg model =
|
||||
{ model = mdp
|
||||
, cmd =
|
||||
Cmd.batch
|
||||
[ Api.itemSearchStats flags Api.Model.ItemSearch.empty GetAllTagsResp
|
||||
[ Api.itemSearchStats flags Api.Model.ItemQuery.empty GetAllTagsResp
|
||||
, Api.getOrgLight flags GetOrgResp
|
||||
, Api.getEquipments flags "" GetEquipResp
|
||||
, Api.getPersons flags "" GetPersonResp
|
||||
@ -450,7 +478,7 @@ updateDrop ddm flags settings msg model =
|
||||
|
||||
ResetForm ->
|
||||
{ model = resetModel model
|
||||
, cmd = Api.itemSearchStats flags Api.Model.ItemSearch.empty GetAllTagsResp
|
||||
, cmd = Api.itemSearchStats flags Api.Model.ItemQuery.empty GetAllTagsResp
|
||||
, stateChange = True
|
||||
, dragDrop = DD.DragDropData ddm Nothing
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ module Data.CustomFieldChange exposing
|
||||
|
||||
import Api.Model.CustomField exposing (CustomField)
|
||||
import Api.Model.CustomFieldValue exposing (CustomFieldValue)
|
||||
import Api.Model.ItemFieldValue exposing (ItemFieldValue)
|
||||
import Dict exposing (Dict)
|
||||
|
||||
|
||||
|
202
modules/webapp/src/main/elm/Data/ItemQuery.elm
Normal file
202
modules/webapp/src/main/elm/Data/ItemQuery.elm
Normal file
@ -0,0 +1,202 @@
|
||||
module Data.ItemQuery exposing
|
||||
( AttrMatch(..)
|
||||
, ItemQuery(..)
|
||||
, TagMatch(..)
|
||||
, and
|
||||
, render
|
||||
, renderMaybe
|
||||
, request
|
||||
)
|
||||
|
||||
{-| Models the query language for the purpose of generating a query string.
|
||||
-}
|
||||
|
||||
import Api.Model.CustomFieldValue exposing (CustomFieldValue)
|
||||
import Api.Model.ItemQuery as RQ
|
||||
import Data.Direction exposing (Direction)
|
||||
|
||||
|
||||
type TagMatch
|
||||
= AnyMatch
|
||||
| AllMatch
|
||||
|
||||
|
||||
type AttrMatch
|
||||
= Eq
|
||||
| Neq
|
||||
| Lt
|
||||
| Gt
|
||||
| Lte
|
||||
| Gte
|
||||
| Like
|
||||
|
||||
|
||||
type ItemQuery
|
||||
= Inbox Bool
|
||||
| And (List ItemQuery)
|
||||
| Or (List ItemQuery)
|
||||
| Not ItemQuery
|
||||
| TagIds TagMatch (List String)
|
||||
| CatNames TagMatch (List String)
|
||||
| FolderId AttrMatch String
|
||||
| CorrOrgId AttrMatch String
|
||||
| CorrPersId AttrMatch String
|
||||
| ConcPersId AttrMatch String
|
||||
| ConcEquipId AttrMatch String
|
||||
| CustomField AttrMatch CustomFieldValue
|
||||
| DateMs AttrMatch Int
|
||||
| DueDateMs AttrMatch Int
|
||||
| Source AttrMatch String
|
||||
| Dir Direction
|
||||
| ItemIdIn (List String)
|
||||
| ItemName AttrMatch String
|
||||
| AllNames String
|
||||
| Contents String
|
||||
|
||||
|
||||
and : List (Maybe ItemQuery) -> Maybe ItemQuery
|
||||
and list =
|
||||
case List.filterMap identity list of
|
||||
[] ->
|
||||
Nothing
|
||||
|
||||
es ->
|
||||
Just (And es)
|
||||
|
||||
|
||||
request : Maybe ItemQuery -> RQ.ItemQuery
|
||||
request mq =
|
||||
{ offset = Nothing
|
||||
, limit = Nothing
|
||||
, withDetails = Just True
|
||||
, query = renderMaybe mq
|
||||
}
|
||||
|
||||
|
||||
renderMaybe : Maybe ItemQuery -> String
|
||||
renderMaybe mq =
|
||||
Maybe.map render mq
|
||||
|> Maybe.withDefault ""
|
||||
|
||||
|
||||
render : ItemQuery -> String
|
||||
render q =
|
||||
let
|
||||
boolStr flag =
|
||||
if flag then
|
||||
"yes"
|
||||
|
||||
else
|
||||
"no"
|
||||
|
||||
between left right str =
|
||||
left ++ str ++ right
|
||||
|
||||
surround lr str =
|
||||
between lr lr str
|
||||
|
||||
tagMatchStr tm =
|
||||
case tm of
|
||||
AnyMatch ->
|
||||
":"
|
||||
|
||||
AllMatch ->
|
||||
"="
|
||||
|
||||
quoteStr =
|
||||
--TODO escape quotes
|
||||
surround "\""
|
||||
in
|
||||
case q of
|
||||
And inner ->
|
||||
List.map render inner
|
||||
|> String.join " "
|
||||
|> between "(& " " )"
|
||||
|
||||
Or inner ->
|
||||
List.map render inner
|
||||
|> String.join " "
|
||||
|> between "(| " " )"
|
||||
|
||||
Not inner ->
|
||||
"!" ++ render inner
|
||||
|
||||
Inbox flag ->
|
||||
"inbox:" ++ boolStr flag
|
||||
|
||||
TagIds m ids ->
|
||||
List.map quoteStr ids
|
||||
|> String.join ","
|
||||
|> between ("tag.id" ++ tagMatchStr m) ""
|
||||
|
||||
CatNames m ids ->
|
||||
List.map quoteStr ids
|
||||
|> String.join ","
|
||||
|> between ("cat" ++ tagMatchStr m) ""
|
||||
|
||||
FolderId m id ->
|
||||
"folder.id" ++ attrMatch m ++ quoteStr id
|
||||
|
||||
CorrOrgId m id ->
|
||||
"correspondent.org.id" ++ attrMatch m ++ quoteStr id
|
||||
|
||||
CorrPersId m id ->
|
||||
"correspondent.person.id" ++ attrMatch m ++ quoteStr id
|
||||
|
||||
ConcPersId m id ->
|
||||
"concerning.person.id" ++ attrMatch m ++ quoteStr id
|
||||
|
||||
ConcEquipId m id ->
|
||||
"concerning.equip.id" ++ attrMatch m ++ quoteStr id
|
||||
|
||||
CustomField m kv ->
|
||||
"f:" ++ kv.field ++ attrMatch m ++ quoteStr kv.value
|
||||
|
||||
DateMs m ms ->
|
||||
"date" ++ attrMatch m ++ "ms" ++ String.fromInt ms
|
||||
|
||||
DueDateMs m ms ->
|
||||
"due" ++ attrMatch m ++ "ms" ++ String.fromInt ms
|
||||
|
||||
Source m str ->
|
||||
"source" ++ attrMatch m ++ quoteStr str
|
||||
|
||||
Dir dir ->
|
||||
"incoming:" ++ boolStr (dir == Data.Direction.Incoming)
|
||||
|
||||
ItemIdIn ids ->
|
||||
"id~=" ++ String.join "," ids
|
||||
|
||||
ItemName m str ->
|
||||
"name" ++ attrMatch m ++ quoteStr str
|
||||
|
||||
AllNames str ->
|
||||
"$names:" ++ quoteStr str
|
||||
|
||||
Contents str ->
|
||||
"content:" ++ quoteStr str
|
||||
|
||||
|
||||
attrMatch : AttrMatch -> String
|
||||
attrMatch am =
|
||||
case am of
|
||||
Eq ->
|
||||
"="
|
||||
|
||||
Neq ->
|
||||
"!="
|
||||
|
||||
Like ->
|
||||
":"
|
||||
|
||||
Gt ->
|
||||
">"
|
||||
|
||||
Gte ->
|
||||
">="
|
||||
|
||||
Lt ->
|
||||
"<"
|
||||
|
||||
Lte ->
|
||||
"<="
|
@ -31,6 +31,7 @@ import Comp.SearchMenu
|
||||
import Comp.YesNoDimmer
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.ItemNav exposing (ItemNav)
|
||||
import Data.ItemQuery as Q
|
||||
import Data.Items
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Http
|
||||
@ -239,12 +240,13 @@ doSearchDefaultCmd : SearchParam -> Model -> Cmd Msg
|
||||
doSearchDefaultCmd param model =
|
||||
let
|
||||
smask =
|
||||
Comp.SearchMenu.getItemSearch model.searchMenuModel
|
||||
Q.request
|
||||
(Comp.SearchMenu.getItemQuery model.searchMenuModel)
|
||||
|
||||
mask =
|
||||
{ smask
|
||||
| limit = param.pageSize
|
||||
, offset = param.offset
|
||||
| limit = Just param.pageSize
|
||||
, offset = Just param.offset
|
||||
}
|
||||
in
|
||||
if param.offset == 0 then
|
||||
|
@ -3,7 +3,7 @@ module Page.Home.Update exposing (update)
|
||||
import Api
|
||||
import Api.Model.IdList exposing (IdList)
|
||||
import Api.Model.ItemLightList exposing (ItemLightList)
|
||||
import Api.Model.ItemSearch
|
||||
import Api.Model.ItemQuery
|
||||
import Browser.Navigation as Nav
|
||||
import Comp.FixedDropdown
|
||||
import Comp.ItemCardList
|
||||
@ -13,6 +13,7 @@ import Comp.LinkTarget exposing (LinkTarget)
|
||||
import Comp.SearchMenu
|
||||
import Comp.YesNoDimmer
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.ItemQuery as Q
|
||||
import Data.ItemSelection
|
||||
import Data.Items
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
@ -648,16 +649,15 @@ loadChangedItems flags ids =
|
||||
|
||||
else
|
||||
let
|
||||
searchInit =
|
||||
Api.Model.ItemSearch.empty
|
||||
|
||||
idList =
|
||||
IdList (Set.toList ids)
|
||||
Set.toList ids
|
||||
|
||||
searchInit =
|
||||
Q.request (Just <| Q.ItemIdIn idList)
|
||||
|
||||
search =
|
||||
{ searchInit
|
||||
| itemSubset = Just idList
|
||||
, limit = Set.size ids
|
||||
| limit = Just <| Set.size ids
|
||||
}
|
||||
in
|
||||
Api.itemSearch flags search ReplaceChangedItemsResp
|
||||
|
@ -320,8 +320,9 @@ viewSearchBar flags model =
|
||||
[ a
|
||||
[ classList
|
||||
[ ( "search-menu-toggle ui icon button", True )
|
||||
, ( "primary", not (searchMenuFilled model) )
|
||||
, ( "secondary", searchMenuFilled model )
|
||||
|
||||
-- , ( "primary", not (searchMenuFilled model) )
|
||||
-- , ( "secondary", searchMenuFilled model )
|
||||
]
|
||||
, onClick ToggleSearchMenu
|
||||
, href "#"
|
||||
@ -332,24 +333,23 @@ viewSearchBar flags model =
|
||||
, div [ class "right menu" ]
|
||||
[ div [ class "fitted item" ]
|
||||
[ div [ class "ui left icon right action input" ]
|
||||
[ i
|
||||
[ classList
|
||||
[ ( "search link icon", not model.searchInProgress )
|
||||
, ( "loading spinner icon", model.searchInProgress )
|
||||
]
|
||||
, href "#"
|
||||
, onClick (DoSearch model.searchTypeDropdownValue)
|
||||
]
|
||||
(if hasMoreSearch model then
|
||||
[ i [ class "icons search-corner-icons" ]
|
||||
[ i [ class "tiny blue circle icon" ] []
|
||||
]
|
||||
]
|
||||
|
||||
else
|
||||
[]
|
||||
)
|
||||
, input
|
||||
[ -- i
|
||||
-- [ classList
|
||||
-- [ ( "search link icon", not model.searchInProgress )
|
||||
-- , ( "loading spinner icon", model.searchInProgress )
|
||||
-- ]
|
||||
-- , href "#"
|
||||
-- , onClick (DoSearch model.searchTypeDropdownValue)
|
||||
-- ]
|
||||
-- (if hasMoreSearch model then
|
||||
-- [ i [ class "icons search-corner-icons" ]
|
||||
-- [ i [ class "tiny blue circle icon" ] []
|
||||
-- ]
|
||||
-- ]
|
||||
-- else
|
||||
-- []
|
||||
-- )
|
||||
input
|
||||
[ type_ "text"
|
||||
, placeholder
|
||||
(case model.searchTypeDropdownValue of
|
||||
@ -384,27 +384,6 @@ viewSearchBar flags model =
|
||||
]
|
||||
|
||||
|
||||
searchMenuFilled : Model -> Bool
|
||||
searchMenuFilled model =
|
||||
let
|
||||
is =
|
||||
Comp.SearchMenu.getItemSearch model.searchMenuModel
|
||||
in
|
||||
is /= Api.Model.ItemSearch.empty
|
||||
|
||||
|
||||
hasMoreSearch : Model -> Bool
|
||||
hasMoreSearch model =
|
||||
let
|
||||
is =
|
||||
Comp.SearchMenu.getItemSearch model.searchMenuModel
|
||||
|
||||
is_ =
|
||||
{ is | allNames = Nothing, fullText = Nothing }
|
||||
in
|
||||
is_ /= Api.Model.ItemSearch.empty
|
||||
|
||||
|
||||
deleteAllDimmer : Comp.YesNoDimmer.Settings
|
||||
deleteAllDimmer =
|
||||
{ message = "Really delete all selected items?"
|
||||
|
Loading…
x
Reference in New Issue
Block a user