Improve item detail view

- Separate page (permalink) for item details

- Use available space and hide search menu

- Disable item navigation links if there is nothing to go to

- Show notes more prominently and allow to hide them
This commit is contained in:
Eike Kettner 2019-12-31 00:56:41 +01:00
parent 36a6fdd746
commit c73cdd82ab
12 changed files with 342 additions and 183 deletions

View File

@ -15,6 +15,7 @@ import Http
import Page exposing (Page(..))
import Page.CollectiveSettings.Data
import Page.Home.Data
import Page.ItemDetail.Data
import Page.Login.Data
import Page.ManageData.Data
import Page.NewInvite.Data
@ -39,6 +40,7 @@ type alias Model =
, registerModel : Page.Register.Data.Model
, uploadModel : Page.Upload.Data.Model
, newInviteModel : Page.NewInvite.Data.Model
, itemDetailModel : Page.ItemDetail.Data.Model
, navMenuOpen : Bool
, subs : Sub Msg
}
@ -64,6 +66,7 @@ init key url flags =
, registerModel = Page.Register.Data.emptyModel
, uploadModel = Page.Upload.Data.emptyModel
, newInviteModel = Page.NewInvite.Data.emptyModel
, itemDetailModel = Page.ItemDetail.Data.emptyModel
, navMenuOpen = False
, subs = Sub.none
}
@ -82,6 +85,7 @@ type Msg
| RegisterMsg Page.Register.Data.Msg
| UploadMsg Page.Upload.Data.Msg
| NewInviteMsg Page.NewInvite.Data.Msg
| ItemDetailMsg Page.ItemDetail.Data.Msg
| Logout
| LogoutResp (Result Http.Error ())
| SessionCheckResp (Result Http.Error AuthResult)

View File

@ -13,6 +13,8 @@ import Page.CollectiveSettings.Data
import Page.CollectiveSettings.Update
import Page.Home.Data
import Page.Home.Update
import Page.ItemDetail.Data
import Page.ItemDetail.Update
import Page.Login.Data
import Page.Login.Update
import Page.ManageData.Data
@ -71,6 +73,9 @@ updateWithSub msg model =
NewInviteMsg m ->
updateNewInvite m model |> noSub
ItemDetailMsg m ->
updateItemDetail m model |> noSub
VersionResp (Ok info) ->
( { model | version = info }, Cmd.none ) |> noSub
@ -170,6 +175,20 @@ updateWithSub msg model =
( { model | navMenuOpen = not model.navMenuOpen }, Cmd.none, Sub.none )
updateItemDetail : Page.ItemDetail.Data.Msg -> Model -> ( Model, Cmd Msg )
updateItemDetail lmsg model =
let
inav =
Page.Home.Data.itemNav model.itemDetailModel.detail.item.id model.homeModel
( lm, lc ) =
Page.ItemDetail.Update.update model.key model.flags inav.next lmsg model.itemDetailModel
in
( { model | itemDetailModel = lm }
, Cmd.map ItemDetailMsg lc
)
updateNewInvite : Page.NewInvite.Data.Msg -> Model -> ( Model, Cmd Msg )
updateNewInvite lmsg model =
let
@ -265,7 +284,7 @@ updateHome : Page.Home.Data.Msg -> Model -> ( Model, Cmd Msg )
updateHome lmsg model =
let
( lm, lc ) =
Page.Home.Update.update model.flags lmsg model.homeModel
Page.Home.Update.update model.key model.flags lmsg model.homeModel
in
( { model | homeModel = lm }
, Cmd.map HomeMsg lc
@ -321,6 +340,9 @@ initPage model page =
NewInvitePage ->
updateQueue Page.Queue.Data.StopRefresh model
ItemDetailPage id ->
updateItemDetail (Page.ItemDetail.Data.Init id) model
noSub : ( Model, Cmd Msg ) -> ( Model, Cmd Msg, Sub Msg )
noSub ( m, c ) =

View File

@ -6,7 +6,9 @@ import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Page exposing (Page(..))
import Page.CollectiveSettings.View
import Page.Home.Data
import Page.Home.View
import Page.ItemDetail.View
import Page.Login.View
import Page.ManageData.View
import Page.NewInvite.View
@ -105,11 +107,23 @@ defaultLayout model =
NewInvitePage ->
viewNewInvite model
ItemDetailPage id ->
viewItemDetail id model
]
, footer model
]
viewItemDetail : String -> Model -> Html Msg
viewItemDetail id model =
let
inav =
Page.Home.Data.itemNav id model.homeModel
in
Html.map ItemDetailMsg (Page.ItemDetail.View.view inav model.itemDetailModel)
viewNewInvite : Model -> Html Msg
viewNewInvite model =
Html.map NewInviteMsg (Page.NewInvite.View.view model.flags model.newInviteModel)

