1694 lines
51 KiB
Elm

module Comp.ItemDetail exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Api
import Api.Model.Attachment exposing (Attachment)
import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.DirectionValue exposing (DirectionValue)
import Api.Model.EquipmentList exposing (EquipmentList)
import Api.Model.IdName exposing (IdName)
import Api.Model.ItemDetail exposing (ItemDetail)
import Api.Model.ItemProposals exposing (ItemProposals)
import Api.Model.OptionalDate exposing (OptionalDate)
import Api.Model.OptionalId exposing (OptionalId)
import Api.Model.OptionalText exposing (OptionalText)
import Api.Model.ReferenceList exposing (ReferenceList)
import Api.Model.SentMails exposing (SentMails)
import Api.Model.Tag exposing (Tag)
import Api.Model.TagList exposing (TagList)
import Browser.Navigation as Nav
import Comp.AttachmentMeta
import Comp.DatePicker
import Comp.Dropdown exposing (isDropdownChangeMsg)
import Comp.ItemMail
import Comp.MarkdownInput
import Comp.SentMails
import Comp.YesNoDimmer
import Data.Direction exposing (Direction)
import Data.Flags exposing (Flags)
import DatePicker exposing (DatePicker)
import Dict exposing (Dict)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput)
import Http
import Markdown
import Page exposing (Page(..))
import Util.Http
import Util.List
import Util.Maybe
import Util.Size
import Util.String
import Util.Time
type alias Model =
{ item : ItemDetail
, visibleAttach : Int
, menuOpen : Bool
, tagModel : Comp.Dropdown.Model Tag
, directionModel : Comp.Dropdown.Model Direction
, corrOrgModel : Comp.Dropdown.Model IdName
, corrPersonModel : Comp.Dropdown.Model IdName
, concPersonModel : Comp.Dropdown.Model IdName
, concEquipModel : Comp.Dropdown.Model IdName
, nameModel : String
, notesModel : Maybe String
, notesField : NotesField
, deleteConfirm : Comp.YesNoDimmer.Model
, itemDatePicker : DatePicker
, itemDate : Maybe Int
, itemProposals : ItemProposals
, dueDate : Maybe Int
, dueDatePicker : DatePicker
, itemMail : Comp.ItemMail.Model
, mailOpen : Bool
, mailSending : Bool
, mailSendResult : Maybe BasicResult
, sentMails : Comp.SentMails.Model
, sentMailsOpen : Bool
, attachMeta : Dict String Comp.AttachmentMeta.Model
, attachMetaOpen : Bool
}
type NotesField
= ViewNotes
| EditNotes Comp.MarkdownInput.Model
| HideNotes
isEditNotes : NotesField -> Bool
isEditNotes field =
case field of
EditNotes _ ->
True
ViewNotes ->
False
HideNotes ->
False
emptyModel : Model
emptyModel =
{ item = Api.Model.ItemDetail.empty
, visibleAttach = 0
, menuOpen = False
, tagModel =
Comp.Dropdown.makeMultiple
{ makeOption = \tag -> { value = tag.id, text = tag.name }
, labelColor =
\tag ->
if Util.Maybe.nonEmpty tag.category then
"basic blue"
else
""
}
, directionModel =
Comp.Dropdown.makeSingleList
{ makeOption =
\entry ->
{ value = Data.Direction.toString entry
, text = Data.Direction.toString entry
}
, options = Data.Direction.all
, placeholder = "Choose a direction"
, selected = Nothing
}
, corrOrgModel =
Comp.Dropdown.makeSingle
{ makeOption = \e -> { value = e.id, text = e.name }
, placeholder = ""
}
, corrPersonModel =
Comp.Dropdown.makeSingle
{ makeOption = \e -> { value = e.id, text = e.name }
, placeholder = ""
}
, concPersonModel =
Comp.Dropdown.makeSingle
{ makeOption = \e -> { value = e.id, text = e.name }
, placeholder = ""
}
, concEquipModel =
Comp.Dropdown.makeSingle
{ makeOption = \e -> { value = e.id, text = e.name }
, placeholder = ""
}
, nameModel = ""
, notesModel = Nothing
, notesField = ViewNotes
, deleteConfirm = Comp.YesNoDimmer.emptyModel
, itemDatePicker = Comp.DatePicker.emptyModel
, itemDate = Nothing
, itemProposals = Api.Model.ItemProposals.empty
, dueDate = Nothing
, dueDatePicker = Comp.DatePicker.emptyModel
, itemMail = Comp.ItemMail.emptyModel
, mailOpen = False
, mailSending = False
, mailSendResult = Nothing
, sentMails = Comp.SentMails.init
, sentMailsOpen = False
, attachMeta = Dict.empty
, attachMetaOpen = False
}
type Msg
= ToggleMenu
| ReloadItem
| Init
| SetItem ItemDetail
| SetActiveAttachment Int
| 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)
| SetName String
| SaveName
| SetNotes String
| ToggleNotes
| ToggleEditNotes
| NotesEditMsg Comp.MarkdownInput.Msg
| SaveNotes
| ConfirmItem
| UnconfirmItem
| SetCorrOrgSuggestion IdName
| SetCorrPersonSuggestion IdName
| SetConcPersonSuggestion IdName
| SetConcEquipSuggestion IdName
| SetItemDateSuggestion Int
| SetDueDateSuggestion Int
| ItemDatePickerMsg Comp.DatePicker.Msg
| DueDatePickerMsg Comp.DatePicker.Msg
| YesNoMsg Comp.YesNoDimmer.Msg
| RequestDelete
| SaveResp (Result Http.Error BasicResult)
| DeleteResp (Result Http.Error BasicResult)
| GetItemResp (Result Http.Error ItemDetail)
| GetProposalResp (Result Http.Error ItemProposals)
| RemoveDueDate
| RemoveDate
| ItemMailMsg Comp.ItemMail.Msg
| ToggleMail
| SendMailResp (Result Http.Error BasicResult)
| SentMailsMsg Comp.SentMails.Msg
| ToggleSentMails
| SentMailsResp (Result Http.Error SentMails)
| AttachMetaClick String
| AttachMetaMsg String Comp.AttachmentMeta.Msg
-- update
getOptions : Flags -> Cmd Msg
getOptions flags =
Cmd.batch
[ Api.getTags flags "" GetTagsResp
, Api.getOrgLight flags GetOrgResp
, Api.getPersonsLight flags GetPersonResp
, Api.getEquipments flags "" GetEquipResp
]
saveTags : Flags -> Model -> Cmd Msg
saveTags flags model =
let
tags =
Comp.Dropdown.getSelected model.tagModel
|> List.map (\t -> IdName t.id t.name)
|> ReferenceList
in
Api.setTags flags model.item.id tags SaveResp
setDirection : Flags -> Model -> Cmd Msg
setDirection flags model =
let
dir =
Comp.Dropdown.getSelected model.directionModel |> List.head
in
case dir of
Just d ->
Api.setDirection flags model.item.id (DirectionValue (Data.Direction.toString d)) SaveResp
Nothing ->
Cmd.none
setCorrOrg : Flags -> Model -> Maybe IdName -> Cmd Msg
setCorrOrg flags model mref =
let
idref =
Maybe.map .id mref
|> OptionalId
in
Api.setCorrOrg flags model.item.id idref SaveResp
setCorrPerson : Flags -> Model -> Maybe IdName -> Cmd Msg
setCorrPerson flags model mref =
let
idref =
Maybe.map .id mref
|> OptionalId
in
Api.setCorrPerson flags model.item.id idref SaveResp
setConcPerson : Flags -> Model -> Maybe IdName -> Cmd Msg
setConcPerson flags model mref =
let
idref =
Maybe.map .id mref
|> OptionalId
in
Api.setConcPerson flags model.item.id idref SaveResp
setConcEquip : Flags -> Model -> Maybe IdName -> Cmd Msg
setConcEquip flags model mref =
let
idref =
Maybe.map .id mref
|> OptionalId
in
Api.setConcEquip flags model.item.id idref SaveResp
setName : Flags -> Model -> Cmd Msg
setName flags model =
let
text =
OptionalText (Just model.nameModel)
in
if model.nameModel == "" then
Cmd.none
else
Api.setItemName flags model.item.id text SaveResp
setNotes : Flags -> Model -> Cmd Msg
setNotes flags model =
let
text =
OptionalText model.notesModel
in
Api.setItemNotes flags model.item.id text SaveResp
setDate : Flags -> Model -> Maybe Int -> Cmd Msg
setDate flags model date =
Api.setItemDate flags model.item.id (OptionalDate date) SaveResp
setDueDate : Flags -> Model -> Maybe Int -> Cmd Msg
setDueDate flags model date =
Api.setItemDueDate flags model.item.id (OptionalDate date) SaveResp
update : Nav.Key -> Flags -> Maybe String -> Msg -> Model -> ( Model, Cmd Msg )
update key flags next msg model =
case msg of
Init ->
let
( dp, dpc ) =
Comp.DatePicker.init
( im, ic ) =
Comp.ItemMail.init flags
in
( { model | itemDatePicker = dp, dueDatePicker = dp, itemMail = im, visibleAttach = 0 }
, Cmd.batch
[ getOptions flags
, Cmd.map ItemDatePickerMsg dpc
, Cmd.map DueDatePickerMsg dpc
, Cmd.map ItemMailMsg ic
, Api.getSentMails flags model.item.id SentMailsResp
]
)
SetItem item ->
let
( m1, c1 ) =
update key flags next (TagDropdownMsg (Comp.Dropdown.SetSelection item.tags)) model
( m2, c2 ) =
update key
flags
next
(DirDropdownMsg
(Comp.Dropdown.SetSelection
(Data.Direction.fromString item.direction
|> Maybe.map List.singleton
|> Maybe.withDefault []
)
)
)
m1
( m3, c3 ) =
update key
flags
next
(OrgDropdownMsg
(Comp.Dropdown.SetSelection
(item.corrOrg
|> Maybe.map List.singleton
|> Maybe.withDefault []
)
)
)
m2
( m4, c4 ) =
update key
flags
next
(CorrPersonMsg
(Comp.Dropdown.SetSelection
(item.corrPerson
|> Maybe.map List.singleton
|> Maybe.withDefault []
)
)
)
m3
( m5, c5 ) =
update key
flags
next
(ConcPersonMsg
(Comp.Dropdown.SetSelection
(item.concPerson
|> Maybe.map List.singleton
|> Maybe.withDefault []
)
)
)
m4
( m6, c6 ) =
update key
flags
next
(ConcEquipMsg
(Comp.Dropdown.SetSelection
(item.concEquipment
|> Maybe.map List.singleton
|> Maybe.withDefault []
)
)
)
m5
proposalCmd =
if item.state == "created" then
Api.getItemProposals flags item.id GetProposalResp
else
Cmd.none
in
( { m6
| item = item
, nameModel = item.name
, notesModel = item.notes
, notesField = ViewNotes
, itemDate = item.itemDate
, dueDate = item.dueDate
, visibleAttach = 0
}
, Cmd.batch
[ c1
, c2
, c3
, c4
, c5
, c6
, getOptions flags
, proposalCmd
, Api.getSentMails flags item.id SentMailsResp
]
)
SetActiveAttachment pos ->
( { model | visibleAttach = pos, sentMailsOpen = False }, Cmd.none )
ToggleMenu ->
( { model | menuOpen = not model.menuOpen }, Cmd.none )
ReloadItem ->
if model.item.id == "" then
( model, Cmd.none )
else
( model, Api.itemDetail flags model.item.id GetItemResp )
TagDropdownMsg m ->
let
( m2, c2 ) =
Comp.Dropdown.update m model.tagModel
newModel =
{ model | tagModel = m2 }
save =
if isDropdownChangeMsg m then
saveTags flags newModel
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map TagDropdownMsg c2 ] )
DirDropdownMsg m ->
let
( m2, c2 ) =
Comp.Dropdown.update m model.directionModel
newModel =
{ model | directionModel = m2 }
save =
if isDropdownChangeMsg m then
setDirection flags newModel
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map DirDropdownMsg c2 ] )
OrgDropdownMsg m ->
let
( m2, c2 ) =
Comp.Dropdown.update m model.corrOrgModel
newModel =
{ model | corrOrgModel = m2 }
idref =
Comp.Dropdown.getSelected m2 |> List.head
save =
if isDropdownChangeMsg m then
setCorrOrg flags newModel idref
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map OrgDropdownMsg c2 ] )
CorrPersonMsg m ->
let
( m2, c2 ) =
Comp.Dropdown.update m model.corrPersonModel
newModel =
{ model | corrPersonModel = m2 }
idref =
Comp.Dropdown.getSelected m2 |> List.head
save =
if isDropdownChangeMsg m then
setCorrPerson flags newModel idref
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map CorrPersonMsg c2 ] )
ConcPersonMsg m ->
let
( m2, c2 ) =
Comp.Dropdown.update m model.concPersonModel
newModel =
{ model | concPersonModel = m2 }
idref =
Comp.Dropdown.getSelected m2 |> List.head
save =
if isDropdownChangeMsg m then
setConcPerson flags newModel idref
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map ConcPersonMsg c2 ] )
ConcEquipMsg m ->
let
( m2, c2 ) =
Comp.Dropdown.update m model.concEquipModel
newModel =
{ model | concEquipModel = m2 }
idref =
Comp.Dropdown.getSelected m2 |> List.head
save =
if isDropdownChangeMsg m then
setConcEquip flags newModel idref
else
Cmd.none
in
( newModel, Cmd.batch [ save, Cmd.map ConcEquipMsg c2 ] )
SetName str ->
( { model | nameModel = str }, Cmd.none )
SaveName ->
( model, setName flags model )
SetNotes str ->
( { model | notesModel = Util.Maybe.fromString str }
, Cmd.none
)
ToggleNotes ->
( { model
| notesField =
if model.notesField == ViewNotes then
HideNotes
else
ViewNotes
}
, Cmd.none
)
ToggleEditNotes ->
( { model
| notesField =
if isEditNotes model.notesField then
ViewNotes
else
EditNotes Comp.MarkdownInput.init
}
, Cmd.none
)
NotesEditMsg lm ->
case model.notesField of
EditNotes em ->
let
( lm2, str ) =
Comp.MarkdownInput.update (Maybe.withDefault "" model.notesModel) lm em
in
( { model | notesField = EditNotes lm2, notesModel = Util.Maybe.fromString str }
, Cmd.none
)
_ ->
( model, Cmd.none )
SaveNotes ->
( model, setNotes flags model )
ConfirmItem ->
( model, Api.setConfirmed flags model.item.id SaveResp )
UnconfirmItem ->
( model, Api.setUnconfirmed flags model.item.id SaveResp )
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
( newModel, setDate flags newModel newModel.itemDate )
_ ->
( { model | itemDatePicker = dp }, Cmd.none )
RemoveDate ->
( { model | itemDate = Nothing }, setDate flags model 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
( newModel, setDueDate flags newModel newModel.dueDate )
_ ->
( { model | dueDatePicker = dp }, Cmd.none )
RemoveDueDate ->
( { model | dueDate = Nothing }, setDueDate flags model Nothing )
YesNoMsg m ->
let
( cm, confirmed ) =
Comp.YesNoDimmer.update m model.deleteConfirm
cmd =
if confirmed then
Api.deleteItem flags model.item.id DeleteResp
else
Cmd.none
in
( { model | deleteConfirm = cm }, cmd )
RequestDelete ->
update key flags next (YesNoMsg Comp.YesNoDimmer.activate) model
SetCorrOrgSuggestion idname ->
( model, setCorrOrg flags model (Just idname) )
SetCorrPersonSuggestion idname ->
( model, setCorrPerson flags model (Just idname) )
SetConcPersonSuggestion idname ->
( model, setConcPerson flags model (Just idname) )
SetConcEquipSuggestion idname ->
( model, setConcEquip flags model (Just idname) )
SetItemDateSuggestion date ->
( model, setDate flags model (Just date) )
SetDueDateSuggestion date ->
( model, setDueDate flags model (Just date) )
GetTagsResp (Ok tags) ->
let
tagList =
Comp.Dropdown.SetOptions tags.items
( m1, c1 ) =
update key flags next (TagDropdownMsg tagList) model
in
( m1, c1 )
GetTagsResp (Err _) ->
( model, Cmd.none )
GetOrgResp (Ok orgs) ->
let
opts =
Comp.Dropdown.SetOptions orgs.items
in
update key flags next (OrgDropdownMsg opts) model
GetOrgResp (Err _) ->
( model, Cmd.none )
GetPersonResp (Ok ps) ->
let
opts =
Comp.Dropdown.SetOptions ps.items
( m1, c1 ) =
update key flags next (CorrPersonMsg opts) model
( m2, c2 ) =
update key flags next (ConcPersonMsg opts) m1
in
( m2, Cmd.batch [ c1, c2 ] )
GetPersonResp (Err _) ->
( model, Cmd.none )
GetEquipResp (Ok equips) ->
let
opts =
Comp.Dropdown.SetOptions
(List.map (\e -> IdName e.id e.name)
equips.items
)
in
update key flags next (ConcEquipMsg opts) model
GetEquipResp (Err _) ->
( model, Cmd.none )
SaveResp (Ok res) ->
if res.success then
( model, Api.itemDetail flags model.item.id GetItemResp )
else
( model, Cmd.none )
SaveResp (Err _) ->
( model, Cmd.none )
DeleteResp (Ok res) ->
if res.success then
case next of
Just id ->
( model, Page.set key (ItemDetailPage id) )
Nothing ->
( model, Page.set key HomePage )
else
( model, Cmd.none )
DeleteResp (Err _) ->
( model, Cmd.none )
GetItemResp (Ok item) ->
update key flags next (SetItem item) model
GetItemResp (Err _) ->
( model, Cmd.none )
GetProposalResp (Ok ip) ->
( { model | itemProposals = ip }, Cmd.none )
GetProposalResp (Err _) ->
( model, Cmd.none )
ItemMailMsg m ->
let
( im, ic, fa ) =
Comp.ItemMail.update flags m model.itemMail
in
case fa of
Comp.ItemMail.FormNone ->
( { model | itemMail = im }, Cmd.map ItemMailMsg ic )
Comp.ItemMail.FormCancel ->
( { model
| itemMail = Comp.ItemMail.clear im
, mailOpen = False
, mailSendResult = Nothing
}
, Cmd.map ItemMailMsg ic
)
Comp.ItemMail.FormSend sm ->
let
mail =
{ item = model.item.id
, mail = sm.mail
, conn = sm.conn
}
in
( { model | mailSending = True }
, Cmd.batch
[ Cmd.map ItemMailMsg ic
, Api.sendMail flags mail SendMailResp
]
)
ToggleMail ->
let
newOpen =
not model.mailOpen
sendResult =
if newOpen then
model.mailSendResult
else
Nothing
in
( { model
| mailOpen = newOpen
, mailSendResult = sendResult
}
, Cmd.none
)
SendMailResp (Ok br) ->
let
mm =
if br.success then
Comp.ItemMail.clear model.itemMail
else
model.itemMail
in
( { model
| itemMail = mm
, mailSending = False
, mailSendResult = Just br
}
, if br.success then
Api.itemDetail flags model.item.id GetItemResp
else
Cmd.none
)
SendMailResp (Err err) ->
let
errmsg =
Util.Http.errorToString err
in
( { model
| mailSendResult = Just (BasicResult False errmsg)
, mailSending = False
}
, Cmd.none
)
SentMailsMsg m ->
let
sm =
Comp.SentMails.update m model.sentMails
in
( { model | sentMails = sm }, Cmd.none )
ToggleSentMails ->
( { model | sentMailsOpen = not model.sentMailsOpen, visibleAttach = -1 }, Cmd.none )
SentMailsResp (Ok list) ->
let
sm =
Comp.SentMails.initMails list.items
in
( { model | sentMails = sm }, Cmd.none )
SentMailsResp (Err _) ->
( model, Cmd.none )
AttachMetaClick id ->
case Dict.get id model.attachMeta of
Just _ ->
( { model | attachMetaOpen = not model.attachMetaOpen }
, Cmd.none
)
Nothing ->
let
( am, ac ) =
Comp.AttachmentMeta.init flags id
nextMeta =
Dict.insert id am model.attachMeta
in
( { model | attachMeta = nextMeta, attachMetaOpen = True }
, Cmd.map (AttachMetaMsg id) ac
)
AttachMetaMsg id lmsg ->
case Dict.get id model.attachMeta of
Just cm ->
let
am =
Comp.AttachmentMeta.update lmsg cm
in
( { model | attachMeta = Dict.insert id am model.attachMeta }
, Cmd.none
)
Nothing ->
( model, Cmd.none )
-- view
actionInputDatePicker : DatePicker.Settings
actionInputDatePicker =
let
ds =
Comp.DatePicker.defaultSettings
in
{ ds | containerClassList = [ ( "ui action input", True ) ] }
view : { prev : Maybe String, next : Maybe String } -> Model -> Html Msg
view inav model =
div []
[ renderItemInfo model
, div
[ classList
[ ( "ui ablue-comp menu", True )
, ( "top attached", model.mailOpen )
]
]
[ a [ class "item", Page.href HomePage ]
[ i [ class "arrow left icon" ] []
]
, 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
[ 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
[ classList
[ ( "toggle item", True )
, ( "active", model.menuOpen )
]
, title "Edit item"
, onClick ToggleMenu
, href ""
]
[ i [ class "edit icon" ] []
]
, a
[ classList
[ ( "toggle item", True )
, ( "active", model.mailOpen )
]
, title "Send Mail"
, onClick ToggleMail
, href "#"
]
[ i [ class "mail outline icon" ] []
]
]
, renderMailForm model
, div [ class "ui grid" ]
[ Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
, div
[ classList
[ ( "four wide column", True )
, ( "invisible", not model.menuOpen )
]
]
(if model.menuOpen then
renderEditMenu model
else
[]
)
, div
[ classList
[ ( "twelve", model.menuOpen )
, ( "sixteen", not model.menuOpen )
, ( "wide column", True )
]
]
<|
List.concat
[ renderNotes model
, [ renderAttachmentsTabMenu model
]
, renderAttachmentsTabBody model
, renderIdInfo model
]
]
]
renderIdInfo : Model -> List (Html Msg)
renderIdInfo model =
[ div [ class "ui center aligned container" ]
[ span [ class "small-info" ]
[ text model.item.id
, text " "
, text "Created: "
, Util.Time.formatDateTime model.item.created |> text
, text " "
, text "Updated: "
, Util.Time.formatDateTime model.item.updated |> text
]
]
]
renderNotes : Model -> List (Html Msg)
renderNotes model =
case model.notesField of
HideNotes ->
case model.item.notes of
Nothing ->
[]
Just _ ->
[ div [ class "ui segment" ]
[ a
[ class "ui top left attached label"
, onClick ToggleNotes
, href "#"
]
[ i [ class "eye icon" ] []
, text "Show notes"
]
]
]
ViewNotes ->
case model.item.notes of
Nothing ->
[]
Just str ->
[ div [ class "ui segment" ]
[ Markdown.toHtml [ class "item-notes" ] str
, a
[ class "ui left corner label"
, onClick ToggleNotes
, href "#"
]
[ i [ class "eye slash icon" ] []
]
, a
[ class "ui right corner label"
, onClick ToggleEditNotes
, href "#"
]
[ i [ class "edit icon" ] []
]
]
]
EditNotes mm ->
[ div [ class "ui segment" ]
[ Html.map NotesEditMsg (Comp.MarkdownInput.view (Maybe.withDefault "" model.notesModel) mm)
, div [ class "ui secondary menu" ]
[ a
[ class "link item"
, href "#"
, onClick SaveNotes
]
[ i [ class "save outline icon" ] []
, text "Save"
]
, a
[ class "link item"
, href "#"
, onClick ToggleEditNotes
]
[ i [ class "cancel icon" ] []
, text "Cancel"
]
]
]
]
attachmentVisible : Model -> Int -> Bool
attachmentVisible model pos =
if model.visibleAttach >= List.length model.item.attachments then
pos == 0
else
model.visibleAttach == pos
renderAttachmentsTabMenu : Model -> Html Msg
renderAttachmentsTabMenu model =
let
mailTab =
if Comp.SentMails.isEmpty model.sentMails then
[]
else
[ div
[ classList
[ ( "right item", True )
, ( "active", model.sentMailsOpen )
]
, onClick ToggleSentMails
]
[ text "E-Mails"
]
]
in
div [ class "ui top attached tabular menu" ]
(List.indexedMap
(\pos ->
\el ->
a
[ classList
[ ( "item", True )
, ( "active", attachmentVisible model pos )
]
, title (Maybe.withDefault "No Name" el.name)
, href ""
, onClick (SetActiveAttachment pos)
]
[ Maybe.map (Util.String.ellipsis 20) el.name
|> Maybe.withDefault "No Name"
|> text
]
)
model.item.attachments
++ mailTab
)
renderAttachmentView : Model -> Int -> Attachment -> Html Msg
renderAttachmentView model pos attach =
let
fileUrl =
"/api/v1/sec/attachment/" ++ attach.id
attachName =
Maybe.withDefault "No name" attach.name
hasArchive =
List.map .id model.item.archives
|> List.member attach.id
in
div
[ classList
[ ( "ui attached tab segment", True )
, ( "active", attachmentVisible model pos )
]
]
[ div [ class "ui small secondary menu" ]
[ div [ class "horizontally fitted item" ]
[ i [ class "file outline icon" ] []
, text attachName
, text " ("
, text (Util.Size.bytesReadable Util.Size.B (toFloat attach.size))
, text ")"
]
, div [ class "right menu" ]
[ a
[ classList
[ ( "item", True )
, ( "invisible", not hasArchive )
]
, title "Download the original archive file."
, href (fileUrl ++ "/archive")
, target "_new"
]
[ i [ class "file archive outline icon" ] []
]
, a
[ classList
[ ( "item", True )
, ( "disabled", not attach.converted )
]
, title
(if attach.converted then
case Util.List.find (\s -> s.id == attach.id) model.item.sources of
Just src ->
"Goto original: "
++ Maybe.withDefault "<noname>" src.name
Nothing ->
"Goto original file"
else
"This is the original file"
)
, href (fileUrl ++ "/original")
, target "_new"
]
[ i [ class "external square alternate icon" ] []
]
, a
[ classList
[ ( "toggle item", True )
, ( "active", isAttachMetaOpen model attach.id )
]
, title "Show extracted data"
, onClick (AttachMetaClick attach.id)
, href "#"
]
[ i [ class "info icon" ] []
]
, a
[ class "item"
, title "Download PDF to disk"
, download attachName
, href fileUrl
]
[ i [ class "download icon" ] []
]
]
]
, div
[ classList
[ ( "ui 4:3 embed doc-embed", True )
, ( "invisible hidden", isAttachMetaOpen model attach.id )
]
]
[ iframe
[ src (fileUrl ++ "/view")
]
[]
]
, div
[ classList
[ ( "ui basic segment", True )
, ( "invisible hidden", not (isAttachMetaOpen model attach.id) )
]
]
[ case Dict.get attach.id model.attachMeta of
Just am ->
Html.map (AttachMetaMsg attach.id)
(Comp.AttachmentMeta.view am)
Nothing ->
span [] []
]
]
isAttachMetaOpen : Model -> String -> Bool
isAttachMetaOpen model id =
model.attachMetaOpen && (Dict.get id model.attachMeta /= Nothing)
renderAttachmentsTabBody : Model -> List (Html Msg)
renderAttachmentsTabBody model =
let
mailTab =
if Comp.SentMails.isEmpty model.sentMails then
[]
else
[ div
[ classList
[ ( "ui attached tab segment", True )
, ( "active", model.sentMailsOpen )
]
]
[ h3 [ class "ui header" ]
[ text "Sent E-Mails"
]
, Html.map SentMailsMsg (Comp.SentMails.view model.sentMails)
]
]
in
List.indexedMap (renderAttachmentView model) model.item.attachments
++ mailTab
renderItemInfo : Model -> Html Msg
renderItemInfo model =
let
date =
div
[ class "item"
, title "Item Date"
]
[ Maybe.withDefault model.item.created model.item.itemDate
|> Util.Time.formatDate
|> text
]
duedate =
div
[ class "item"
, title "Due Date"
]
[ i [ class "bell icon" ] []
, Maybe.map Util.Time.formatDate model.item.dueDate
|> Maybe.withDefault ""
|> text
]
corr =
div
[ class "item"
, title "Correspondent"
]
[ i [ class "envelope outline icon" ] []
, List.filterMap identity [ model.item.corrOrg, model.item.corrPerson ]
|> List.map .name
|> String.join ", "
|> Util.String.withDefault "(None)"
|> text
]
conc =
div
[ class "item"
, title "Concerning"
]
[ i [ class "comment outline icon" ] []
, List.filterMap identity [ model.item.concPerson, model.item.concEquipment ]
|> List.map .name
|> String.join ", "
|> Util.String.withDefault "(None)"
|> text
]
src =
div
[ class "item"
, title "Source"
]
[ text model.item.source
]
in
div [ class "ui fluid container" ]
(h2
[ class "ui header"
]
[ i
[ class (Data.Direction.iconFromString model.item.direction)
, title model.item.direction
]
[]
, div [ class "content" ]
[ text model.item.name
, div
[ classList
[ ( "ui teal label", True )
, ( "invisible", model.item.state /= "created" )
]
]
[ text "New!"
]
, div [ class "sub header" ]
[ div [ class "ui horizontal bulleted list" ] <|
List.append
[ date
, corr
, conc
, src
]
(if Util.Maybe.isEmpty model.item.dueDate then
[]
else
[ duedate ]
)
]
]
]
:: renderTags model
)
renderTags : Model -> List (Html Msg)
renderTags model =
case model.item.tags of
[] ->
[]
_ ->
[ div [ class "ui right aligned fluid container" ] <|
List.map
(\t ->
div
[ classList
[ ( "ui tag label", True )
, ( "blue", Util.Maybe.nonEmpty t.category )
]
]
[ text t.name
]
)
model.item.tags
]
renderEditMenu : Model -> List (Html Msg)
renderEditMenu model =
[ renderEditButtons model
, renderEditForm model
]
renderEditButtons : Model -> Html Msg
renderEditButtons model =
div [ class "ui top attached right aligned segment" ]
[ button
[ classList
[ ( "ui primary button", True )
, ( "invisible", model.item.state /= "created" )
]
, onClick ConfirmItem
]
[ i [ class "check icon" ] []
, text "Confirm"
]
, button
[ classList
[ ( "ui primary button", True )
, ( "invisible", model.item.state /= "confirmed" )
]
, onClick UnconfirmItem
]
[ i [ class "eye slash outline icon" ] []
, text "Unconfirm"
]
, button [ class "ui negative button", onClick RequestDelete ]
[ i [ class "trash icon" ] []
, text "Delete"
]
]
renderEditForm : Model -> Html Msg
renderEditForm model =
div [ class "ui attached segment" ]
[ div [ class "ui form" ]
[ div [ class "field" ]
[ label []
[ i [ class "tags icon" ] []
, text "Tags"
]
, Html.map TagDropdownMsg (Comp.Dropdown.view model.tagModel)
]
, div [ class " field" ]
[ 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" ] []
]
]
]
, div [ class "field" ]
[ label [] [ text "Direction" ]
, Html.map DirDropdownMsg (Comp.Dropdown.view model.directionModel)
]
, div [ class " field" ]
[ label [] [ 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" ] []
]
]
, renderItemDateSuggestions model
]
, div [ class " field" ]
[ label [] [ 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" ] [] ]
]
, renderDueDateSuggestions model
]
, h4 [ class "ui dividing header" ]
[ i [ class "tiny envelope outline icon" ] []
, text "Correspondent"
]
, div [ class "field" ]
[ label [] [ text "Organization" ]
, Html.map OrgDropdownMsg (Comp.Dropdown.view model.corrOrgModel)
, renderOrgSuggestions model
]
, div [ class "field" ]
[ label [] [ text "Person" ]
, Html.map CorrPersonMsg (Comp.Dropdown.view model.corrPersonModel)
, renderCorrPersonSuggestions model
]
, h4 [ class "ui dividing header" ]
[ i [ class "tiny comment outline icon" ] []
, text "Concerning"
]
, div [ class "field" ]
[ label [] [ text "Person" ]
, Html.map ConcPersonMsg (Comp.Dropdown.view model.concPersonModel)
, renderConcPersonSuggestions model
]
, div [ class "field" ]
[ label [] [ text "Equipment" ]
, Html.map ConcEquipMsg (Comp.Dropdown.view model.concEquipModel)
, renderConcEquipSuggestions model
]
, h4 [ class "ui dividing header" ]
[ i [ class "tiny edit icon" ] []
, div [ class "content" ]
[ text "Notes"
]
]
, div [ class "field" ]
[ div [ class "ui input" ]
[ button [ class "ui basic primary fluid button", onClick ToggleEditNotes ]
[ i [ class "edit outline icon" ] []
, text "Toggle Notes Form"
]
]
]
]
]
renderSuggestions : Model -> (a -> String) -> List a -> (a -> Msg) -> Html Msg
renderSuggestions model mkName idnames tagger =
div
[ classList
[ ( "ui secondary vertical menu", True )
, ( "invisible", model.item.state /= "created" )
]
]
[ div [ class "item" ]
[ div [ class "header" ]
[ text "Suggestions"
]
, div [ class "menu" ] <|
(idnames
|> List.take 5
|> List.map (\p -> a [ class "item", href "", onClick (tagger p) ] [ text (mkName p) ])
)
]
]
renderOrgSuggestions : Model -> Html Msg
renderOrgSuggestions model =
renderSuggestions model
.name
(List.take 5 model.itemProposals.corrOrg)
SetCorrOrgSuggestion
renderCorrPersonSuggestions : Model -> Html Msg
renderCorrPersonSuggestions model =
renderSuggestions model
.name
(List.take 5 model.itemProposals.corrPerson)
SetCorrPersonSuggestion
renderConcPersonSuggestions : Model -> Html Msg
renderConcPersonSuggestions model =
renderSuggestions model
.name
(List.take 5 model.itemProposals.concPerson)
SetConcPersonSuggestion
renderConcEquipSuggestions : Model -> Html Msg
renderConcEquipSuggestions model =
renderSuggestions model
.name
(List.take 5 model.itemProposals.concEquipment)
SetConcEquipSuggestion
renderItemDateSuggestions : Model -> Html Msg
renderItemDateSuggestions model =
renderSuggestions model
Util.Time.formatDate
(List.take 5 model.itemProposals.itemDate)
SetItemDateSuggestion
renderDueDateSuggestions : Model -> Html Msg
renderDueDateSuggestions model =
renderSuggestions model
Util.Time.formatDate
(List.take 5 model.itemProposals.dueDate)
SetDueDateSuggestion
renderMailForm : Model -> Html Msg
renderMailForm model =
div
[ classList
[ ( "ui bottom attached segment", True )
, ( "invisible hidden", not model.mailOpen )
]
]
[ div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.mailSending )
]
]
[ div [ class "ui text loader" ]
[ text "Sending "
]
]
, h4 [ class "ui header" ]
[ text "Send this item via E-Mail"
]
, Html.map ItemMailMsg (Comp.ItemMail.view model.itemMail)
, div
[ classList
[ ( "ui message", True )
, ( "error"
, Maybe.map .success model.mailSendResult
|> Maybe.map not
|> Maybe.withDefault False
)
, ( "success"
, Maybe.map .success model.mailSendResult
|> Maybe.withDefault False
)
, ( "invisible hidden", model.mailSendResult == Nothing )
]
]
[ Maybe.map .message model.mailSendResult
|> Maybe.withDefault ""
|> text
]
]