mirror of
				https://github.com/TheAnachronism/docspell.git
				synced 2025-10-30 21:40:12 +00:00 
			
		
		
		
	Allow to select multiple items for deletion and edit
This commit is contained in:
		| @@ -1,6 +1,7 @@ | ||||
| module Comp.ItemCardList exposing | ||||
|     ( Model | ||||
|     , Msg(..) | ||||
|     , ViewConfig | ||||
|     , init | ||||
|     , nextItem | ||||
|     , prevItem | ||||
| @@ -17,12 +18,16 @@ import Data.Direction | ||||
| import Data.Fields | ||||
| import Data.Flags exposing (Flags) | ||||
| import Data.Icons as Icons | ||||
| import Data.ItemSelection exposing (ItemSelection) | ||||
| import Data.Items | ||||
| import Data.UiSettings exposing (UiSettings) | ||||
| import Html exposing (..) | ||||
| import Html.Attributes exposing (..) | ||||
| import Html.Events exposing (onClick) | ||||
| import Markdown | ||||
| import Page exposing (Page(..)) | ||||
| import Set exposing (Set) | ||||
| import Util.Html | ||||
| import Util.ItemDragDrop as DD | ||||
| import Util.List | ||||
| import Util.String | ||||
| @@ -38,6 +43,7 @@ type Msg | ||||
|     = SetResults ItemLightList | ||||
|     | AddResults ItemLightList | ||||
|     | ItemDDMsg DD.Msg | ||||
|     | ToggleSelectItem (Set String) String | ||||
|  | ||||
|  | ||||
| init : Model | ||||
| @@ -75,6 +81,7 @@ type alias UpdateResult = | ||||
|     { model : Model | ||||
|     , cmd : Cmd Msg | ||||
|     , dragModel : DD.Model | ||||
|     , selection : ItemSelection | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -91,51 +98,78 @@ updateDrag dm _ msg model = | ||||
|                 newModel = | ||||
|                     { model | results = list } | ||||
|             in | ||||
|             UpdateResult newModel Cmd.none dm | ||||
|             UpdateResult newModel Cmd.none dm Data.ItemSelection.Inactive | ||||
|  | ||||
|         AddResults list -> | ||||
|             if list.groups == [] then | ||||
|                 UpdateResult model Cmd.none dm | ||||
|                 UpdateResult model Cmd.none dm Data.ItemSelection.Inactive | ||||
|  | ||||
|             else | ||||
|                 let | ||||
|                     newModel = | ||||
|                         { model | results = Data.Items.concat model.results list } | ||||
|                 in | ||||
|                 UpdateResult newModel Cmd.none dm | ||||
|                 UpdateResult newModel Cmd.none dm Data.ItemSelection.Inactive | ||||
|  | ||||
|         ItemDDMsg lm -> | ||||
|             let | ||||
|                 ddd = | ||||
|                     DD.update lm dm | ||||
|             in | ||||
|             UpdateResult model Cmd.none ddd.model | ||||
|             UpdateResult model Cmd.none ddd.model Data.ItemSelection.Inactive | ||||
|  | ||||
|         ToggleSelectItem ids id -> | ||||
|             let | ||||
|                 newSet = | ||||
|                     if Set.member id ids then | ||||
|                         Set.remove id ids | ||||
|  | ||||
|                     else | ||||
|                         Set.insert id ids | ||||
|             in | ||||
|             UpdateResult model Cmd.none dm (Data.ItemSelection.Active newSet) | ||||
|  | ||||
|  | ||||
|  | ||||
| --- View | ||||
|  | ||||
|  | ||||
| view : Maybe String -> UiSettings -> Model -> Html Msg | ||||
| view current settings model = | ||||
| type alias ViewConfig = | ||||
|     { current : Maybe String | ||||
|     , selection : ItemSelection | ||||
|     } | ||||
|  | ||||
|  | ||||
| isSelected : ViewConfig -> String -> Bool | ||||
| isSelected cfg id = | ||||
|     case cfg.selection of | ||||
|         Data.ItemSelection.Active ids -> | ||||
|             Set.member id ids | ||||
|  | ||||
|         Data.ItemSelection.Inactive -> | ||||
|             False | ||||
|  | ||||
|  | ||||
| view : ViewConfig -> UiSettings -> Model -> Html Msg | ||||
| view cfg settings model = | ||||
|     div [ class "ui container" ] | ||||
|         (List.map (viewGroup current settings) model.results.groups) | ||||
|         (List.map (viewGroup cfg settings) model.results.groups) | ||||
|  | ||||
|  | ||||
| viewGroup : Maybe String -> UiSettings -> ItemLightGroup -> Html Msg | ||||
| viewGroup current settings group = | ||||
| viewGroup : ViewConfig -> UiSettings -> ItemLightGroup -> Html Msg | ||||
| viewGroup cfg settings group = | ||||
|     div [ class "item-group" ] | ||||
|         [ div [ class "ui horizontal divider header item-list" ] | ||||
|             [ i [ class "calendar alternate outline icon" ] [] | ||||
|             , text group.name | ||||
|             ] | ||||
|         , div [ class "ui stackable three cards" ] | ||||
|             (List.map (viewItem current settings) group.items) | ||||
|             (List.map (viewItem cfg settings) group.items) | ||||
|         ] | ||||
|  | ||||
|  | ||||
| viewItem : Maybe String -> UiSettings -> ItemLight -> Html Msg | ||||
| viewItem current settings item = | ||||
| viewItem : ViewConfig -> UiSettings -> ItemLight -> Html Msg | ||||
| viewItem cfg settings item = | ||||
|     let | ||||
|         dirIcon = | ||||
|             i [ class (Data.Direction.iconFromMaybe item.direction) ] [] | ||||
| @@ -163,43 +197,68 @@ viewItem current settings item = | ||||
|         isConfirmed = | ||||
|             item.state /= "created" | ||||
|  | ||||
|         newColor = | ||||
|             "blue" | ||||
|         cardColor = | ||||
|             if isSelected cfg item.id then | ||||
|                 "purple" | ||||
|  | ||||
|             else if not isConfirmed then | ||||
|                 "blue" | ||||
|  | ||||
|             else | ||||
|                 "" | ||||
|  | ||||
|         fieldHidden f = | ||||
|             Data.UiSettings.fieldHidden settings f | ||||
|  | ||||
|         cardAction = | ||||
|             case cfg.selection of | ||||
|                 Data.ItemSelection.Inactive -> | ||||
|                     Page.href (ItemDetailPage item.id) | ||||
|  | ||||
|                 Data.ItemSelection.Active ids -> | ||||
|                     onClick (ToggleSelectItem ids item.id) | ||||
|     in | ||||
|     a | ||||
|         ([ classList | ||||
|             [ ( "ui fluid card", True ) | ||||
|             , ( newColor, not isConfirmed ) | ||||
|             , ( "current", current == Just item.id ) | ||||
|             , ( cardColor, True ) | ||||
|             , ( "current", cfg.current == Just item.id ) | ||||
|             ] | ||||
|          , id item.id | ||||
|          , Page.href (ItemDetailPage item.id) | ||||
|          , href "#" | ||||
|          , cardAction | ||||
|          ] | ||||
|             ++ DD.draggable ItemDDMsg item.id | ||||
|         ) | ||||
|         [ div [ class "content" ] | ||||
|             [ if fieldHidden Data.Fields.Direction then | ||||
|                 div [ class "header" ] | ||||
|                     [ Util.String.underscoreToSpace item.name |> text | ||||
|                     ] | ||||
|             [ case cfg.selection of | ||||
|                 Data.ItemSelection.Active ids -> | ||||
|                     div [ class "header" ] | ||||
|                         [ Util.Html.checkbox (Set.member item.id ids) | ||||
|                         , Util.String.underscoreToSpace item.name | ||||
|                             |> text | ||||
|                         ] | ||||
|  | ||||
|               else | ||||
|                 div | ||||
|                     [ class "header" | ||||
|                     , Data.Direction.labelFromMaybe item.direction | ||||
|                         |> title | ||||
|                     ] | ||||
|                     [ dirIcon | ||||
|                     , Util.String.underscoreToSpace item.name | ||||
|                         |> text | ||||
|                     ] | ||||
|                 Data.ItemSelection.Inactive -> | ||||
|                     if fieldHidden Data.Fields.Direction then | ||||
|                         div [ class "header" ] | ||||
|                             [ Util.String.underscoreToSpace item.name |> text | ||||
|                             ] | ||||
|  | ||||
|                     else | ||||
|                         div | ||||
|                             [ class "header" | ||||
|                             , Data.Direction.labelFromMaybe item.direction | ||||
|                                 |> title | ||||
|                             ] | ||||
|                             [ dirIcon | ||||
|                             , Util.String.underscoreToSpace item.name | ||||
|                                 |> text | ||||
|                             ] | ||||
|             , div | ||||
|                 [ classList | ||||
|                     [ ( "ui right corner label", True ) | ||||
|                     , ( newColor, True ) | ||||
|                     , ( cardColor, True ) | ||||
|                     , ( "invisible", isConfirmed ) | ||||
|                     ] | ||||
|                 , title "New" | ||||
|   | ||||
							
								
								
									
										693
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/EditMenu.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										693
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/EditMenu.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,693 @@ | ||||
| module Comp.ItemDetail.EditMenu exposing | ||||
|     ( Model | ||||
|     , Msg | ||||
|     , SaveNameState(..) | ||||
|     , defaultViewConfig | ||||
|     , init | ||||
|     , loadModel | ||||
|     , update | ||||
|     , view | ||||
|     ) | ||||
|  | ||||
| import Api | ||||
| import Api.Model.EquipmentList exposing (EquipmentList) | ||||
| import Api.Model.FolderItem exposing (FolderItem) | ||||
| import Api.Model.FolderList exposing (FolderList) | ||||
| import Api.Model.IdName exposing (IdName) | ||||
| import Api.Model.ItemProposals exposing (ItemProposals) | ||||
| import Api.Model.ReferenceList exposing (ReferenceList) | ||||
| import Api.Model.Tag exposing (Tag) | ||||
| import Api.Model.TagList exposing (TagList) | ||||
| import Comp.DatePicker | ||||
| import Comp.DetailEdit | ||||
| import Comp.Dropdown exposing (isDropdownChangeMsg) | ||||
| import Comp.ItemDetail.FormChange exposing (FormChange(..)) | ||||
| import Data.Direction exposing (Direction) | ||||
| import Data.Fields | ||||
| import Data.Flags exposing (Flags) | ||||
| import Data.Icons as Icons | ||||
| import Data.UiSettings exposing (UiSettings) | ||||
| import DatePicker exposing (DatePicker) | ||||
| import Html exposing (..) | ||||
| import Html.Attributes exposing (..) | ||||
| import Html.Events exposing (onClick, onInput) | ||||
| import Http | ||||
| import Markdown | ||||
| import Page exposing (Page(..)) | ||||
| import Task | ||||
| import Throttle exposing (Throttle) | ||||
| import Time | ||||
| import Util.Folder exposing (mkFolderOption) | ||||
| import Util.List | ||||
| import Util.Maybe | ||||
| import Util.Tag | ||||
|  | ||||
|  | ||||
|  | ||||
| --- Model | ||||
|  | ||||
|  | ||||
| type SaveNameState | ||||
|     = Saving | ||||
|     | SaveSuccess | ||||
|     | SaveFailed | ||||
|  | ||||
|  | ||||
| type alias Model = | ||||
|     { tagModel : Comp.Dropdown.Model Tag | ||||
|     , nameModel : String | ||||
|     , nameSaveThrottle : Throttle Msg | ||||
|     , folderModel : Comp.Dropdown.Model IdName | ||||
|     , allFolders : List FolderItem | ||||
|     , directionModel : Comp.Dropdown.Model Direction | ||||
|     , itemDatePicker : DatePicker | ||||
|     , itemDate : Maybe Int | ||||
|     , itemProposals : ItemProposals | ||||
|     , dueDate : Maybe Int | ||||
|     , dueDatePicker : DatePicker | ||||
|     , corrOrgModel : Comp.Dropdown.Model IdName | ||||
|     , corrPersonModel : Comp.Dropdown.Model IdName | ||||
|     , concPersonModel : Comp.Dropdown.Model IdName | ||||
|     , concEquipModel : Comp.Dropdown.Model IdName | ||||
|     , modalEdit : Maybe Comp.DetailEdit.Model | ||||
|     } | ||||
|  | ||||
|  | ||||
| type Msg | ||||
|     = ItemDatePickerMsg Comp.DatePicker.Msg | ||||
|     | DueDatePickerMsg Comp.DatePicker.Msg | ||||
|     | SetName String | ||||
|     | SaveName | ||||
|     | UpdateThrottle | ||||
|     | RemoveDueDate | ||||
|     | RemoveDate | ||||
|     | FolderDropdownMsg (Comp.Dropdown.Msg IdName) | ||||
|     | TagDropdownMsg (Comp.Dropdown.Msg Tag) | ||||
|     | DirDropdownMsg (Comp.Dropdown.Msg Direction) | ||||
|     | OrgDropdownMsg (Comp.Dropdown.Msg IdName) | ||||
|     | CorrPersonMsg (Comp.Dropdown.Msg IdName) | ||||
|     | ConcPersonMsg (Comp.Dropdown.Msg IdName) | ||||
|     | ConcEquipMsg (Comp.Dropdown.Msg IdName) | ||||
|     | GetTagsResp (Result Http.Error TagList) | ||||
|     | GetOrgResp (Result Http.Error ReferenceList) | ||||
|     | GetPersonResp (Result Http.Error ReferenceList) | ||||
|     | GetEquipResp (Result Http.Error EquipmentList) | ||||
|     | GetFolderResp (Result Http.Error FolderList) | ||||
|  | ||||
|  | ||||
| init : Model | ||||
| init = | ||||
|     { tagModel = | ||||
|         Util.Tag.makeDropdownModel | ||||
|     , directionModel = | ||||
|         Comp.Dropdown.makeSingleList | ||||
|             { makeOption = | ||||
|                 \entry -> | ||||
|                     { value = Data.Direction.toString entry | ||||
|                     , text = Data.Direction.toString entry | ||||
|                     , additional = "" | ||||
|                     } | ||||
|             , options = Data.Direction.all | ||||
|             , placeholder = "Choose a direction…" | ||||
|             , selected = Nothing | ||||
|             } | ||||
|     , corrOrgModel = | ||||
|         Comp.Dropdown.makeSingle | ||||
|             { makeOption = \e -> { value = e.id, text = e.name, additional = "" } | ||||
|             , placeholder = "" | ||||
|             } | ||||
|     , corrPersonModel = | ||||
|         Comp.Dropdown.makeSingle | ||||
|             { makeOption = \e -> { value = e.id, text = e.name, additional = "" } | ||||
|             , placeholder = "" | ||||
|             } | ||||
|     , concPersonModel = | ||||
|         Comp.Dropdown.makeSingle | ||||
|             { makeOption = \e -> { value = e.id, text = e.name, additional = "" } | ||||
|             , placeholder = "" | ||||
|             } | ||||
|     , concEquipModel = | ||||
|         Comp.Dropdown.makeSingle | ||||
|             { makeOption = \e -> { value = e.id, text = e.name, additional = "" } | ||||
|             , placeholder = "" | ||||
|             } | ||||
|     , folderModel = | ||||
|         Comp.Dropdown.makeSingle | ||||
|             { makeOption = \e -> { value = e.id, text = e.name, additional = "" } | ||||
|             , placeholder = "" | ||||
|             } | ||||
|     , allFolders = [] | ||||
|     , nameModel = "" | ||||
|     , nameSaveThrottle = Throttle.create 1 | ||||
|     , itemDatePicker = Comp.DatePicker.emptyModel | ||||
|     , itemDate = Nothing | ||||
|     , itemProposals = Api.Model.ItemProposals.empty | ||||
|     , dueDate = Nothing | ||||
|     , dueDatePicker = Comp.DatePicker.emptyModel | ||||
|     , modalEdit = Nothing | ||||
|     } | ||||
|  | ||||
|  | ||||
| loadModel : Flags -> Cmd Msg | ||||
| loadModel flags = | ||||
|     let | ||||
|         ( _, dpc ) = | ||||
|             Comp.DatePicker.init | ||||
|     in | ||||
|     Cmd.batch | ||||
|         [ Api.getTags flags "" GetTagsResp | ||||
|         , Api.getOrgLight flags GetOrgResp | ||||
|         , Api.getPersonsLight flags GetPersonResp | ||||
|         , Api.getEquipments flags "" GetEquipResp | ||||
|         , Api.getFolders flags "" False GetFolderResp | ||||
|         , Cmd.map ItemDatePickerMsg dpc | ||||
|         , Cmd.map DueDatePickerMsg dpc | ||||
|         ] | ||||
|  | ||||
|  | ||||
| isFolderMember : Model -> Bool | ||||
| isFolderMember model = | ||||
|     let | ||||
|         selected = | ||||
|             Comp.Dropdown.getSelected model.folderModel | ||||
|                 |> List.head | ||||
|                 |> Maybe.map .id | ||||
|     in | ||||
|     Util.Folder.isFolderMember model.allFolders selected | ||||
|  | ||||
|  | ||||
|  | ||||
| --- Update | ||||
|  | ||||
|  | ||||
| type alias UpdateResult = | ||||
|     { model : Model | ||||
|     , cmd : Cmd Msg | ||||
|     , sub : Sub Msg | ||||
|     , change : FormChange | ||||
|     } | ||||
|  | ||||
|  | ||||
| resultNoCmd : FormChange -> Model -> UpdateResult | ||||
| resultNoCmd change model = | ||||
|     UpdateResult model Cmd.none Sub.none change | ||||
|  | ||||
|  | ||||
| resultNone : Model -> UpdateResult | ||||
| resultNone model = | ||||
|     resultNoCmd NoFormChange model | ||||
|  | ||||
|  | ||||
| update : Flags -> Msg -> Model -> UpdateResult | ||||
| update flags msg model = | ||||
|     case msg of | ||||
|         TagDropdownMsg m -> | ||||
|             let | ||||
|                 ( m2, _ ) = | ||||
|                     Comp.Dropdown.update m model.tagModel | ||||
|  | ||||
|                 newModel = | ||||
|                     { model | tagModel = m2 } | ||||
|  | ||||
|                 change = | ||||
|                     if isDropdownChangeMsg m then | ||||
|                         Comp.Dropdown.getSelected newModel.tagModel | ||||
|                             |> Util.List.distinct | ||||
|                             |> List.map (\t -> IdName t.id t.name) | ||||
|                             |> ReferenceList | ||||
|                             |> TagChange | ||||
|  | ||||
|                     else | ||||
|                         NoFormChange | ||||
|             in | ||||
|             resultNoCmd change newModel | ||||
|  | ||||
|         GetTagsResp (Ok tags) -> | ||||
|             let | ||||
|                 tagList = | ||||
|                     Comp.Dropdown.SetOptions tags.items | ||||
|             in | ||||
|             update flags (TagDropdownMsg tagList) model | ||||
|  | ||||
|         GetTagsResp (Err _) -> | ||||
|             resultNone model | ||||
|  | ||||
|         FolderDropdownMsg m -> | ||||
|             let | ||||
|                 ( m2, _ ) = | ||||
|                     Comp.Dropdown.update m model.folderModel | ||||
|  | ||||
|                 newModel = | ||||
|                     { model | folderModel = m2 } | ||||
|  | ||||
|                 idref = | ||||
|                     Comp.Dropdown.getSelected m2 |> List.head | ||||
|  | ||||
|                 change = | ||||
|                     if isDropdownChangeMsg m then | ||||
|                         FolderChange idref | ||||
|  | ||||
|                     else | ||||
|                         NoFormChange | ||||
|             in | ||||
|             resultNoCmd change newModel | ||||
|  | ||||
|         GetFolderResp (Ok fs) -> | ||||
|             let | ||||
|                 model_ = | ||||
|                     { model | ||||
|                         | allFolders = fs.items | ||||
|                         , folderModel = | ||||
|                             Comp.Dropdown.setMkOption | ||||
|                                 (mkFolderOption flags fs.items) | ||||
|                                 model.folderModel | ||||
|                     } | ||||
|  | ||||
|                 mkIdName fitem = | ||||
|                     IdName fitem.id fitem.name | ||||
|  | ||||
|                 opts = | ||||
|                     fs.items | ||||
|                         |> List.map mkIdName | ||||
|                         |> Comp.Dropdown.SetOptions | ||||
|             in | ||||
|             update flags (FolderDropdownMsg opts) model_ | ||||
|  | ||||
|         GetFolderResp (Err _) -> | ||||
|             resultNone model | ||||
|  | ||||
|         DirDropdownMsg m -> | ||||
|             let | ||||
|                 ( m2, _ ) = | ||||
|                     Comp.Dropdown.update m model.directionModel | ||||
|  | ||||
|                 newModel = | ||||
|                     { model | directionModel = m2 } | ||||
|  | ||||
|                 change = | ||||
|                     if isDropdownChangeMsg m then | ||||
|                         let | ||||
|                             dir = | ||||
|                                 Comp.Dropdown.getSelected m2 |> List.head | ||||
|                         in | ||||
|                         case dir of | ||||
|                             Just d -> | ||||
|                                 DirectionChange d | ||||
|  | ||||
|                             Nothing -> | ||||
|                                 NoFormChange | ||||
|  | ||||
|                     else | ||||
|                         NoFormChange | ||||
|             in | ||||
|             resultNoCmd change newModel | ||||
|  | ||||
|         OrgDropdownMsg m -> | ||||
|             let | ||||
|                 ( m2, _ ) = | ||||
|                     Comp.Dropdown.update m model.corrOrgModel | ||||
|  | ||||
|                 newModel = | ||||
|                     { model | corrOrgModel = m2 } | ||||
|  | ||||
|                 idref = | ||||
|                     Comp.Dropdown.getSelected m2 |> List.head | ||||
|  | ||||
|                 change = | ||||
|                     if isDropdownChangeMsg m then | ||||
|                         OrgChange idref | ||||
|  | ||||
|                     else | ||||
|                         NoFormChange | ||||
|             in | ||||
|             resultNoCmd change newModel | ||||
|  | ||||
|         GetOrgResp (Ok orgs) -> | ||||
|             let | ||||
|                 opts = | ||||
|                     Comp.Dropdown.SetOptions orgs.items | ||||
|             in | ||||
|             update flags (OrgDropdownMsg opts) model | ||||
|  | ||||
|         GetOrgResp (Err _) -> | ||||
|             resultNone model | ||||
|  | ||||
|         CorrPersonMsg m -> | ||||
|             let | ||||
|                 ( m2, _ ) = | ||||
|                     Comp.Dropdown.update m model.corrPersonModel | ||||
|  | ||||
|                 newModel = | ||||
|                     { model | corrPersonModel = m2 } | ||||
|  | ||||
|                 idref = | ||||
|                     Comp.Dropdown.getSelected m2 |> List.head | ||||
|  | ||||
|                 change = | ||||
|                     if isDropdownChangeMsg m then | ||||
|                         CorrPersonChange idref | ||||
|  | ||||
|                     else | ||||
|                         NoFormChange | ||||
|             in | ||||
|             resultNoCmd change newModel | ||||
|  | ||||
|         ConcPersonMsg m -> | ||||
|             let | ||||
|                 ( m2, _ ) = | ||||
|                     Comp.Dropdown.update m model.concPersonModel | ||||
|  | ||||
|                 newModel = | ||||
|                     { model | concPersonModel = m2 } | ||||
|  | ||||
|                 idref = | ||||
|                     Comp.Dropdown.getSelected m2 |> List.head | ||||
|  | ||||
|                 change = | ||||
|                     if isDropdownChangeMsg m then | ||||
|                         ConcPersonChange idref | ||||
|  | ||||
|                     else | ||||
|                         NoFormChange | ||||
|             in | ||||
|             resultNoCmd change newModel | ||||
|  | ||||
|         GetPersonResp (Ok ps) -> | ||||
|             let | ||||
|                 opts = | ||||
|                     Comp.Dropdown.SetOptions ps.items | ||||
|  | ||||
|                 res1 = | ||||
|                     update flags (CorrPersonMsg opts) model | ||||
|  | ||||
|                 res2 = | ||||
|                     update flags (ConcPersonMsg opts) res1.model | ||||
|             in | ||||
|             res2 | ||||
|  | ||||
|         GetPersonResp (Err _) -> | ||||
|             resultNone model | ||||
|  | ||||
|         ConcEquipMsg m -> | ||||
|             let | ||||
|                 ( m2, _ ) = | ||||
|                     Comp.Dropdown.update m model.concEquipModel | ||||
|  | ||||
|                 newModel = | ||||
|                     { model | concEquipModel = m2 } | ||||
|  | ||||
|                 idref = | ||||
|                     Comp.Dropdown.getSelected m2 |> List.head | ||||
|  | ||||
|                 change = | ||||
|                     if isDropdownChangeMsg m then | ||||
|                         EquipChange idref | ||||
|  | ||||
|                     else | ||||
|                         NoFormChange | ||||
|             in | ||||
|             resultNoCmd change newModel | ||||
|  | ||||
|         GetEquipResp (Ok equips) -> | ||||
|             let | ||||
|                 opts = | ||||
|                     Comp.Dropdown.SetOptions | ||||
|                         (List.map (\e -> IdName e.id e.name) | ||||
|                             equips.items | ||||
|                         ) | ||||
|             in | ||||
|             update flags (ConcEquipMsg opts) model | ||||
|  | ||||
|         GetEquipResp (Err _) -> | ||||
|             resultNone model | ||||
|  | ||||
|         ItemDatePickerMsg m -> | ||||
|             let | ||||
|                 ( dp, event ) = | ||||
|                     Comp.DatePicker.updateDefault m model.itemDatePicker | ||||
|             in | ||||
|             case event of | ||||
|                 DatePicker.Picked date -> | ||||
|                     let | ||||
|                         newModel = | ||||
|                             { model | itemDatePicker = dp, itemDate = Just (Comp.DatePicker.midOfDay date) } | ||||
|                     in | ||||
|                     resultNoCmd (ItemDateChange newModel.itemDate) newModel | ||||
|  | ||||
|                 _ -> | ||||
|                     resultNone { model | itemDatePicker = dp } | ||||
|  | ||||
|         RemoveDate -> | ||||
|             resultNoCmd (ItemDateChange Nothing) { model | itemDate = Nothing } | ||||
|  | ||||
|         DueDatePickerMsg m -> | ||||
|             let | ||||
|                 ( dp, event ) = | ||||
|                     Comp.DatePicker.updateDefault m model.dueDatePicker | ||||
|             in | ||||
|             case event of | ||||
|                 DatePicker.Picked date -> | ||||
|                     let | ||||
|                         newModel = | ||||
|                             { model | dueDatePicker = dp, dueDate = Just (Comp.DatePicker.midOfDay date) } | ||||
|                     in | ||||
|                     resultNoCmd (DueDateChange newModel.dueDate) newModel | ||||
|  | ||||
|                 _ -> | ||||
|                     resultNone { model | dueDatePicker = dp } | ||||
|  | ||||
|         RemoveDueDate -> | ||||
|             resultNoCmd (DueDateChange Nothing) { model | dueDate = Nothing } | ||||
|  | ||||
|         SetName str -> | ||||
|             case Util.Maybe.fromString str of | ||||
|                 Just newName -> | ||||
|                     let | ||||
|                         cmd_ = | ||||
|                             Task.succeed () | ||||
|                                 |> Task.perform (\_ -> SaveName) | ||||
|  | ||||
|                         ( newThrottle, cmd ) = | ||||
|                             Throttle.try cmd_ model.nameSaveThrottle | ||||
|  | ||||
|                         newModel = | ||||
|                             { model | ||||
|                                 | nameSaveThrottle = newThrottle | ||||
|                                 , nameModel = newName | ||||
|                             } | ||||
|  | ||||
|                         sub = | ||||
|                             nameThrottleSub newModel | ||||
|                     in | ||||
|                     UpdateResult newModel cmd sub NoFormChange | ||||
|  | ||||
|                 Nothing -> | ||||
|                     resultNone { model | nameModel = str } | ||||
|  | ||||
|         SaveName -> | ||||
|             case Util.Maybe.fromString model.nameModel of | ||||
|                 Just n -> | ||||
|                     resultNoCmd (NameChange n) model | ||||
|  | ||||
|                 Nothing -> | ||||
|                     resultNone model | ||||
|  | ||||
|         UpdateThrottle -> | ||||
|             let | ||||
|                 ( newThrottle, cmd ) = | ||||
|                     Throttle.update model.nameSaveThrottle | ||||
|  | ||||
|                 newModel = | ||||
|                     { model | nameSaveThrottle = newThrottle } | ||||
|  | ||||
|                 sub = | ||||
|                     nameThrottleSub newModel | ||||
|             in | ||||
|             UpdateResult newModel cmd sub NoFormChange | ||||
|  | ||||
|  | ||||
| nameThrottleSub : Model -> Sub Msg | ||||
| nameThrottleSub model = | ||||
|     Throttle.ifNeeded | ||||
|         (Time.every 400 (\_ -> UpdateThrottle)) | ||||
|         model.nameSaveThrottle | ||||
|  | ||||
|  | ||||
|  | ||||
| --- View | ||||
|  | ||||
|  | ||||
| type alias ViewConfig = | ||||
|     { menuClass : String | ||||
|     , nameState : SaveNameState | ||||
|     } | ||||
|  | ||||
|  | ||||
| defaultViewConfig : ViewConfig | ||||
| defaultViewConfig = | ||||
|     { menuClass = "ui vertical segment" | ||||
|     , nameState = SaveSuccess | ||||
|     } | ||||
|  | ||||
|  | ||||
| view : ViewConfig -> UiSettings -> Model -> Html Msg | ||||
| view = | ||||
|     renderEditForm | ||||
|  | ||||
|  | ||||
| renderEditForm : ViewConfig -> UiSettings -> Model -> Html Msg | ||||
| renderEditForm cfg settings model = | ||||
|     let | ||||
|         fieldVisible field = | ||||
|             Data.UiSettings.fieldVisible settings field | ||||
|  | ||||
|         optional fields html = | ||||
|             if | ||||
|                 List.map fieldVisible fields | ||||
|                     |> List.foldl (||) False | ||||
|             then | ||||
|                 html | ||||
|  | ||||
|             else | ||||
|                 span [ class "invisible hidden" ] [] | ||||
|     in | ||||
|     div [ class cfg.menuClass ] | ||||
|         [ div [ class "ui form warning" ] | ||||
|             [ optional [ Data.Fields.Tag ] <| | ||||
|                 div [ class "field" ] | ||||
|                     [ label [] | ||||
|                         [ Icons.tagsIcon "grey" | ||||
|                         , text "Tags" | ||||
|                         ] | ||||
|                     , Html.map TagDropdownMsg (Comp.Dropdown.view settings model.tagModel) | ||||
|                     ] | ||||
|             , div [ class " field" ] | ||||
|                 [ label [] [ text "Name" ] | ||||
|                 , div [ class "ui icon input" ] | ||||
|                     [ input [ type_ "text", value model.nameModel, onInput SetName ] [] | ||||
|                     , i | ||||
|                         [ classList | ||||
|                             [ ( "green check icon", cfg.nameState == SaveSuccess ) | ||||
|                             , ( "red exclamation triangle icon", cfg.nameState == SaveFailed ) | ||||
|                             , ( "sync loading icon", cfg.nameState == Saving ) | ||||
|                             ] | ||||
|                         ] | ||||
|                         [] | ||||
|                     ] | ||||
|                 ] | ||||
|             , optional [ Data.Fields.Folder ] <| | ||||
|                 div [ class "field" ] | ||||
|                     [ label [] | ||||
|                         [ Icons.folderIcon "grey" | ||||
|                         , text "Folder" | ||||
|                         ] | ||||
|                     , Html.map FolderDropdownMsg (Comp.Dropdown.view settings model.folderModel) | ||||
|                     , div | ||||
|                         [ classList | ||||
|                             [ ( "ui warning message", True ) | ||||
|                             , ( "hidden", isFolderMember model ) | ||||
|                             ] | ||||
|                         ] | ||||
|                         [ Markdown.toHtml [] """ | ||||
| You are **not a member** of this folder. This item will be **hidden** | ||||
| from any search now. Use a folder where you are a member of to make this | ||||
| item visible. This message will disappear then. | ||||
|                       """ | ||||
|                         ] | ||||
|                     ] | ||||
|             , optional [ Data.Fields.Direction ] <| | ||||
|                 div [ class "field" ] | ||||
|                     [ label [] | ||||
|                         [ Icons.directionIcon "grey" | ||||
|                         , text "Direction" | ||||
|                         ] | ||||
|                     , Html.map DirDropdownMsg (Comp.Dropdown.view settings model.directionModel) | ||||
|                     ] | ||||
|             , optional [ Data.Fields.Date ] <| | ||||
|                 div [ class "field" ] | ||||
|                     [ label [] | ||||
|                         [ Icons.dateIcon "grey" | ||||
|                         , text "Date" | ||||
|                         ] | ||||
|                     , div [ class "ui action input" ] | ||||
|                         [ Html.map ItemDatePickerMsg | ||||
|                             (Comp.DatePicker.viewTime | ||||
|                                 model.itemDate | ||||
|                                 actionInputDatePicker | ||||
|                                 model.itemDatePicker | ||||
|                             ) | ||||
|                         , a [ class "ui icon button", href "", onClick RemoveDate ] | ||||
|                             [ i [ class "trash alternate outline icon" ] [] | ||||
|                             ] | ||||
|                         ] | ||||
|                     ] | ||||
|             , optional [ Data.Fields.DueDate ] <| | ||||
|                 div [ class " field" ] | ||||
|                     [ label [] | ||||
|                         [ Icons.dueDateIcon "grey" | ||||
|                         , text "Due Date" | ||||
|                         ] | ||||
|                     , div [ class "ui action input" ] | ||||
|                         [ Html.map DueDatePickerMsg | ||||
|                             (Comp.DatePicker.viewTime | ||||
|                                 model.dueDate | ||||
|                                 actionInputDatePicker | ||||
|                                 model.dueDatePicker | ||||
|                             ) | ||||
|                         , a [ class "ui icon button", href "", onClick RemoveDueDate ] | ||||
|                             [ i [ class "trash alternate outline icon" ] [] ] | ||||
|                         ] | ||||
|                     ] | ||||
|             , optional [ Data.Fields.CorrOrg, Data.Fields.CorrPerson ] <| | ||||
|                 h4 [ class "ui dividing header" ] | ||||
|                     [ Icons.correspondentIcon "" | ||||
|                     , text "Correspondent" | ||||
|                     ] | ||||
|             , optional [ Data.Fields.CorrOrg ] <| | ||||
|                 div [ class "field" ] | ||||
|                     [ label [] | ||||
|                         [ Icons.organizationIcon "grey" | ||||
|                         , text "Organization" | ||||
|                         ] | ||||
|                     , Html.map OrgDropdownMsg (Comp.Dropdown.view settings model.corrOrgModel) | ||||
|                     ] | ||||
|             , optional [ Data.Fields.CorrPerson ] <| | ||||
|                 div [ class "field" ] | ||||
|                     [ label [] | ||||
|                         [ Icons.personIcon "grey" | ||||
|                         , text "Person" | ||||
|                         ] | ||||
|                     , Html.map CorrPersonMsg (Comp.Dropdown.view settings model.corrPersonModel) | ||||
|                     ] | ||||
|             , optional [ Data.Fields.ConcPerson, Data.Fields.ConcEquip ] <| | ||||
|                 h4 [ class "ui dividing header" ] | ||||
|                     [ Icons.concernedIcon | ||||
|                     , text "Concerning" | ||||
|                     ] | ||||
|             , optional [ Data.Fields.ConcPerson ] <| | ||||
|                 div [ class "field" ] | ||||
|                     [ label [] | ||||
|                         [ Icons.personIcon "grey" | ||||
|                         , text "Person" | ||||
|                         ] | ||||
|                     , Html.map ConcPersonMsg (Comp.Dropdown.view settings model.concPersonModel) | ||||
|                     ] | ||||
|             , optional [ Data.Fields.ConcEquip ] <| | ||||
|                 div [ class "field" ] | ||||
|                     [ label [] | ||||
|                         [ Icons.equipmentIcon "grey" | ||||
|                         , text "Equipment" | ||||
|                         ] | ||||
|                     , Html.map ConcEquipMsg (Comp.Dropdown.view settings model.concEquipModel) | ||||
|                     ] | ||||
|             ] | ||||
|         ] | ||||
|  | ||||
|  | ||||
| actionInputDatePicker : DatePicker.Settings | ||||
| actionInputDatePicker = | ||||
|     let | ||||
|         ds = | ||||
|             Comp.DatePicker.defaultSettings | ||||
|     in | ||||
|     { ds | containerClassList = [ ( "ui action input", True ) ] } | ||||
							
								
								
									
										44
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/FormChange.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/FormChange.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| module Comp.ItemDetail.FormChange exposing | ||||
|     ( FormChange(..) | ||||
|     , multiUpdate | ||||
|     ) | ||||
|  | ||||
| import Api | ||||
| import Api.Model.BasicResult exposing (BasicResult) | ||||
| import Api.Model.IdName exposing (IdName) | ||||
| import Api.Model.ItemsAndRefs exposing (ItemsAndRefs) | ||||
| import Api.Model.ReferenceList exposing (ReferenceList) | ||||
| import Data.Direction exposing (Direction) | ||||
| import Data.Flags exposing (Flags) | ||||
| import Http | ||||
| import Set exposing (Set) | ||||
|  | ||||
|  | ||||
| type FormChange | ||||
|     = NoFormChange | ||||
|     | TagChange ReferenceList | ||||
|     | FolderChange (Maybe IdName) | ||||
|     | DirectionChange Direction | ||||
|     | OrgChange (Maybe IdName) | ||||
|     | CorrPersonChange (Maybe IdName) | ||||
|     | ConcPersonChange (Maybe IdName) | ||||
|     | EquipChange (Maybe IdName) | ||||
|     | ItemDateChange (Maybe Int) | ||||
|     | DueDateChange (Maybe Int) | ||||
|     | NameChange String | ||||
|  | ||||
|  | ||||
| multiUpdate : | ||||
|     Flags | ||||
|     -> Set String | ||||
|     -> FormChange | ||||
|     -> (Result Http.Error BasicResult -> msg) | ||||
|     -> Cmd msg | ||||
| multiUpdate flags ids change receive = | ||||
|     let | ||||
|         items = | ||||
|             Set.toList ids | ||||
|     in | ||||
|     case change of | ||||
|         _ -> | ||||
|             Cmd.none | ||||
| @@ -24,7 +24,6 @@ import File exposing (File) | ||||
| import Html exposing (..) | ||||
| import Html.Attributes exposing (..) | ||||
| import Html.Events exposing (onCheck, onClick, onInput) | ||||
| import Html5.DragDrop as DD | ||||
| import Markdown | ||||
| import Page exposing (Page(..)) | ||||
| import Set | ||||
|   | ||||
| @@ -6,6 +6,8 @@ module Comp.YesNoDimmer exposing | ||||
|     , defaultSettings | ||||
|     , disable | ||||
|     , emptyModel | ||||
|     , initActive | ||||
|     , initInactive | ||||
|     , update | ||||
|     , view | ||||
|     , view2 | ||||
| @@ -27,6 +29,18 @@ emptyModel = | ||||
|     } | ||||
|  | ||||
|  | ||||
| initInactive : Model | ||||
| initInactive = | ||||
|     { active = False | ||||
|     } | ||||
|  | ||||
|  | ||||
| initActive : Model | ||||
| initActive = | ||||
|     { active = True | ||||
|     } | ||||
|  | ||||
|  | ||||
| type Msg | ||||
|     = Activate | ||||
|     | Disable | ||||
| @@ -40,6 +54,7 @@ type alias Settings = | ||||
|     , confirmButton : String | ||||
|     , cancelButton : String | ||||
|     , invertedDimmer : Bool | ||||
|     , extraClass : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -51,6 +66,7 @@ defaultSettings = | ||||
|     , confirmButton = "Yes, do it!" | ||||
|     , cancelButton = "No" | ||||
|     , invertedDimmer = False | ||||
|     , extraClass = "" | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -87,6 +103,7 @@ view2 active settings model = | ||||
|     div | ||||
|         [ classList | ||||
|             [ ( "ui dimmer", True ) | ||||
|             , ( settings.extraClass, True ) | ||||
|             , ( "inverted", settings.invertedDimmer ) | ||||
|             , ( "active", active && model.active ) | ||||
|             ] | ||||
|   | ||||
							
								
								
									
										32
									
								
								modules/webapp/src/main/elm/Data/ItemSelection.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								modules/webapp/src/main/elm/Data/ItemSelection.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| module Data.ItemSelection exposing | ||||
|     ( ItemSelection(..) | ||||
|     , isActive | ||||
|     , isSelected | ||||
|     ) | ||||
|  | ||||
| import Set exposing (Set) | ||||
|  | ||||
|  | ||||
| type ItemSelection | ||||
|     = Inactive | ||||
|     | Active (Set String) | ||||
|  | ||||
|  | ||||
| isSelected : String -> ItemSelection -> Bool | ||||
| isSelected id set = | ||||
|     case set of | ||||
|         Inactive -> | ||||
|             False | ||||
|  | ||||
|         Active ids -> | ||||
|             Set.member id ids | ||||
|  | ||||
|  | ||||
| isActive : ItemSelection -> Bool | ||||
| isActive sel = | ||||
|     case sel of | ||||
|         Active _ -> | ||||
|             True | ||||
|  | ||||
|         Inactive -> | ||||
|             False | ||||
| @@ -1,12 +1,14 @@ | ||||
| module Data.Items exposing | ||||
|     ( concat | ||||
|     , first | ||||
|     , idSet | ||||
|     , length | ||||
|     ) | ||||
|  | ||||
| import Api.Model.ItemLight exposing (ItemLight) | ||||
| import Api.Model.ItemLightGroup exposing (ItemLightGroup) | ||||
| import Api.Model.ItemLightList exposing (ItemLightList) | ||||
| import Set exposing (Set) | ||||
| import Util.List | ||||
|  | ||||
|  | ||||
| @@ -65,3 +67,15 @@ lastGroup : ItemLightList -> Maybe ItemLightGroup | ||||
| lastGroup list = | ||||
|     List.reverse list.groups | ||||
|         |> List.head | ||||
|  | ||||
|  | ||||
| idSet : ItemLightList -> Set String | ||||
| idSet items = | ||||
|     List.map idSetGroup items.groups | ||||
|         |> List.foldl Set.union Set.empty | ||||
|  | ||||
|  | ||||
| idSetGroup : ItemLightGroup -> Set String | ||||
| idSetGroup group = | ||||
|     List.map .id group.items | ||||
|         |> Set.fromList | ||||
|   | ||||
| @@ -2,12 +2,18 @@ module Page.Home.Data exposing | ||||
|     ( Model | ||||
|     , Msg(..) | ||||
|     , SearchType(..) | ||||
|     , SelectActionMode(..) | ||||
|     , SelectViewModel | ||||
|     , ViewMode(..) | ||||
|     , defaultSearchType | ||||
|     , doSearchCmd | ||||
|     , init | ||||
|     , initSelectViewModel | ||||
|     , itemNav | ||||
|     , menuCollapsed | ||||
|     , resultsBelowLimit | ||||
|     , searchTypeString | ||||
|     , selectActive | ||||
|     ) | ||||
|  | ||||
| import Api | ||||
| @@ -16,12 +22,16 @@ import Api.Model.ItemSearch | ||||
| import Browser.Dom as Dom | ||||
| import Comp.FixedDropdown | ||||
| import Comp.ItemCardList | ||||
| import Comp.ItemDetail.EditMenu | ||||
| import Comp.SearchMenu | ||||
| import Comp.YesNoDimmer | ||||
| import Data.Flags exposing (Flags) | ||||
| import Data.ItemNav exposing (ItemNav) | ||||
| import Data.ItemSelection exposing (ItemSelection) | ||||
| import Data.Items | ||||
| import Data.UiSettings exposing (UiSettings) | ||||
| import Http | ||||
| import Set exposing (Set) | ||||
| import Throttle exposing (Throttle) | ||||
| import Util.Html exposing (KeyCode(..)) | ||||
| import Util.ItemDragDrop as DD | ||||
| @@ -31,7 +41,7 @@ type alias Model = | ||||
|     { searchMenuModel : Comp.SearchMenu.Model | ||||
|     , itemListModel : Comp.ItemCardList.Model | ||||
|     , searchInProgress : Bool | ||||
|     , menuCollapsed : Bool | ||||
|     , viewMode : ViewMode | ||||
|     , searchOffset : Int | ||||
|     , moreAvailable : Bool | ||||
|     , moreInProgress : Bool | ||||
| @@ -45,6 +55,29 @@ type alias Model = | ||||
|     } | ||||
|  | ||||
|  | ||||
| type alias SelectViewModel = | ||||
|     { ids : Set String | ||||
|     , action : SelectActionMode | ||||
|     , deleteAllConfirm : Comp.YesNoDimmer.Model | ||||
|     , editModel : Comp.ItemDetail.EditMenu.Model | ||||
|     } | ||||
|  | ||||
|  | ||||
| initSelectViewModel : SelectViewModel | ||||
| initSelectViewModel = | ||||
|     { ids = Set.empty | ||||
|     , action = NoneAction | ||||
|     , deleteAllConfirm = Comp.YesNoDimmer.initActive | ||||
|     , editModel = Comp.ItemDetail.EditMenu.init | ||||
|     } | ||||
|  | ||||
|  | ||||
| type ViewMode | ||||
|     = SimpleView | ||||
|     | SearchView | ||||
|     | SelectView SelectViewModel | ||||
|  | ||||
|  | ||||
| init : Flags -> Model | ||||
| init flags = | ||||
|     let | ||||
| @@ -58,7 +91,6 @@ init flags = | ||||
|     { searchMenuModel = Comp.SearchMenu.init | ||||
|     , itemListModel = Comp.ItemCardList.init | ||||
|     , searchInProgress = False | ||||
|     , menuCollapsed = True | ||||
|     , searchOffset = 0 | ||||
|     , moreAvailable = True | ||||
|     , moreInProgress = False | ||||
| @@ -72,6 +104,7 @@ init flags = | ||||
|     , dragDropData = | ||||
|         DD.DragDropData DD.init Nothing | ||||
|     , scrollToCard = Nothing | ||||
|     , viewMode = SimpleView | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -84,6 +117,32 @@ defaultSearchType flags = | ||||
|         BasicSearch | ||||
|  | ||||
|  | ||||
| menuCollapsed : Model -> Bool | ||||
| menuCollapsed model = | ||||
|     case model.viewMode of | ||||
|         SimpleView -> | ||||
|             True | ||||
|  | ||||
|         SearchView -> | ||||
|             False | ||||
|  | ||||
|         SelectView _ -> | ||||
|             False | ||||
|  | ||||
|  | ||||
| selectActive : Model -> Bool | ||||
| selectActive model = | ||||
|     case model.viewMode of | ||||
|         SimpleView -> | ||||
|             False | ||||
|  | ||||
|         SearchView -> | ||||
|             False | ||||
|  | ||||
|         SelectView _ -> | ||||
|             True | ||||
|  | ||||
|  | ||||
| type Msg | ||||
|     = Init | ||||
|     | SearchMenuMsg Comp.SearchMenu.Msg | ||||
| @@ -93,6 +152,7 @@ type Msg | ||||
|     | ItemSearchAddResp (Result Http.Error ItemLightList) | ||||
|     | DoSearch | ||||
|     | ToggleSearchMenu | ||||
|     | ToggleSelectView | ||||
|     | LoadMore | ||||
|     | UpdateThrottle | ||||
|     | SetBasicSearch String | ||||
| @@ -101,6 +161,12 @@ type Msg | ||||
|     | SetContentOnly String | ||||
|     | ScrollResult (Result Dom.Error ()) | ||||
|     | ClearItemDetailId | ||||
|     | SelectAllItems | ||||
|     | SelectNoItems | ||||
|     | RequestDeleteSelected | ||||
|     | DeleteSelectedConfirmMsg Comp.YesNoDimmer.Msg | ||||
|     | EditSelectedItems | ||||
|     | EditMenuMsg Comp.ItemDetail.EditMenu.Msg | ||||
|  | ||||
|  | ||||
| type SearchType | ||||
| @@ -109,6 +175,12 @@ type SearchType | ||||
|     | ContentOnlySearch | ||||
|  | ||||
|  | ||||
| type SelectActionMode | ||||
|     = NoneAction | ||||
|     | DeleteSelected | ||||
|     | EditSelected | ||||
|  | ||||
|  | ||||
| searchTypeString : SearchType -> String | ||||
| searchTypeString st = | ||||
|     case st of | ||||
|   | ||||
| @@ -3,13 +3,18 @@ module Page.Home.Update exposing (update) | ||||
| import Browser.Navigation as Nav | ||||
| import Comp.FixedDropdown | ||||
| import Comp.ItemCardList | ||||
| import Comp.ItemDetail.EditMenu | ||||
| import Comp.SearchMenu | ||||
| import Comp.YesNoDimmer | ||||
| import Data.Flags exposing (Flags) | ||||
| import Data.ItemSelection | ||||
| import Data.Items | ||||
| import Data.UiSettings exposing (UiSettings) | ||||
| import Page exposing (Page(..)) | ||||
| import Page.Home.Data exposing (..) | ||||
| import Process | ||||
| import Scroll | ||||
| import Set | ||||
| import Task | ||||
| import Throttle | ||||
| import Time | ||||
| @@ -82,10 +87,19 @@ update mId key flags settings msg model = | ||||
|                         flags | ||||
|                         m | ||||
|                         model.itemListModel | ||||
|  | ||||
|                 nextView = | ||||
|                     case ( model.viewMode, result.selection ) of | ||||
|                         ( SelectView svm, Data.ItemSelection.Active ids ) -> | ||||
|                             SelectView { svm | ids = ids } | ||||
|  | ||||
|                         ( v, _ ) -> | ||||
|                             v | ||||
|             in | ||||
|             withSub | ||||
|                 ( { model | ||||
|                     | itemListModel = result.model | ||||
|                     , viewMode = nextView | ||||
|                     , dragDropData = DD.DragDropData result.dragModel Nothing | ||||
|                   } | ||||
|                 , Cmd.batch [ Cmd.map ItemCardListMsg result.cmd ] | ||||
| @@ -159,11 +173,43 @@ update mId key flags settings msg model = | ||||
|                 doSearch flags settings False nm | ||||
|  | ||||
|         ToggleSearchMenu -> | ||||
|             let | ||||
|                 nextView = | ||||
|                     case model.viewMode of | ||||
|                         SimpleView -> | ||||
|                             SearchView | ||||
|  | ||||
|                         SearchView -> | ||||
|                             SimpleView | ||||
|  | ||||
|                         SelectView _ -> | ||||
|                             SimpleView | ||||
|             in | ||||
|             withSub | ||||
|                 ( { model | menuCollapsed = not model.menuCollapsed } | ||||
|                 ( { model | viewMode = nextView } | ||||
|                 , Cmd.none | ||||
|                 ) | ||||
|  | ||||
|         ToggleSelectView -> | ||||
|             let | ||||
|                 ( nextView, cmd ) = | ||||
|                     case model.viewMode of | ||||
|                         SimpleView -> | ||||
|                             ( SelectView initSelectViewModel, loadEditModel flags ) | ||||
|  | ||||
|                         SearchView -> | ||||
|                             ( SelectView initSelectViewModel, loadEditModel flags ) | ||||
|  | ||||
|                         SelectView _ -> | ||||
|                             ( SearchView, Cmd.none ) | ||||
|             in | ||||
|             withSub | ||||
|                 ( { model | ||||
|                     | viewMode = nextView | ||||
|                   } | ||||
|                 , cmd | ||||
|                 ) | ||||
|  | ||||
|         LoadMore -> | ||||
|             if model.moreAvailable then | ||||
|                 doSearchMore flags settings model |> withSub | ||||
| @@ -253,6 +299,139 @@ update mId key flags settings msg model = | ||||
|         ClearItemDetailId -> | ||||
|             noSub ( { model | scrollToCard = Nothing }, Cmd.none ) | ||||
|  | ||||
|         SelectAllItems -> | ||||
|             case model.viewMode of | ||||
|                 SelectView svm -> | ||||
|                     let | ||||
|                         visible = | ||||
|                             Data.Items.idSet model.itemListModel.results | ||||
|  | ||||
|                         svm_ = | ||||
|                             { svm | ids = Set.union svm.ids visible } | ||||
|                     in | ||||
|                     noSub | ||||
|                         ( { model | viewMode = SelectView svm_ } | ||||
|                         , Cmd.none | ||||
|                         ) | ||||
|  | ||||
|                 _ -> | ||||
|                     noSub ( model, Cmd.none ) | ||||
|  | ||||
|         SelectNoItems -> | ||||
|             case model.viewMode of | ||||
|                 SelectView svm -> | ||||
|                     let | ||||
|                         svm_ = | ||||
|                             { svm | ids = Set.empty } | ||||
|                     in | ||||
|                     noSub | ||||
|                         ( { model | viewMode = SelectView svm_ } | ||||
|                         , Cmd.none | ||||
|                         ) | ||||
|  | ||||
|                 _ -> | ||||
|                     noSub ( model, Cmd.none ) | ||||
|  | ||||
|         DeleteSelectedConfirmMsg lmsg -> | ||||
|             case model.viewMode of | ||||
|                 SelectView svm -> | ||||
|                     let | ||||
|                         ( confirmModel, confirmed ) = | ||||
|                             Comp.YesNoDimmer.update lmsg svm.deleteAllConfirm | ||||
|  | ||||
|                         cmd = | ||||
|                             if confirmed then | ||||
|                                 Cmd.none | ||||
|  | ||||
|                             else | ||||
|                                 Cmd.none | ||||
|  | ||||
|                         act = | ||||
|                             if confirmModel.active || confirmed then | ||||
|                                 DeleteSelected | ||||
|  | ||||
|                             else | ||||
|                                 NoneAction | ||||
|                     in | ||||
|                     noSub | ||||
|                         ( { model | ||||
|                             | viewMode = | ||||
|                                 SelectView | ||||
|                                     { svm | ||||
|                                         | deleteAllConfirm = confirmModel | ||||
|                                         , action = act | ||||
|                                     } | ||||
|                           } | ||||
|                         , cmd | ||||
|                         ) | ||||
|  | ||||
|                 _ -> | ||||
|                     noSub ( model, Cmd.none ) | ||||
|  | ||||
|         RequestDeleteSelected -> | ||||
|             case model.viewMode of | ||||
|                 SelectView svm -> | ||||
|                     if svm.ids == Set.empty then | ||||
|                         noSub ( model, Cmd.none ) | ||||
|  | ||||
|                     else | ||||
|                         let | ||||
|                             lmsg = | ||||
|                                 DeleteSelectedConfirmMsg Comp.YesNoDimmer.activate | ||||
|  | ||||
|                             model_ = | ||||
|                                 { model | viewMode = SelectView { svm | action = DeleteSelected } } | ||||
|                         in | ||||
|                         update mId key flags settings lmsg model_ | ||||
|  | ||||
|                 _ -> | ||||
|                     noSub ( model, Cmd.none ) | ||||
|  | ||||
|         EditSelectedItems -> | ||||
|             case model.viewMode of | ||||
|                 SelectView svm -> | ||||
|                     if svm.action == EditSelected then | ||||
|                         noSub | ||||
|                             ( { model | viewMode = SelectView { svm | action = NoneAction } } | ||||
|                             , Cmd.none | ||||
|                             ) | ||||
|  | ||||
|                     else if svm.ids == Set.empty then | ||||
|                         noSub ( model, Cmd.none ) | ||||
|  | ||||
|                     else | ||||
|                         noSub | ||||
|                             ( { model | viewMode = SelectView { svm | action = EditSelected } } | ||||
|                             , Cmd.none | ||||
|                             ) | ||||
|  | ||||
|                 _ -> | ||||
|                     noSub ( model, Cmd.none ) | ||||
|  | ||||
|         EditMenuMsg lmsg -> | ||||
|             case model.viewMode of | ||||
|                 SelectView svm -> | ||||
|                     let | ||||
|                         res = | ||||
|                             Comp.ItemDetail.EditMenu.update flags lmsg svm.editModel | ||||
|  | ||||
|                         svm_ = | ||||
|                             { svm | editModel = res.model } | ||||
|  | ||||
|                         cmd_ = | ||||
|                             Cmd.map EditMenuMsg res.cmd | ||||
|  | ||||
|                         sub_ = | ||||
|                             Sub.map EditMenuMsg res.sub | ||||
|  | ||||
|                         _ = | ||||
|                             Debug.log "change" res.change | ||||
|                     in | ||||
|                     ( { model | viewMode = SelectView svm_ }, cmd_, sub_ ) | ||||
|  | ||||
|                 _ -> | ||||
|                     noSub ( model, Cmd.none ) | ||||
|  | ||||
|  | ||||
|  | ||||
| --- Helpers | ||||
| @@ -275,12 +454,17 @@ scrollToCard mId model = | ||||
|             ( model, Cmd.none, Sub.none ) | ||||
|  | ||||
|  | ||||
| loadEditModel : Flags -> Cmd Msg | ||||
| loadEditModel flags = | ||||
|     Cmd.map EditMenuMsg (Comp.ItemDetail.EditMenu.loadModel flags) | ||||
|  | ||||
|  | ||||
| doSearch : Flags -> UiSettings -> Bool -> Model -> ( Model, Cmd Msg, Sub Msg ) | ||||
| doSearch flags settings scroll model = | ||||
|     let | ||||
|         stype = | ||||
|             if | ||||
|                 not model.menuCollapsed | ||||
|                 not (menuCollapsed model) | ||||
|                     || Util.String.isNothingOrBlank model.contentOnlySearch | ||||
|             then | ||||
|                 BasicSearch | ||||
|   | ||||
| @@ -3,26 +3,51 @@ module Page.Home.View exposing (view) | ||||
| import Api.Model.ItemSearch | ||||
| import Comp.FixedDropdown | ||||
| import Comp.ItemCardList | ||||
| import Comp.ItemDetail.EditMenu | ||||
| import Comp.SearchMenu | ||||
| import Comp.YesNoDimmer | ||||
| import Data.Flags exposing (Flags) | ||||
| import Data.ItemSelection | ||||
| import Data.UiSettings exposing (UiSettings) | ||||
| import Html exposing (..) | ||||
| import Html.Attributes exposing (..) | ||||
| import Html.Events exposing (onClick, onInput) | ||||
| import Page exposing (Page(..)) | ||||
| import Page.Home.Data exposing (..) | ||||
| import Set | ||||
| import Util.Html | ||||
|  | ||||
|  | ||||
| view : Flags -> UiSettings -> Model -> Html Msg | ||||
| view flags settings model = | ||||
|     let | ||||
|         itemViewCfg = | ||||
|             case model.viewMode of | ||||
|                 SelectView svm -> | ||||
|                     Comp.ItemCardList.ViewConfig | ||||
|                         model.scrollToCard | ||||
|                         (Data.ItemSelection.Active svm.ids) | ||||
|  | ||||
|                 _ -> | ||||
|                     Comp.ItemCardList.ViewConfig | ||||
|                         model.scrollToCard | ||||
|                         Data.ItemSelection.Inactive | ||||
|  | ||||
|         selectAction = | ||||
|             case model.viewMode of | ||||
|                 SelectView svm -> | ||||
|                     svm.action | ||||
|  | ||||
|                 _ -> | ||||
|                     NoneAction | ||||
|     in | ||||
|     div [ class "home-page ui padded grid" ] | ||||
|         [ div | ||||
|             [ classList | ||||
|                 [ ( "sixteen wide mobile six wide tablet four wide computer search-menu column" | ||||
|                   , True | ||||
|                   ) | ||||
|                 , ( "invisible hidden", model.menuCollapsed ) | ||||
|                 , ( "invisible hidden", menuCollapsed model ) | ||||
|                 ] | ||||
|             ] | ||||
|             [ div | ||||
| @@ -38,6 +63,17 @@ view flags settings model = | ||||
|                     ] | ||||
|                 , div [ class "right floated menu" ] | ||||
|                     [ a | ||||
|                         [ classList | ||||
|                             [ ( "borderless item", True ) | ||||
|                             , ( "active", selectActive model ) | ||||
|                             ] | ||||
|                         , href "#" | ||||
|                         , title "Select items" | ||||
|                         , onClick ToggleSelectView | ||||
|                         ] | ||||
|                         [ i [ class "tasks icon" ] [] | ||||
|                         ] | ||||
|                     , a | ||||
|                         [ class "borderless item" | ||||
|                         , onClick ResetSearch | ||||
|                         , title "Reset form" | ||||
| @@ -63,26 +99,30 @@ view flags settings model = | ||||
|                     ] | ||||
|                 ] | ||||
|             , div [ class "" ] | ||||
|                 [ Html.map SearchMenuMsg | ||||
|                     (Comp.SearchMenu.viewDrop model.dragDropData | ||||
|                         flags | ||||
|                         settings | ||||
|                         model.searchMenuModel | ||||
|                     ) | ||||
|                 ] | ||||
|                 (viewLeftMenu flags settings model) | ||||
|             ] | ||||
|         , div | ||||
|             [ classList | ||||
|                 [ ( "sixteen wide mobile ten wide tablet twelve wide computer column" | ||||
|                   , not model.menuCollapsed | ||||
|                   , not (menuCollapsed model) | ||||
|                   ) | ||||
|                 , ( "sixteen wide column", model.menuCollapsed ) | ||||
|                 , ( "sixteen wide column", menuCollapsed model ) | ||||
|                 , ( "item-card-list", True ) | ||||
|                 ] | ||||
|             ] | ||||
|             [ viewSearchBar flags model | ||||
|             [ viewBar flags model | ||||
|             , case model.viewMode of | ||||
|                 SelectView svm -> | ||||
|                     Html.map DeleteSelectedConfirmMsg | ||||
|                         (Comp.YesNoDimmer.view2 (selectAction == DeleteSelected) | ||||
|                             deleteAllDimmer | ||||
|                             svm.deleteAllConfirm | ||||
|                         ) | ||||
|  | ||||
|                 _ -> | ||||
|                     span [ class "invisible" ] [] | ||||
|             , Html.map ItemCardListMsg | ||||
|                 (Comp.ItemCardList.view model.scrollToCard settings model.itemListModel) | ||||
|                 (Comp.ItemCardList.view itemViewCfg settings model.itemListModel) | ||||
|             ] | ||||
|         , div | ||||
|             [ classList | ||||
| @@ -117,6 +157,113 @@ view flags settings model = | ||||
|         ] | ||||
|  | ||||
|  | ||||
| viewLeftMenu : Flags -> UiSettings -> Model -> List (Html Msg) | ||||
| viewLeftMenu flags settings model = | ||||
|     let | ||||
|         searchMenu = | ||||
|             [ Html.map SearchMenuMsg | ||||
|                 (Comp.SearchMenu.viewDrop model.dragDropData | ||||
|                     flags | ||||
|                     settings | ||||
|                     model.searchMenuModel | ||||
|                 ) | ||||
|             ] | ||||
|     in | ||||
|     case model.viewMode of | ||||
|         SelectView svm -> | ||||
|             case svm.action of | ||||
|                 EditSelected -> | ||||
|                     let | ||||
|                         cfg = | ||||
|                             Comp.ItemDetail.EditMenu.defaultViewConfig | ||||
|                     in | ||||
|                     [ div [ class "ui dividing header" ] | ||||
|                         [ text "Multi-Edit" | ||||
|                         ] | ||||
|                     , div [ class "ui info message" ] | ||||
|                         [ text "Note that a change here immediatly affects all selected items on the right!" | ||||
|                         ] | ||||
|                     , Html.map EditMenuMsg | ||||
|                         (Comp.ItemDetail.EditMenu.view cfg settings svm.editModel) | ||||
|                     ] | ||||
|  | ||||
|                 _ -> | ||||
|                     searchMenu | ||||
|  | ||||
|         _ -> | ||||
|             searchMenu | ||||
|  | ||||
|  | ||||
| viewBar : Flags -> Model -> Html Msg | ||||
| viewBar flags model = | ||||
|     case model.viewMode of | ||||
|         SimpleView -> | ||||
|             viewSearchBar flags model | ||||
|  | ||||
|         SearchView -> | ||||
|             div [ class "hidden invisible" ] [] | ||||
|  | ||||
|         SelectView svm -> | ||||
|             viewActionBar flags svm model | ||||
|  | ||||
|  | ||||
| viewActionBar : Flags -> SelectViewModel -> Model -> Html Msg | ||||
| viewActionBar _ svm model = | ||||
|     let | ||||
|         selectCount = | ||||
|             Set.size svm.ids |> String.fromInt | ||||
|     in | ||||
|     div | ||||
|         [ class "ui ablue-comp icon menu" | ||||
|         ] | ||||
|         [ a | ||||
|             [ classList | ||||
|                 [ ( "borderless item", True ) | ||||
|                 , ( "active", svm.action == EditSelected ) | ||||
|                 ] | ||||
|             , href "#" | ||||
|             , title <| "Edit " ++ selectCount ++ " selected items" | ||||
|             , onClick EditSelectedItems | ||||
|             ] | ||||
|             [ i [ class "ui edit icon" ] [] | ||||
|             ] | ||||
|         , a | ||||
|             [ classList | ||||
|                 [ ( "borderless item", True ) | ||||
|                 , ( "active", svm.action == DeleteSelected ) | ||||
|                 ] | ||||
|             , href "#" | ||||
|             , title <| "Delete " ++ selectCount ++ " selected items" | ||||
|             , onClick RequestDeleteSelected | ||||
|             ] | ||||
|             [ i [ class "trash icon" ] [] | ||||
|             ] | ||||
|         , div [ class "right menu" ] | ||||
|             [ a | ||||
|                 [ class "item" | ||||
|                 , href "#" | ||||
|                 , onClick SelectAllItems | ||||
|                 , title "Select all" | ||||
|                 ] | ||||
|                 [ i [ class "check square outline icon" ] [] | ||||
|                 ] | ||||
|             , a | ||||
|                 [ class "borderless item" | ||||
|                 , href "#" | ||||
|                 , title "Select none" | ||||
|                 , onClick SelectNoItems | ||||
|                 ] | ||||
|                 [ i [ class "square outline icon" ] [] | ||||
|                 ] | ||||
|             , div [ class "borderless label item" ] | ||||
|                 [ div [ class "ui circular purple icon label" ] | ||||
|                     [ text selectCount | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|         ] | ||||
|  | ||||
|  | ||||
| viewSearchBar : Flags -> Model -> Html Msg | ||||
| viewSearchBar flags model = | ||||
|     let | ||||
| @@ -145,7 +292,7 @@ viewSearchBar flags model = | ||||
|     in | ||||
|     div | ||||
|         [ classList | ||||
|             [ ( "invisible hidden", not model.menuCollapsed ) | ||||
|             [ ( "invisible hidden", not (menuCollapsed model) ) | ||||
|             , ( "ui secondary stackable menu container", True ) | ||||
|             ] | ||||
|         ] | ||||
| @@ -221,3 +368,15 @@ hasMoreSearch model = | ||||
|                     Api.Model.ItemSearch.empty | ||||
|     in | ||||
|     is_ /= Api.Model.ItemSearch.empty | ||||
|  | ||||
|  | ||||
| deleteAllDimmer : Comp.YesNoDimmer.Settings | ||||
| deleteAllDimmer = | ||||
|     { message = "Really delete all selected items?" | ||||
|     , headerIcon = "exclamation icon" | ||||
|     , headerClass = "ui inverted icon header" | ||||
|     , confirmButton = "Yes" | ||||
|     , cancelButton = "No" | ||||
|     , invertedDimmer = False | ||||
|     , extraClass = "top aligned" | ||||
|     } | ||||
|   | ||||
| @@ -31,6 +31,9 @@ | ||||
|     margin: 0 1em; | ||||
| } | ||||
|  | ||||
| .default-layout .ui.icon.menu .label.item { | ||||
|     padding: 0 0.5em 0 0; | ||||
| } | ||||
|  | ||||
| .default-layout .right-float { | ||||
|     float: right; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user