View File

@ -1,7 +1,6 @@
module Comp.ItemDetail exposing
( Model
, Msg(..)
, UserNav(..)
, emptyModel
, update
, view
@ -20,6 +19,7 @@ import Api.Model.OptionalText exposing (OptionalText)
import Api.Model.ReferenceList exposing (ReferenceList)
import Api.Model.Tag exposing (Tag)
import Api.Model.TagList exposing (TagList)
import Browser.Navigation as Nav
import Comp.DatePicker
import Comp.Dropdown exposing (isDropdownChangeMsg)
import Comp.YesNoDimmer
@ -31,6 +31,7 @@ import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput)
import Http
import Markdown
import Page exposing (Page(..))
import Util.Maybe
import Util.Size
import Util.String
@ -49,6 +50,7 @@ type alias Model =
, concEquipModel : Comp.Dropdown.Model IdName
, nameModel : String
, notesModel : Maybe String
, notesHidden : Bool
, deleteConfirm : Comp.YesNoDimmer.Model
, itemDatePicker : DatePicker
, itemDate : Maybe Int
@ -76,7 +78,11 @@ emptyModel =
}
, directionModel =
Comp.Dropdown.makeSingleList
{ makeOption = \entry -> { value = Data.Direction.toString entry, text = Data.Direction.toString entry }
{ makeOption =
\entry ->
{ value = Data.Direction.toString entry
, text = Data.Direction.toString entry
}
, options = Data.Direction.all
, placeholder = "Choose a direction"
, selected = Nothing
@ -103,6 +109,7 @@ emptyModel =
}
, nameModel = ""
, notesModel = Nothing
, notesHidden = False
, deleteConfirm = Comp.YesNoDimmer.emptyModel
, itemDatePicker = Comp.DatePicker.emptyModel
, itemDate = Nothing
@ -112,26 +119,12 @@ emptyModel =
}
type UserNav
= NavBack
| NavPrev
| NavNext
| NavNone
| NavNextOrBack
noNav : ( Model, Cmd Msg ) -> ( Model, Cmd Msg, UserNav )
noNav ( model, cmd ) =
( model, cmd, NavNone )
type Msg
= ToggleMenu
| ReloadItem
| Init
| SetItem ItemDetail
| SetActiveAttachment Int
| NavClick UserNav
| TagDropdownMsg (Comp.Dropdown.Msg Tag)
| DirDropdownMsg (Comp.Dropdown.Msg Direction)
| OrgDropdownMsg (Comp.Dropdown.Msg IdName)
@ -145,6 +138,7 @@ type Msg
| SetName String
| SaveName
| SetNotes String
| ToggleNotes
| SaveNotes
| ConfirmItem
| UnconfirmItem
@ -281,8 +275,8 @@ setDueDate flags model date =
Api.setItemDueDate flags model.item.id (OptionalDate date) SaveResp
update : Flags -> Msg -> Model -> ( Model, Cmd Msg, UserNav )
update flags msg model =
update : Nav.Key -> Flags -> Maybe String -> Msg -> Model -> ( Model, Cmd Msg )
update key flags next msg model =
case msg of
Init ->
let
@ -295,16 +289,17 @@ update flags msg model =
, Cmd.map ItemDatePickerMsg dpc
, Cmd.map DueDatePickerMsg dpc
]
, NavNone
)
SetItem item ->
let
( m1, c1, _ ) =
update flags (TagDropdownMsg (Comp.Dropdown.SetSelection item.tags)) model
( m1, c1 ) =
update key flags next (TagDropdownMsg (Comp.Dropdown.SetSelection item.tags)) model
( m2, c2, _ ) =
update flags
( m2, c2 ) =
update key
flags
next
(DirDropdownMsg
(Comp.Dropdown.SetSelection
(Data.Direction.fromString item.direction
@ -315,8 +310,10 @@ update flags msg model =
)
m1
( m3, c3, _ ) =
update flags
( m3, c3 ) =
update key
flags
next
(OrgDropdownMsg
(Comp.Dropdown.SetSelection
(item.corrOrg
@ -327,8 +324,10 @@ update flags msg model =
)
m2
( m4, c4, _ ) =
update flags
( m4, c4 ) =
update key
flags
next
(CorrPersonMsg
(Comp.Dropdown.SetSelection
(item.corrPerson
@ -339,8 +338,10 @@ update flags msg model =
)
m3
( m5, c5, _ ) =
update flags
( m5, c5 ) =
update key
flags
next
(ConcPersonMsg
(Comp.Dropdown.SetSelection
(item.concPerson
@ -358,26 +359,28 @@ update flags msg model =
else
Cmd.none
in
( { m5 | item = item, nameModel = item.name, notesModel = item.notes, itemDate = item.itemDate, dueDate = item.dueDate }
( { m5
| item = item
, nameModel = item.name
, notesModel = item.notes
, itemDate = item.itemDate
, dueDate = item.dueDate
}
, Cmd.batch [ c1, c2, c3, c4, c5, getOptions flags, proposalCmd ]
)
|> noNav
SetActiveAttachment pos ->
( { model | visibleAttach = pos }, Cmd.none, NavNone )
NavClick nav ->
( model, Cmd.none, nav )
( { model | visibleAttach = pos }, Cmd.none )
ToggleMenu ->
( { model | menuOpen = not model.menuOpen }, Cmd.none, NavNone )
( { model | menuOpen = not model.menuOpen }, Cmd.none )
ReloadItem ->
if model.item.id == "" then
( model, Cmd.none, NavNone )
( model, Cmd.none )
else
( model, Api.itemDetail flags model.item.id GetItemResp, NavNone )
( model, Api.itemDetail flags model.item.id GetItemResp )
TagDropdownMsg m ->
let
@ -394,7 +397,7 @@ update flags msg model =
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map TagDropdownMsg c2 ], NavNone )
( newModel, Cmd.batch [ save, Cmd.map TagDropdownMsg c2 ] )
DirDropdownMsg m ->
let
@ -411,7 +414,7 @@ update flags msg model =
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map DirDropdownMsg c2 ] ) |> noNav
( newModel, Cmd.batch [ save, Cmd.map DirDropdownMsg c2 ] )
OrgDropdownMsg m ->
let
@ -431,7 +434,7 @@ update flags msg model =
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map OrgDropdownMsg c2 ] ) |> noNav
( newModel, Cmd.batch [ save, Cmd.map OrgDropdownMsg c2 ] )
CorrPersonMsg m ->
let
@ -451,7 +454,7 @@ update flags msg model =
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map CorrPersonMsg c2 ] ) |> noNav
( newModel, Cmd.batch [ save, Cmd.map CorrPersonMsg c2 ] )
ConcPersonMsg m ->
let
@ -471,7 +474,7 @@ update flags msg model =
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map ConcPersonMsg c2 ] ) |> noNav
( newModel, Cmd.batch [ save, Cmd.map ConcPersonMsg c2 ] )
ConcEquipMsg m ->
let
@ -491,13 +494,13 @@ update flags msg model =
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map ConcEquipMsg c2 ] ) |> noNav
( newModel, Cmd.batch [ save, Cmd.map ConcEquipMsg c2 ] )
SetName str ->
( { model | nameModel = str }, Cmd.none ) |> noNav
( { model | nameModel = str }, Cmd.none )
SaveName ->
( model, setName flags model ) |> noNav
( model, setName flags model )
SetNotes str ->
( { model
@ -510,16 +513,20 @@ update flags msg model =
}
, Cmd.none
)
|> noNav
ToggleNotes ->
( { model | notesHidden = not model.notesHidden }
, Cmd.none
)
SaveNotes ->
( model, setNotes flags model ) |> noNav
( model, setNotes flags model )
ConfirmItem ->
( model, Api.setConfirmed flags model.item.id SaveResp ) |> noNav
( model, Api.setConfirmed flags model.item.id SaveResp )
UnconfirmItem ->
( model, Api.setUnconfirmed flags model.item.id SaveResp ) |> noNav
( model, Api.setUnconfirmed flags model.item.id SaveResp )
ItemDatePickerMsg m ->
let
@ -532,13 +539,13 @@ update flags msg model =
newModel =
{ model | itemDatePicker = dp, itemDate = Just (Comp.DatePicker.midOfDay date) }
in
( newModel, setDate flags newModel newModel.itemDate ) |> noNav
( newModel, setDate flags newModel newModel.itemDate )
_ ->
( { model | itemDatePicker = dp }, Cmd.none ) |> noNav
( { model | itemDatePicker = dp }, Cmd.none )
RemoveDate ->
( { model | itemDate = Nothing }, setDate flags model Nothing ) |> noNav
( { model | itemDate = Nothing }, setDate flags model Nothing )
DueDatePickerMsg m ->
let
@ -551,13 +558,13 @@ update flags msg model =
newModel =
{ model | dueDatePicker = dp, dueDate = Just (Comp.DatePicker.midOfDay date) }
in
( newModel, setDueDate flags newModel newModel.dueDate ) |> noNav
( newModel, setDueDate flags newModel newModel.dueDate )
_ ->
( { model | dueDatePicker = dp }, Cmd.none ) |> noNav
( { model | dueDatePicker = dp }, Cmd.none )
RemoveDueDate ->
( { model | dueDate = Nothing }, setDueDate flags model Nothing ) |> noNav
( { model | dueDate = Nothing }, setDueDate flags model Nothing )
YesNoMsg m ->
let
@ -571,67 +578,67 @@ update flags msg model =
else
Cmd.none
in
( { model | deleteConfirm = cm }, cmd ) |> noNav
( { model | deleteConfirm = cm }, cmd )
RequestDelete ->
update flags (YesNoMsg Comp.YesNoDimmer.activate) model
update key flags next (YesNoMsg Comp.YesNoDimmer.activate) model
SetCorrOrgSuggestion idname ->
( model, setCorrOrg flags model (Just idname) ) |> noNav
( model, setCorrOrg flags model (Just idname) )
SetCorrPersonSuggestion idname ->
( model, setCorrPerson flags model (Just idname) ) |> noNav
( model, setCorrPerson flags model (Just idname) )
SetConcPersonSuggestion idname ->
( model, setConcPerson flags model (Just idname) ) |> noNav
( model, setConcPerson flags model (Just idname) )
SetConcEquipSuggestion idname ->
( model, setConcEquip flags model (Just idname) ) |> noNav
( model, setConcEquip flags model (Just idname) )
SetItemDateSuggestion date ->
( model, setDate flags model (Just date) ) |> noNav
( model, setDate flags model (Just date) )
SetDueDateSuggestion date ->
( model, setDueDate flags model (Just date) ) |> noNav
( model, setDueDate flags model (Just date) )
GetTagsResp (Ok tags) ->
let
tagList =
Comp.Dropdown.SetOptions tags.items
( m1, c1, _ ) =
update flags (TagDropdownMsg tagList) model
( m1, c1 ) =
update key flags next (TagDropdownMsg tagList) model
in
( m1, c1 ) |> noNav
( m1, c1 )
GetTagsResp (Err _) ->
( model, Cmd.none ) |> noNav
( model, Cmd.none )
GetOrgResp (Ok orgs) ->
let
opts =
Comp.Dropdown.SetOptions orgs.items
in
update flags (OrgDropdownMsg opts) model
update key flags next (OrgDropdownMsg opts) model
GetOrgResp (Err _) ->
( model, Cmd.none ) |> noNav
( model, Cmd.none )
GetPersonResp (Ok ps) ->
let
opts =
Comp.Dropdown.SetOptions ps.items
( m1, c1, _ ) =
update flags (CorrPersonMsg opts) model
( m1, c1 ) =
update key flags next (CorrPersonMsg opts) model
( m2, c2, _ ) =
update flags (ConcPersonMsg opts) m1
( m2, c2 ) =
update key flags next (ConcPersonMsg opts) m1
in
( m2, Cmd.batch [ c1, c2 ] ) |> noNav
( m2, Cmd.batch [ c1, c2 ] )
GetPersonResp (Err _) ->
( model, Cmd.none ) |> noNav
( model, Cmd.none )
GetEquipResp (Ok equips) ->
let
@ -641,42 +648,47 @@ update flags msg model =
equips.items
)
in
update flags (ConcEquipMsg opts) model
update key flags next (ConcEquipMsg opts) model
GetEquipResp (Err _) ->
( model, Cmd.none ) |> noNav
( model, Cmd.none )
SaveResp (Ok res) ->
if res.success then
( model, Api.itemDetail flags model.item.id GetItemResp ) |> noNav
( model, Api.itemDetail flags model.item.id GetItemResp )
else
( model, Cmd.none ) |> noNav
( model, Cmd.none )
SaveResp (Err _) ->
( model, Cmd.none ) |> noNav
( model, Cmd.none )
DeleteResp (Ok res) ->
if res.success then
( model, Cmd.none, NavNextOrBack )
case next of
Just id ->
( model, Page.set key (ItemDetailPage id) )
Nothing ->
( model, Page.set key HomePage )
else
( model, Cmd.none ) |> noNav
( model, Cmd.none )
DeleteResp (Err _) ->
( model, Cmd.none ) |> noNav
( model, Cmd.none )
GetItemResp (Ok item) ->
update flags (SetItem item) model
update key flags next (SetItem item) model
GetItemResp (Err _) ->
( model, Cmd.none ) |> noNav
( model, Cmd.none )
GetProposalResp (Ok ip) ->
( { model | itemProposals = ip }, Cmd.none ) |> noNav
( { model | itemProposals = ip }, Cmd.none )
GetProposalResp (Err _) ->
( model, Cmd.none ) |> noNav
( model, Cmd.none )
@ -692,21 +704,38 @@ actionInputDatePicker =
{ ds | containerClassList = [ ( "ui action input", True ) ] }
view : Model -> Html Msg
view model =
view : { prev : Maybe String, next : Maybe String } -> Model -> Html Msg
view inav model =
div []
[ div
[ renderItemInfo model
, div
[ classList
[ ( "ui ablue-comp menu", True )
]
]
[ a [ class "item", href "", onClick (NavClick NavBack) ]
[ a [ class "item", Page.href HomePage ]
[ i [ class "arrow left icon" ] []
]
, a [ class "item", href "", onClick (NavClick NavPrev) ]
, a
[ classList
[ ( "item", True )
, ( "disabled", inav.prev == Nothing )
]
, Maybe.map ItemDetailPage inav.prev
|> Maybe.map Page.href
|> Maybe.withDefault (href "#")
]
[ i [ class "caret square left outline icon" ] []
]
, a [ class "item", href "", onClick (NavClick NavNext) ]
, a
[ classList
[ ( "item", True )
, ( "disabled", inav.next == Nothing )
]
, Maybe.map ItemDetailPage inav.next
|> Maybe.map Page.href
|> Maybe.withDefault (href "#")
]
[ i [ class "caret square right outline icon" ] []
]
, a
@ -722,9 +751,10 @@ view model =
]
]
, div [ class "ui grid" ]
[ div
[ Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
, div
[ classList
[ ( "six wide column", True )
[ ( "four wide column", True )
, ( "invisible", not model.menuOpen )
]
]
@ -736,17 +766,17 @@ view model =
)
, div
[ classList
[ ( "ten", model.menuOpen )
[ ( "twelve", model.menuOpen )
, ( "sixteen", not model.menuOpen )
, ( "wide column", True )
]
]
<|
List.concat
[ [ renderItemInfo model ]
, [ renderAttachmentsTabMenu model ]
[ renderNotes model
, [ renderAttachmentsTabMenu model
]
, renderAttachmentsTabBody model
, renderNotes model
, renderIdInfo model
]
]
@ -776,11 +806,31 @@ renderNotes model =
[]
Just str ->
[ h3 [ class "ui header" ]
[ text "Notes"
if model.notesHidden then
[ div [ class "ui segment" ]
[ a
[ class "ui top left attached label"
, onClick ToggleNotes
, href "#"
]
[ i [ class "eye icon" ] []
, text "Show notes"
]
]
]
else
[ div [ class "ui segment" ]
[ Markdown.toHtml [ class "item-notes" ] str
, a
[ class "ui right corner label"
, onClick ToggleNotes
, href "#"
]
[ i [ class "delete icon" ] []
]
]
]
, Markdown.toHtml [ class "item-notes" ] str
]
renderAttachmentsTabMenu : Model -> Html Msg
@ -829,12 +879,6 @@ renderAttachmentsTabBody model =
renderItemInfo : Model -> Html Msg
renderItemInfo model =
let
name =
div [ class "item" ]
[ i [ class (Data.Direction.iconFromString model.item.direction) ] []
, text model.item.name
]
date =
div [ class "item" ]
[ Maybe.withDefault model.item.created model.item.itemDate
@ -876,7 +920,7 @@ renderItemInfo model =
]
in
div [ class "ui fluid container" ]
([ h2 [ class "ui header" ]
(h2 [ class "ui header" ]
[ i [ class (Data.Direction.iconFromString model.item.direction) ] []
, div [ class "content" ]
[ text model.item.name
@ -905,8 +949,7 @@ renderItemInfo model =
]
]
]
]
++ renderTags model
:: renderTags model
)
@ -973,8 +1016,7 @@ renderEditButtons model =
renderEditForm : Model -> Html Msg
renderEditForm model =
div [ class "ui attached segment" ]
[ Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
, div [ class "ui form" ]
[ div [ class "ui form" ]
[ div [ class "field" ]
[ label []
[ i [ class "tags icon" ] []
@ -986,7 +1028,12 @@ renderEditForm model =
[ label [] [ text "Name" ]
, div [ class "ui action input" ]
[ input [ type_ "text", value model.nameModel, onInput SetName ] []
, button [ class "ui icon button", onClick SaveName ] [ i [ class "save outline icon" ] [] ]
, button
[ class "ui icon button"
, onClick SaveName
]
[ i [ class "save outline icon" ] []
]
]
]
, div [ class "field" ]
@ -996,7 +1043,12 @@ renderEditForm model =
, div [ class " field" ]
[ label [] [ text "Date" ]
, div [ class "ui action input" ]
[ Html.map ItemDatePickerMsg (Comp.DatePicker.viewTime model.itemDate actionInputDatePicker model.itemDatePicker)
[ 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" ] []
]
@ -1006,7 +1058,12 @@ renderEditForm model =
, div [ class " field" ]
[ label [] [ text "Due Date" ]
, div [ class "ui action input" ]
[ Html.map DueDatePickerMsg (Comp.DatePicker.viewTime model.dueDate actionInputDatePicker model.dueDatePicker)
[ 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" ] [] ]
]

View File

@ -10,6 +10,7 @@ module Page exposing
, pageFromString
, pageName
, pageToString
, set
, uploadId
)
@ -31,6 +32,7 @@ type Page
| RegisterPage
| UploadPage (Maybe String)
| NewInvitePage
| ItemDetailPage String
isSecured : Page -> Bool
@ -63,6 +65,9 @@ isSecured page =
UploadPage arg ->
Util.Maybe.isEmpty arg
ItemDetailPage _ ->
True
isOpen : Page -> Bool
isOpen page =
@ -114,6 +119,9 @@ pageName page =
Nothing ->
"Upload"
ItemDetailPage _ ->
"Item"
loginPageReferrer : Page -> Maybe Page
loginPageReferrer page =
@ -169,6 +177,9 @@ pageToString page =
NewInvitePage ->
"/app/newinvite"
ItemDetailPage id ->
"/app/item/" ++ id
pageFromString : String -> Maybe Page
pageFromString str =
@ -191,6 +202,11 @@ href page =
Attr.href (pageToString page)
set : Nav.Key -> Page -> Cmd msg
set key page =
Nav.pushUrl key (pageToString page)
goto : Page -> Cmd msg
goto page =
Nav.load (pageToString page)
@ -215,6 +231,7 @@ parser =
, Parser.map (\s -> UploadPage (Just s)) (s pathPrefix </> s "upload" </> string)
, Parser.map (UploadPage Nothing) (s pathPrefix </> s "upload")
, Parser.map NewInvitePage (s pathPrefix </> s "newinvite")
, Parser.map ItemDetailPage (s pathPrefix </> s "item" </> string)
]

View File

@ -3,11 +3,11 @@ module Page.Home.Data exposing
, Msg(..)
, ViewMode(..)
, emptyModel
, itemNav
)
import Api.Model.ItemDetail exposing (ItemDetail)
import Api.Model.ItemLightList exposing (ItemLightList)
import Comp.ItemDetail
import Comp.ItemList
import Comp.SearchMenu
import Http
@ -17,7 +17,6 @@ type alias Model =
{ searchMenuModel : Comp.SearchMenu.Model
, itemListModel : Comp.ItemList.Model
, searchInProgress : Bool
, itemDetailModel : Comp.ItemDetail.Model
, viewMode : ViewMode
}
@ -26,7 +25,6 @@ emptyModel : Model
emptyModel =
{ searchMenuModel = Comp.SearchMenu.emptyModel
, itemListModel = Comp.ItemList.emptyModel
, itemDetailModel = Comp.ItemDetail.emptyModel
, searchInProgress = False
, viewMode = Listing
}
@ -38,10 +36,22 @@ type Msg
| ItemListMsg Comp.ItemList.Msg
| ItemSearchResp (Result Http.Error ItemLightList)
| DoSearch
| ItemDetailMsg Comp.ItemDetail.Msg
| ItemDetailResp (Result Http.Error ItemDetail)
type ViewMode
= Listing
| Detail
itemNav : String -> Model -> { prev : Maybe String, next : Maybe String }
itemNav id model =
let
prev =
Comp.ItemList.prevItem model.itemListModel id
next =
Comp.ItemList.nextItem model.itemListModel id
in
{ prev = Maybe.map .id prev
, next = Maybe.map .id next
}

View File

@ -1,21 +1,22 @@
module Page.Home.Update exposing (update)
import Api
import Browser.Navigation as Nav
import Comp.ItemDetail
import Comp.ItemList
import Comp.SearchMenu
import Data.Flags exposing (Flags)
import Page exposing (Page(..))
import Page.Home.Data exposing (..)
import Util.Update
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
update : Nav.Key -> Flags -> Msg -> Model -> ( Model, Cmd Msg )
update key flags msg model =
case msg of
Init ->
Util.Update.andThen1
[ update flags (SearchMenuMsg Comp.SearchMenu.Init)
, update flags (ItemDetailMsg Comp.ItemDetail.Init)
[ update key flags (SearchMenuMsg Comp.SearchMenu.Init)
, doSearch flags
]
model
@ -45,7 +46,7 @@ update flags msg model =
cmd =
case mitem of
Just item ->
Api.itemDetail flags item.id ItemDetailResp
Page.set key (ItemDetailPage item.id)
Nothing ->
Cmd.none
@ -57,7 +58,7 @@ update flags msg model =
m =
{ model | searchInProgress = False, viewMode = Listing }
in
update flags (ItemListMsg (Comp.ItemList.SetResults list)) m
update key flags (ItemListMsg (Comp.ItemList.SetResults list)) m
ItemSearchResp (Err _) ->
( { model | searchInProgress = False }, Cmd.none )
@ -65,58 +66,6 @@ update flags msg model =
DoSearch ->
doSearch flags model
ItemDetailMsg m ->
let
( m2, c2, nav ) =
Comp.ItemDetail.update flags m model.itemDetailModel
newModel =
{ model | itemDetailModel = m2 }
newCmd =
Cmd.map ItemDetailMsg c2
in
case nav of
Comp.ItemDetail.NavBack ->
doSearch flags newModel
Comp.ItemDetail.NavPrev ->
case Comp.ItemList.prevItem model.itemListModel m2.item.id of
Just n ->
( newModel, Cmd.batch [ newCmd, Api.itemDetail flags n.id ItemDetailResp ] )
Nothing ->
( newModel, newCmd )
Comp.ItemDetail.NavNext ->
case Comp.ItemList.nextItem model.itemListModel m2.item.id of
Just n ->
( newModel, Cmd.batch [ newCmd, Api.itemDetail flags n.id ItemDetailResp ] )
Nothing ->
( newModel, newCmd )
Comp.ItemDetail.NavNextOrBack ->
case Comp.ItemList.nextItem model.itemListModel m2.item.id of
Just n ->
( newModel, Cmd.batch [ newCmd, Api.itemDetail flags n.id ItemDetailResp ] )
Nothing ->
doSearch flags newModel
Comp.ItemDetail.NavNone ->
( newModel, newCmd )
ItemDetailResp (Ok item) ->
let
m =
{ model | viewMode = Detail }
in
update flags (ItemDetailMsg (Comp.ItemDetail.SetItem item)) m
ItemDetailResp (Err _) ->
( model, Cmd.none )
doSearch : Flags -> Model -> ( Model, Cmd Msg )
doSearch flags model =

View File

@ -42,7 +42,7 @@ view model =
Html.map ItemListMsg (Comp.ItemList.view model.itemListModel)
Detail ->
Html.map ItemDetailMsg (Comp.ItemDetail.view model.itemDetailModel)
div [] []
]
]

View File

@ -0,0 +1,22 @@
module Page.ItemDetail.Data exposing (Model, Msg(..), emptyModel)
import Api.Model.ItemDetail exposing (ItemDetail)
import Comp.ItemDetail
import Http
type alias Model =
{ detail : Comp.ItemDetail.Model
}
emptyModel : Model
emptyModel =
{ detail = Comp.ItemDetail.emptyModel
}
type Msg
= Init String
| ItemDetailMsg Comp.ItemDetail.Msg
| ItemResp (Result Http.Error ItemDetail)

View File

@ -0,0 +1,39 @@
module Page.ItemDetail.Update exposing (update)
import Api
import Browser.Navigation as Nav
import Comp.ItemDetail
import Data.Flags exposing (Flags)
import Page.ItemDetail.Data exposing (Model, Msg(..))
update : Nav.Key -> Flags -> Maybe String -> Msg -> Model -> ( Model, Cmd Msg )
update key flags next msg model =
case msg of
Init id ->
let
( lm, lc ) =
Comp.ItemDetail.update key flags next Comp.ItemDetail.Init model.detail
in
( { model | detail = lm }
, Cmd.batch [ Api.itemDetail flags id ItemResp, Cmd.map ItemDetailMsg lc ]
)
ItemDetailMsg lmsg ->
let
( lm, lc ) =
Comp.ItemDetail.update key flags next lmsg model.detail
in
( { model | detail = lm }
, Cmd.map ItemDetailMsg lc
)
ItemResp (Ok item) ->
let
lmsg =
Comp.ItemDetail.SetItem item
in
update key flags next (ItemDetailMsg lmsg) model
ItemResp (Err err) ->
( model, Cmd.none )

View File

@ -0,0 +1,19 @@
module Page.ItemDetail.View exposing (view)
import Comp.ItemDetail
import Html exposing (..)
import Html.Attributes exposing (..)
import Page.ItemDetail.Data exposing (Model, Msg(..))
type alias ItemNav =
{ prev : Maybe String
, next : Maybe String
}
view : ItemNav -> Model -> Html Msg
view inav model =
div [ class "ui fluid container item-detail-page" ]
[ Html.map ItemDetailMsg (Comp.ItemDetail.view inav model.detail)
]

View File

@ -59,6 +59,12 @@
background: aliceblue;
}
.default-layout .ui.fluid.container.item-detail-page {
padding-top: 1em;
padding-left: 1em;
padding-right: 1em;
}
.ui.search.dropdown.open {
z-index: 20;
}