Using elm-format for all files

This commit is contained in:
Eike Kettner
2019-12-29 21:55:12 +01:00
parent 546f1a6ee3
commit 2001cca88b
84 changed files with 7668 additions and 5079 deletions

View File

@ -1,32 +1,36 @@
module Comp.AddressForm exposing ( Model
, emptyModel
, Msg(..)
, view
, update
, getAddress)
module Comp.AddressForm exposing
( Model
, Msg(..)
, emptyModel
, getAddress
, update
, view
)
import Api.Model.Address exposing (Address)
import Comp.Dropdown
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
import Data.Flags exposing (Flags)
import Api.Model.Address exposing (Address)
import Comp.Dropdown
import Util.List
type alias Model =
{ address: Address
, street: String
, zip: String
, city: String
, country: Comp.Dropdown.Model Country
{ address : Address
, street : String
, zip : String
, city : String
, country : Comp.Dropdown.Model Country
}
type alias Country =
{ code: String
, label: String
{ code : String
, label : String
}
countries: List Country
countries : List Country
countries =
[ Country "DE" "Germany"
, Country "CH" "Switzerland"
@ -35,22 +39,24 @@ countries =
, Country "AU" "Austria"
]
emptyModel: Model
emptyModel : Model
emptyModel =
{ address = Api.Model.Address.empty
, street = ""
, zip = ""
, city = ""
, country = Comp.Dropdown.makeSingleList
{ makeOption = \c -> { value = c.code, text = c.label }
, placeholder = "Select Country"
, options = countries
, selected = Nothing
}
, country =
Comp.Dropdown.makeSingleList
{ makeOption = \c -> { value = c.code, text = c.label }
, placeholder = "Select Country"
, options = countries
, selected = Nothing
}
}
getAddress: Model -> Address
getAddress : Model -> Address
getAddress model =
{ street = model.street
, zip = model.zip
@ -58,6 +64,7 @@ getAddress model =
, country = Comp.Dropdown.getSelected model.country |> List.head |> Maybe.map .code |> Maybe.withDefault ""
}
type Msg
= SetStreet String
| SetCity String
@ -65,65 +72,80 @@ type Msg
| SetAddress Address
| CountryMsg (Comp.Dropdown.Msg Country)
update: Msg -> Model -> (Model, Cmd Msg)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
SetAddress a ->
let
selection = Util.List.find (\c -> c.code == a.country) countries
|> Maybe.map List.singleton
|> Maybe.withDefault []
(m2, c2) = Comp.Dropdown.update (Comp.Dropdown.SetSelection selection) model.country
selection =
Util.List.find (\c -> c.code == a.country) countries
|> Maybe.map List.singleton
|> Maybe.withDefault []
( m2, c2 ) =
Comp.Dropdown.update (Comp.Dropdown.SetSelection selection) model.country
in
({model | address = a, street = a.street, city = a.city, zip = a.zip, country = m2 }, Cmd.map CountryMsg c2)
( { model | address = a, street = a.street, city = a.city, zip = a.zip, country = m2 }, Cmd.map CountryMsg c2 )
SetStreet n ->
({model | street = n}, Cmd.none)
( { model | street = n }, Cmd.none )
SetCity c ->
({model | city = c }, Cmd.none)
( { model | city = c }, Cmd.none )
SetZip z ->
({model | zip = z }, Cmd.none)
( { model | zip = z }, Cmd.none )
CountryMsg m ->
let
(m1, c1) = Comp.Dropdown.update m model.country
( m1, c1 ) =
Comp.Dropdown.update m model.country
in
({model | country = m1}, Cmd.map CountryMsg c1)
( { model | country = m1 }, Cmd.map CountryMsg c1 )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div [class "ui form"]
[div [class "field"
]
[label [][text "Street"]
,input [type_ "text"
,onInput SetStreet
,placeholder "Street"
,value model.street
][]
]
,div [class "field"
]
[label [][text "Zip Code"]
,input [type_ "text"
,onInput SetZip
,placeholder "Zip"
,value model.zip
][]
]
,div [class "field"
]
[label [][text "City"]
,input [type_ "text"
,onInput SetCity
,placeholder "City"
,value model.city
][]
]
,div [class "field"]
[label [][text "Country"]
,Html.map CountryMsg (Comp.Dropdown.view model.country)
]
div [ class "ui form" ]
[ div
[ class "field"
]
[ label [] [ text "Street" ]
, input
[ type_ "text"
, onInput SetStreet
, placeholder "Street"
, value model.street
]
[]
]
, div
[ class "field"
]
[ label [] [ text "Zip Code" ]
, input
[ type_ "text"
, onInput SetZip
, placeholder "Zip"
, value model.zip
]
[]
]
, div
[ class "field"
]
[ label [] [ text "City" ]
, input
[ type_ "text"
, onInput SetCity
, placeholder "City"
, value model.city
]
[]
]
, div [ class "field" ]
[ label [] [ text "Country" ]
, Html.map CountryMsg (Comp.Dropdown.view model.country)
]
]

View File

@ -1,45 +1,49 @@
module Comp.ChangePasswordForm exposing (Model
,emptyModel
,Msg(..)
,update
,view
)
import Http
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput, onClick)
module Comp.ChangePasswordForm exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Api
import Api.Model.PasswordChange exposing (PasswordChange)
import Util.Http
import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.PasswordChange exposing (PasswordChange)
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput)
import Http
import Util.Http
type alias Model =
{ current: String
, newPass1: String
, newPass2: String
, showCurrent: Bool
, showPass1: Bool
, showPass2: Bool
, errors: List String
, loading: Bool
, successMsg: String
{ current : String
, newPass1 : String
, newPass2 : String
, showCurrent : Bool
, showPass1 : Bool
, showPass2 : Bool
, errors : List String
, loading : Bool
, successMsg : String
}
emptyModel: Model
emptyModel : Model
emptyModel =
validateModel
{ current = ""
, newPass1 = ""
, newPass2 = ""
, showCurrent = False
, showPass1 = False
, showPass2 = False
, errors = []
, loading = False
, successMsg = ""
}
{ current = ""
, newPass1 = ""
, newPass2 = ""
, showCurrent = False
, showPass1 = False
, showPass2 = False
, errors = []
, loading = False
, successMsg = ""
}
type Msg
= SetCurrent String
@ -52,147 +56,205 @@ type Msg
| SubmitResp (Result Http.Error BasicResult)
validate: Model -> List String
validate : Model -> List String
validate model =
List.concat
[ if model.newPass1 /= "" && model.newPass2 /= "" && model.newPass1 /= model.newPass2
then ["New passwords do not match."]
else []
, if model.newPass1 == "" || model.newPass2 == "" || model.current == ""
then ["Please fill in required fields."]
else []
[ if model.newPass1 /= "" && model.newPass2 /= "" && model.newPass1 /= model.newPass2 then
[ "New passwords do not match." ]
else
[]
, if model.newPass1 == "" || model.newPass2 == "" || model.current == "" then
[ "Please fill in required fields." ]
else
[]
]
validateModel: Model -> Model
validateModel : Model -> Model
validateModel model =
let
err = validate model
err =
validate model
in
{model | errors = err, successMsg = if err == [] then model.successMsg else "" }
{ model
| errors = err
, successMsg =
if err == [] then
model.successMsg
else
""
}
-- Update
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetCurrent s ->
(validateModel {model | current = s}, Cmd.none)
( validateModel { model | current = s }, Cmd.none )
SetNew1 s ->
(validateModel {model | newPass1 = s}, Cmd.none)
( validateModel { model | newPass1 = s }, Cmd.none )
SetNew2 s ->
(validateModel {model | newPass2 = s}, Cmd.none)
( validateModel { model | newPass2 = s }, Cmd.none )
ToggleShowCurrent ->
({model | showCurrent = not model.showCurrent}, Cmd.none)
( { model | showCurrent = not model.showCurrent }, Cmd.none )
ToggleShowPass1 ->
({model | showPass1 = not model.showPass1}, Cmd.none)
( { model | showPass1 = not model.showPass1 }, Cmd.none )
ToggleShowPass2 ->
({model | showPass2 = not model.showPass2}, Cmd.none)
( { model | showPass2 = not model.showPass2 }, Cmd.none )
Submit ->
let
valid = validate model
cp = PasswordChange model.current model.newPass1
valid =
validate model
cp =
PasswordChange model.current model.newPass1
in
if List.isEmpty valid then
({model | loading = True, errors = [], successMsg = ""}, Api.changePassword flags cp SubmitResp)
else
(model, Cmd.none)
if List.isEmpty valid then
( { model | loading = True, errors = [], successMsg = "" }, Api.changePassword flags cp SubmitResp )
else
( model, Cmd.none )
SubmitResp (Ok res) ->
let
em = { emptyModel | errors = [], successMsg = "Password has been changed."}
em =
{ emptyModel | errors = [], successMsg = "Password has been changed." }
in
if res.success then
(em, Cmd.none)
else
({model | errors = [res.message], loading = False, successMsg = ""}, Cmd.none)
if res.success then
( em, Cmd.none )
else
( { model | errors = [ res.message ], loading = False, successMsg = "" }, Cmd.none )
SubmitResp (Err err) ->
let
str = Util.Http.errorToString err
str =
Util.Http.errorToString err
in
({model | errors = [str], loading = False, successMsg = ""}, Cmd.none)
( { model | errors = [ str ], loading = False, successMsg = "" }, Cmd.none )
-- View
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div [classList [("ui form", True)
,("error", List.isEmpty model.errors |> not)
,("success", model.successMsg /= "")
]
]
[div [classList [("field", True)
,("error", model.current == "")
]
]
[label [][text "Current Password*"]
,div [class "ui action input"]
[input [type_ <| if model.showCurrent then "text" else "password"
,onInput SetCurrent
,value model.current
][]
,button [class "ui icon button", onClick ToggleShowCurrent]
[i [class "eye icon"][]
]
]
]
,div [classList [("field", True)
,("error", model.newPass1 == "")
]
]
[label [][text "New Password*"]
,div [class "ui action input"]
[input [type_ <| if model.showPass1 then "text" else "password"
,onInput SetNew1
,value model.newPass1
][]
,button [class "ui icon button", onClick ToggleShowPass1]
[i [class "eye icon"][]
]
]
]
,div [classList [("field", True)
,("error", model.newPass2 == "")
]
]
[label [][text "New Password (repeat)*"]
,div [class "ui action input"]
[input [type_ <| if model.showPass2 then "text" else "password"
,onInput SetNew2
,value model.newPass2
][]
,button [class "ui icon button", onClick ToggleShowPass2]
[i [class "eye icon"][]
]
]
]
,div [class "ui horizontal divider"][]
,div [class "ui success message"]
[text model.successMsg
]
,div [class "ui error message"]
[case model.errors of
a :: [] ->
text a
_ ->
ul [class "ui list"]
(List.map (\em -> li[][text em]) model.errors)
]
,div [class "ui horizontal divider"][]
,button [class "ui primary button", onClick Submit]
[text "Submit"
]
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
div
[ classList
[ ( "ui form", True )
, ( "error", List.isEmpty model.errors |> not )
, ( "success", model.successMsg /= "" )
]
]
[ div
[ classList
[ ( "field", True )
, ( "error", model.current == "" )
]
]
[ label [] [ text "Current Password*" ]
, div [ class "ui action input" ]
[ input
[ type_ <|
if model.showCurrent then
"text"
else
"password"
, onInput SetCurrent
, value model.current
]
[]
, button [ class "ui icon button", onClick ToggleShowCurrent ]
[ i [ class "eye icon" ] []
]
]
]
, div
[ classList
[ ( "field", True )
, ( "error", model.newPass1 == "" )
]
]
[ label [] [ text "New Password*" ]
, div [ class "ui action input" ]
[ input
[ type_ <|
if model.showPass1 then
"text"
else
"password"
, onInput SetNew1
, value model.newPass1
]
[]
, button [ class "ui icon button", onClick ToggleShowPass1 ]
[ i [ class "eye icon" ] []
]
]
]
, div
[ classList
[ ( "field", True )
, ( "error", model.newPass2 == "" )
]
]
[ label [] [ text "New Password (repeat)*" ]
, div [ class "ui action input" ]
[ input
[ type_ <|
if model.showPass2 then
"text"
else
"password"
, onInput SetNew2
, value model.newPass2
]
[]
, button [ class "ui icon button", onClick ToggleShowPass2 ]
[ i [ class "eye icon" ] []
]
]
]
, div [ class "ui horizontal divider" ] []
, div [ class "ui success message" ]
[ text model.successMsg
]
, div [ class "ui error message" ]
[ case model.errors of
a :: [] ->
text a
_ ->
ul [ class "ui list" ]
(List.map (\em -> li [] [ text em ]) model.errors)
]
, div [ class "ui horizontal divider" ] []
, button [ class "ui primary button", onClick Submit ]
[ text "Submit"
]
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]

View File

@ -1,40 +1,50 @@
module Comp.ContactField exposing (Model
,emptyModel
,getContacts
,Msg(..)
,update
,view
)
module Comp.ContactField exposing
( Model
, Msg(..)
, emptyModel
, getContacts
, update
, view
)
import Api.Model.Contact exposing (Contact)
import Comp.Dropdown
import Data.ContactType exposing (ContactType)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput, onClick)
import Api.Model.Contact exposing (Contact)
import Data.ContactType exposing (ContactType)
import Comp.Dropdown
import Html.Events exposing (onClick, onInput)
type alias Model =
{ items: List Contact
, kind: Comp.Dropdown.Model ContactType
, value: String
{ items : List Contact
, kind : Comp.Dropdown.Model ContactType
, value : String
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ items = []
, kind = Comp.Dropdown.makeSingleList
{ makeOption = \ct -> { value = Data.ContactType.toString ct, text = Data.ContactType.toString ct }
, placeholder = ""
, options = Data.ContactType.all
, selected = List.head Data.ContactType.all
}
, kind =
Comp.Dropdown.makeSingleList
{ makeOption =
\ct ->
{ value = Data.ContactType.toString ct
, text = Data.ContactType.toString ct
}
, placeholder = ""
, options = Data.ContactType.all
, selected = List.head Data.ContactType.all
}
, value = ""
}
getContacts: Model -> List Contact
getContacts : Model -> List Contact
getContacts model =
List.filter (\c -> c.value /= "") model.items
type Msg
= SetValue String
| TypeMsg (Comp.Dropdown.Msg ContactType)
@ -42,76 +52,89 @@ type Msg
| Select Contact
| SetItems (List Contact)
update: Msg -> Model -> (Model, Cmd Msg)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
SetItems contacts ->
({model | items = contacts, value = "" }, Cmd.none)
( { model | items = contacts, value = "" }, Cmd.none )
SetValue v ->
({model | value = v}, Cmd.none)
( { model | value = v }, Cmd.none )
TypeMsg m ->
let
(m1, c1) = Comp.Dropdown.update m model.kind
( m1, c1 ) =
Comp.Dropdown.update m model.kind
in
({model|kind = m1}, Cmd.map TypeMsg c1)
( { model | kind = m1 }, Cmd.map TypeMsg c1 )
AddContact ->
if model.value == "" then (model, Cmd.none)
if model.value == "" then
( model, Cmd.none )
else
let
kind = Comp.Dropdown.getSelected model.kind
|> List.head
|> Maybe.map Data.ContactType.toString
|> Maybe.withDefault ""
kind =
Comp.Dropdown.getSelected model.kind
|> List.head
|> Maybe.map Data.ContactType.toString
|> Maybe.withDefault ""
in
({model| items = (Contact "" model.value kind) :: model.items, value = ""}, Cmd.none)
( { model | items = Contact "" model.value kind :: model.items, value = "" }, Cmd.none )
Select contact ->
let
newItems = List.filter (\c -> c /= contact) model.items
(m1, c1) = Data.ContactType.fromString contact.kind
|> Maybe.map (\ct -> update (TypeMsg (Comp.Dropdown.SetSelection [ct])) model)
|> Maybe.withDefault (model, Cmd.none)
in
({m1 | value = contact.value, items = newItems}, c1)
newItems =
List.filter (\c -> c /= contact) model.items
view: Model -> Html Msg
( m1, c1 ) =
Data.ContactType.fromString contact.kind
|> Maybe.map (\ct -> update (TypeMsg (Comp.Dropdown.SetSelection [ ct ])) model)
|> Maybe.withDefault ( model, Cmd.none )
in
( { m1 | value = contact.value, items = newItems }, c1 )
view : Model -> Html Msg
view model =
div []
[div [class "fields"]
[div [class "four wide field"]
[Html.map TypeMsg (Comp.Dropdown.view model.kind)
]
,div [class "twelve wide field"]
[div [class "ui action input"]
[input [type_ "text"
,onInput SetValue
,value model.value
][]
,a [class "ui button", onClick AddContact, href ""]
[text "Add"
]
]
]
]
,div [classList [("field", True)
,("invisible", List.isEmpty model.items)
[ div [ class "fields" ]
[ div [ class "four wide field" ]
[ Html.map TypeMsg (Comp.Dropdown.view model.kind)
]
, div [ class "twelve wide field" ]
[ div [ class "ui action input" ]
[ input
[ type_ "text"
, onInput SetValue
, value model.value
]
]
[div [class "ui vertical secondary fluid menu"]
(List.map renderItem model.items)
]
]
renderItem: Contact -> Html Msg
renderItem contact =
div [class "link item", onClick (Select contact) ]
[i [class "delete icon"][]
,div [class "ui blue label"]
[text contact.kind
[]
, a [ class "ui button", onClick AddContact, href "" ]
[ text "Add"
]
]
]
]
, div
[ classList
[ ( "field", True )
, ( "invisible", List.isEmpty model.items )
]
]
[ div [ class "ui vertical secondary fluid menu" ]
(List.map renderItem model.items)
]
,text contact.value
]
renderItem : Contact -> Html Msg
renderItem contact =
div [ class "link item", onClick (Select contact) ]
[ i [ class "delete icon" ] []
, div [ class "ui blue label" ]
[ text contact.kind
]
, text contact.value
]

View File

@ -1,65 +1,94 @@
module Comp.DatePicker exposing (..)
module Comp.DatePicker exposing
( Msg
, defaultSettings
, emptyModel
, endOfDay
, init
, midOfDay
, startOfDay
, update
, updateDefault
, view
, viewTime
, viewTimeDefault
)
import Html exposing (Html)
import DatePicker exposing (DatePicker, DateEvent, Settings)
import Date exposing (Date)
import Time exposing (Posix, Zone, utc, Month(..))
import DatePicker exposing (DateEvent, DatePicker, Settings)
import Html exposing (Html)
import Time exposing (Month(..), Posix, Zone, utc)
type alias Msg = DatePicker.Msg
init: (DatePicker, Cmd Msg)
type alias Msg =
DatePicker.Msg
init : ( DatePicker, Cmd Msg )
init =
DatePicker.init
emptyModel: DatePicker
emptyModel : DatePicker
emptyModel =
DatePicker.initFromDate (Date.fromCalendarDate 2019 Aug 21)
defaultSettings: Settings
defaultSettings : Settings
defaultSettings =
let
ds = DatePicker.defaultSettings
ds =
DatePicker.defaultSettings
in
{ds | changeYear = DatePicker.from 2010}
{ ds | changeYear = DatePicker.from 2010 }
update: Settings -> Msg -> DatePicker -> (DatePicker, DateEvent)
update : Settings -> Msg -> DatePicker -> ( DatePicker, DateEvent )
update settings msg model =
DatePicker.update settings msg model
updateDefault: Msg -> DatePicker -> (DatePicker, DateEvent)
updateDefault : Msg -> DatePicker -> ( DatePicker, DateEvent )
updateDefault msg model =
DatePicker.update defaultSettings msg model
view : Maybe Date -> Settings -> DatePicker -> Html Msg
view md settings model =
view md settings model =
DatePicker.view md settings model
viewTime : Maybe Int -> Settings -> DatePicker -> Html Msg
viewTime md settings model =
viewTime md settings model =
let
date = Maybe.map Time.millisToPosix md
date =
Maybe.map Time.millisToPosix md
|> Maybe.map (Date.fromPosix Time.utc)
in
view date settings model
view date settings model
viewTimeDefault: Maybe Int -> DatePicker -> Html Msg
viewTimeDefault : Maybe Int -> DatePicker -> Html Msg
viewTimeDefault md model =
viewTime md defaultSettings model
startOfDay: Date -> Int
startOfDay : Date -> Int
startOfDay date =
let
unix0 = Date.fromPosix Time.utc (Time.millisToPosix 0)
days = Date.diff Date.Days unix0 date
unix0 =
Date.fromPosix Time.utc (Time.millisToPosix 0)
days =
Date.diff Date.Days unix0 date
in
days * 24 * 60 * 60 * 1000
days * 24 * 60 * 60 * 1000
endOfDay: Date -> Int
endOfDay : Date -> Int
endOfDay date =
(startOfDay date) + ((24 * 60) - 1) * 60 * 1000
startOfDay date + ((24 * 60) - 1) * 60 * 1000
midOfDay: Date -> Int
midOfDay : Date -> Int
midOfDay date =
(startOfDay date) + (12 * 60 * 60 * 1000)
startOfDay date + (12 * 60 * 60 * 1000)

View File

@ -1,39 +1,41 @@
module Comp.Dropdown exposing ( Model
, Option
, makeModel
, makeSingle
, makeSingleList
, makeMultiple
, update
, isDropdownChangeMsg
, view
, getSelected
, Msg(..))
module Comp.Dropdown exposing
( Model
, Msg(..)
, Option
, getSelected
, isDropdownChangeMsg
, makeModel
, makeMultiple
, makeSingle
, makeSingleList
, update
, view
)
import Http
import Task
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput, onClick, onFocus, onBlur)
import Json.Decode as Decode
import Html.Events exposing (onClick, onInput)
import Simple.Fuzzy
import Util.Html exposing (onKeyUp)
import Util.List
type alias Option =
{ value: String
, text: String
{ value : String
, text : String
}
type alias Item a =
{ value: a
, option: Option
, visible: Bool
, selected: Bool
, active: Bool
{ value : a
, option : Option
, visible : Bool
, selected : Bool
, active : Bool
}
makeItem: Model a -> a -> Item a
makeItem : Model a -> a -> Item a
makeItem model val =
{ value = val
, option = model.makeOption val
@ -42,25 +44,28 @@ makeItem model val =
, active = False
}
type alias Model a =
{ multiple: Bool
, selected: List (Item a)
, available: List (Item a)
, makeOption: a -> Option
, menuOpen: Bool
, filterString: String
, labelColor: a -> String
, searchable: Int -> Bool
, placeholder: String
{ multiple : Bool
, selected : List (Item a)
, available : List (Item a)
, makeOption : a -> Option
, menuOpen : Bool
, filterString : String
, labelColor : a -> String
, searchable : Int -> Bool
, placeholder : String
}
makeModel:
{ multiple: Bool
, searchable: Int -> Bool
, makeOption: a -> Option
, labelColor: a -> String
, placeholder: String
} -> Model a
makeModel :
{ multiple : Bool
, searchable : Int -> Bool
, makeOption : a -> Option
, labelColor : a -> String
, placeholder : String
}
-> Model a
makeModel input =
{ multiple = input.multiple
, searchable = input.searchable
@ -73,10 +78,12 @@ makeModel input =
, placeholder = input.placeholder
}
makeSingle:
{ makeOption: a -> Option
, placeholder: String
} -> Model a
makeSingle :
{ makeOption : a -> Option
, placeholder : String
}
-> Model a
makeSingle opts =
makeModel
{ multiple = False
@ -86,26 +93,35 @@ makeSingle opts =
, placeholder = opts.placeholder
}
makeSingleList:
{ makeOption: a -> Option
, placeholder: String
, options: List a
, selected: Maybe a
} -> Model a
makeSingleList :
{ makeOption : a -> Option
, placeholder : String
, options : List a
, selected : Maybe a
}
-> Model a
makeSingleList opts =
let
m = makeSingle {makeOption = opts.makeOption, placeholder = opts.placeholder}
m2 = {m | available = List.map (makeItem m) opts.options}
m3 = Maybe.map (makeItem m2) opts.selected
|> Maybe.map (selectItem m2)
|> Maybe.withDefault m2
in
m3
m =
makeSingle { makeOption = opts.makeOption, placeholder = opts.placeholder }
makeMultiple:
{ makeOption: a -> Option
, labelColor: a -> String
} -> Model a
m2 =
{ m | available = List.map (makeItem m) opts.options }
m3 =
Maybe.map (makeItem m2) opts.selected
|> Maybe.map (selectItem m2)
|> Maybe.withDefault m2
in
m3
makeMultiple :
{ makeOption : a -> Option
, labelColor : a -> String
}
-> Model a
makeMultiple opts =
makeModel
{ multiple = True
@ -115,10 +131,12 @@ makeMultiple opts =
, placeholder = ""
}
getSelected: Model a -> List a
getSelected : Model a -> List a
getSelected model =
List.map .value model.selected
type Msg a
= SetOptions (List a)
| SetSelection (List a)
@ -129,265 +147,367 @@ type Msg a
| ShowMenu Bool
| KeyPress Int
getOptions: Model a -> List (Item a)
getOptions model =
if not model.multiple && isSearchable model && model.menuOpen
then List.filter .visible model.available
else List.filter (\e -> e.visible && (not e.selected)) model.available
isSearchable: Model a -> Bool
getOptions : Model a -> List (Item a)
getOptions model =
if not model.multiple && isSearchable model && model.menuOpen then
List.filter .visible model.available
else
List.filter (\e -> e.visible && not e.selected) model.available
isSearchable : Model a -> Bool
isSearchable model =
List.length model.available |> model.searchable
-- Update
deselectItem: Model a -> Item a -> Model a
deselectItem : Model a -> Item a -> Model a
deselectItem model item =
let
value = item.option.value
sel = if model.multiple then List.filter (\e -> e.option.value /= value) model.selected
else []
value =
item.option.value
show e = if e.option.value == value then {e | selected = False } else e
avail = List.map show model.available
sel =
if model.multiple then
List.filter (\e -> e.option.value /= value) model.selected
else
[]
show e =
if e.option.value == value then
{ e | selected = False }
else
e
avail =
List.map show model.available
in
{ model | selected = sel, available = avail }
{ model | selected = sel, available = avail }
selectItem: Model a -> Item a -> Model a
selectItem : Model a -> Item a -> Model a
selectItem model item =
let
value = item.option.value
sel = if model.multiple
then List.concat [ model.selected, [ item ] ]
else [ item ]
value =
item.option.value
hide e = if e.option.value == value
then {e | selected = True }
else if model.multiple then e else {e | selected = False}
avail = List.map hide model.available
sel =
if model.multiple then
List.concat [ model.selected, [ item ] ]
else
[ item ]
hide e =
if e.option.value == value then
{ e | selected = True }
else if model.multiple then
e
else
{ e | selected = False }
avail =
List.map hide model.available
in
{ model | selected = sel, available = avail }
{ model | selected = sel, available = avail }
filterOptions: String -> List (Item a) -> List (Item a)
filterOptions : String -> List (Item a) -> List (Item a)
filterOptions str list =
List.map (\e -> {e | visible = Simple.Fuzzy.match str e.option.text, active = False}) list
List.map (\e -> { e | visible = Simple.Fuzzy.match str e.option.text, active = False }) list
applyFilter: String -> Model a -> Model a
applyFilter : String -> Model a -> Model a
applyFilter str model =
{ model | filterString = str, available = filterOptions str model.available }
makeNextActive: (Int -> Int) -> Model a -> Model a
makeNextActive : (Int -> Int) -> Model a -> Model a
makeNextActive nextEl model =
let
opts = getOptions model
current = Util.List.findIndexed .active opts
next = Maybe.map Tuple.second current
|> Maybe.map nextEl
|> Maybe.andThen (Util.List.get opts)
merge item1 item2 = { item2 | active = item1.option.value == item2.option.value }
updateModel item = { model | available = List.map (merge item) model.available, menuOpen = True }
in
case next of
Just item -> updateModel item
Nothing ->
case List.head opts of
Just item -> updateModel item
Nothing -> model
opts =
getOptions model
selectActive: Model a -> Model a
current =
Util.List.findIndexed .active opts
next =
Maybe.map Tuple.second current
|> Maybe.map nextEl
|> Maybe.andThen (Util.List.get opts)
merge item1 item2 =
{ item2 | active = item1.option.value == item2.option.value }
updateModel item =
{ model | available = List.map (merge item) model.available, menuOpen = True }
in
case next of
Just item ->
updateModel item
Nothing ->
case List.head opts of
Just item ->
updateModel item
Nothing ->
model
selectActive : Model a -> Model a
selectActive model =
let
current = getOptions model |> Util.List.find .active
current =
getOptions model |> Util.List.find .active
in
case current of
Just item ->
selectItem model item |> applyFilter ""
Nothing ->
model
case current of
Just item ->
selectItem model item |> applyFilter ""
clearActive: Model a -> Model a
Nothing ->
model
clearActive : Model a -> Model a
clearActive model =
{ model | available = List.map (\e -> {e | active = False}) model.available }
{ model | available = List.map (\e -> { e | active = False }) model.available }
-- TODO enhance update function to return this info
isDropdownChangeMsg: Msg a -> Bool
isDropdownChangeMsg : Msg a -> Bool
isDropdownChangeMsg cm =
case cm of
AddItem _ -> True
RemoveItem _ -> True
AddItem _ ->
True
RemoveItem _ ->
True
KeyPress code ->
Util.Html.intToKeyCode code
|> Maybe.map (\c -> c == Util.Html.Enter)
|> Maybe.withDefault False
_ -> False
Util.Html.intToKeyCode code
|> Maybe.map (\c -> c == Util.Html.Enter)
|> Maybe.withDefault False
_ ->
False
update: Msg a -> Model a -> (Model a, Cmd (Msg a))
update : Msg a -> Model a -> ( Model a, Cmd (Msg a) )
update msg model =
case msg of
SetOptions list ->
({model | available = List.map (makeItem model) list}, Cmd.none)
( { model | available = List.map (makeItem model) list }, Cmd.none )
SetSelection list ->
let
m0 = List.foldl (\item -> \m -> deselectItem m item) model model.selected
m1 = List.map (makeItem model) list
|> List.foldl (\item -> \m -> selectItem m item) m0
m0 =
List.foldl (\item -> \m -> deselectItem m item) model model.selected
m1 =
List.map (makeItem model) list
|> List.foldl (\item -> \m -> selectItem m item) m0
in
(m1, Cmd.none)
( m1, Cmd.none )
ToggleMenu ->
({model | menuOpen = not model.menuOpen}, Cmd.none)
( { model | menuOpen = not model.menuOpen }, Cmd.none )
AddItem e ->
let
m = selectItem model e |> applyFilter ""
m =
selectItem model e |> applyFilter ""
in
({ m | menuOpen = False }, Cmd.none)
( { m | menuOpen = False }, Cmd.none )
RemoveItem e ->
let
m = deselectItem model e |> applyFilter ""
m =
deselectItem model e |> applyFilter ""
in
({ m | menuOpen = False }, Cmd.none)
( { m | menuOpen = False }, Cmd.none )
Filter str ->
let
m = applyFilter str model
m =
applyFilter str model
in
({ m | menuOpen = True}, Cmd.none)
( { m | menuOpen = True }, Cmd.none )
ShowMenu flag ->
({ model | menuOpen = flag }, Cmd.none)
( { model | menuOpen = flag }, Cmd.none )
KeyPress code ->
case Util.Html.intToKeyCode code of
Just Util.Html.Up ->
(makeNextActive (\n -> n - 1) model, Cmd.none)
( makeNextActive (\n -> n - 1) model, Cmd.none )
Just Util.Html.Down ->
(makeNextActive ((+) 1) model, Cmd.none)
( makeNextActive ((+) 1) model, Cmd.none )
Just Util.Html.Enter ->
let
m = selectActive model
m =
selectActive model
in
({m | menuOpen = False }, Cmd.none)
( { m | menuOpen = False }, Cmd.none )
_ ->
(model, Cmd.none)
( model, Cmd.none )
-- View
view: Model a -> Html (Msg a)
view : Model a -> Html (Msg a)
view model =
if model.multiple then viewMultiple model else viewSingle model
if model.multiple then
viewMultiple model
else
viewSingle model
viewSingle: Model a -> Html (Msg a)
viewSingle : Model a -> Html (Msg a)
viewSingle model =
let
renderClosed item =
div [class "message"
,style "display" "inline-block !important"
,onClick ToggleMenu
div
[ class "message"
, style "display" "inline-block !important"
, onClick ToggleMenu
]
[i [class "delete icon", onClick (RemoveItem item)][]
,text item.option.text
[ i [ class "delete icon", onClick (RemoveItem item) ] []
, text item.option.text
]
renderDefault =
[ List.head model.selected |> Maybe.map renderClosed |> Maybe.withDefault (renderPlaceholder model)
, renderMenu model
]
openSearch =
[ input [ class "search"
[ input
[ class "search"
, placeholder "Search"
, onInput Filter
, onKeyUp KeyPress
, value model.filterString
]
[]
, renderMenu model
]
in
div
[ classList
[ ( "ui search dropdown selection", True )
, ( "open", model.menuOpen )
]
]
(List.append
[ i [ class "dropdown icon", onClick ToggleMenu ] []
]
<|
if model.menuOpen && isSearchable model then
openSearch
else
renderDefault
)
viewMultiple : Model a -> Html (Msg a)
viewMultiple model =
let
renderSelectMultiple : Item a -> Html (Msg a)
renderSelectMultiple item =
div
[ classList
[ ( "ui label", True )
, ( model.labelColor item.value, True )
]
, style "display" "inline-block !important"
, onClick (RemoveItem item)
]
[ text item.option.text
, i [ class "delete icon" ] []
]
in
div
[ classList
[ ( "ui search dropdown multiple selection", True )
, ( "open", model.menuOpen )
]
]
(List.concat
[ [ i [ class "dropdown icon", onClick ToggleMenu ] []
]
, List.map renderSelectMultiple model.selected
, if isSearchable model then
[ input
[ class "search"
, placeholder "Search"
, onInput Filter
, onKeyUp KeyPress
, value model.filterString
][]
, renderMenu model
]
in
div [classList [ ("ui search dropdown selection", True)
, ("open", model.menuOpen)
]
]
(List.append [ i [class "dropdown icon", onClick ToggleMenu][]
] <|
if model.menuOpen && isSearchable model
then openSearch
else renderDefault
)
viewMultiple: Model a -> Html (Msg a)
viewMultiple model =
let
renderSelectMultiple: Item a -> Html (Msg a)
renderSelectMultiple item =
div [classList [ ("ui label", True)
, (model.labelColor item.value, True)
]
,style "display" "inline-block !important"
,onClick (RemoveItem item)
]
[]
]
[text item.option.text
,i [class "delete icon"][]
]
in
div [classList [ ("ui search dropdown multiple selection", True)
, ("open", model.menuOpen)
]
]
(List.concat
[ [i [class "dropdown icon", onClick ToggleMenu][]
]
, List.map renderSelectMultiple model.selected
, if isSearchable model then
[ input [ class "search"
, placeholder "Search"
, onInput Filter
, onKeyUp KeyPress
, value model.filterString
][]
]
else []
, [ renderMenu model
]
])
renderMenu: Model a -> Html (Msg a)
else
[]
, [ renderMenu model
]
]
)
renderMenu : Model a -> Html (Msg a)
renderMenu model =
div [classList [( "menu", True )
,( "transition visible", model.menuOpen )
]
] (getOptions model |> List.map renderOption)
div
[ classList
[ ( "menu", True )
, ( "transition visible", model.menuOpen )
]
]
(getOptions model |> List.map renderOption)
renderPlaceholder: Model a -> Html (Msg a)
renderPlaceholder : Model a -> Html (Msg a)
renderPlaceholder model =
div [classList [ ("placeholder-message", True)
, ("text", model.multiple)
]
,style "display" "inline-block !important"
,onClick ToggleMenu
div
[ classList
[ ( "placeholder-message", True )
, ( "text", model.multiple )
]
, style "display" "inline-block !important"
, onClick ToggleMenu
]
[ text model.placeholder
]
[text model.placeholder
]
renderOption: Item a -> Html (Msg a)
renderOption : Item a -> Html (Msg a)
renderOption item =
div [classList [ ("item", True)
, ("active", item.active || item.selected)
]
,onClick (AddItem item)
div
[ classList
[ ( "item", True )
, ( "active", item.active || item.selected )
]
, onClick (AddItem item)
]
[text item.option.text
[ text item.option.text
]

View File

@ -1,48 +1,57 @@
-- inspired from here: https://ellie-app.com/3T5mNms7SwKa1
module Comp.Dropzone exposing ( view
, Settings
, defaultSettings
, update
, setActive
, Model
, init
, Msg(..)
)
module Comp.Dropzone exposing
( Model
, Msg(..)
, Settings
, defaultSettings
, init
, setActive
, update
, view
)
import File exposing (File)
import File.Select
import Json.Decode as D
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Json.Decode as D
type alias State =
{ hover: Bool
, active: Bool
{ hover : Bool
, active : Bool
}
type alias Settings =
{ classList: State -> List (String, Bool)
, contentTypes: List String
{ classList : State -> List ( String, Bool )
, contentTypes : List String
}
defaultSettings: Settings
defaultSettings : Settings
defaultSettings =
{ classList = \m -> [("ui placeholder segment", True)]
{ classList = \_ -> [ ( "ui placeholder segment", True ) ]
, contentTypes = [ "application/pdf" ]
}
type alias Model =
{ state: State
, settings: Settings
{ state : State
, settings : Settings
}
init: Settings -> Model
init : Settings -> Model
init settings =
{ state = State False True
, settings = settings
}
type Msg
= DragEnter
| DragLeave
@ -50,45 +59,55 @@ type Msg
| PickFiles
| SetActive Bool
setActive: Bool -> Msg
setActive : Bool -> Msg
setActive flag =
SetActive flag
update: Msg -> Model -> (Model, Cmd Msg, List File)
update : Msg -> Model -> ( Model, Cmd Msg, List File )
update msg model =
case msg of
SetActive flag ->
let
ns = { hover = model.state.hover, active = flag }
ns =
{ hover = model.state.hover, active = flag }
in
({ model | state = ns }, Cmd.none, [])
( { model | state = ns }, Cmd.none, [] )
PickFiles ->
(model, File.Select.files model.settings.contentTypes GotFiles, [])
( model, File.Select.files model.settings.contentTypes GotFiles, [] )
DragEnter ->
let
ns = {hover = True, active = model.state.active}
ns =
{ hover = True, active = model.state.active }
in
({model| state = ns}, Cmd.none, [])
( { model | state = ns }, Cmd.none, [] )
DragLeave ->
let
ns = {hover = False, active = model.state.active}
ns =
{ hover = False, active = model.state.active }
in
({model | state = ns}, Cmd.none, [])
( { model | state = ns }, Cmd.none, [] )
GotFiles file files ->
let
ns = {hover = False, active = model.state.active}
newFiles = if model.state.active then filterMime model.settings (file :: files)
else []
ns =
{ hover = False, active = model.state.active }
newFiles =
if model.state.active then
filterMime model.settings (file :: files)
else
[]
in
({model | state = ns}, Cmd.none, newFiles)
( { model | state = ns }, Cmd.none, newFiles )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div
[ classList (model.settings.classList model.state)
@ -97,46 +116,51 @@ view model =
, hijackOn "dragleave" (D.succeed DragLeave)
, hijackOn "drop" dropDecoder
]
[div [class "ui icon header"]
[i [class "mouse pointer icon"][]
,div [class "content"]
[text "Drop files here"
,div [class "sub header"]
[text "PDF files only"
]
]
]
,div [class "ui horizontal divider"]
[text "Or"
[ div [ class "ui icon header" ]
[ i [ class "mouse pointer icon" ] []
, div [ class "content" ]
[ text "Drop files here"
, div [ class "sub header" ]
[ text "PDF files only"
]
]
]
, div [ class "ui horizontal divider" ]
[ text "Or"
]
, a
[ classList
[ ( "ui basic primary button", True )
, ( "disabled", not model.state.active )
]
, onClick PickFiles
, href ""
]
[ i [ class "folder open icon" ] []
, text "Select ..."
]
,a [classList [("ui basic primary button", True)
,("disabled", not model.state.active)
]
, onClick PickFiles
, href ""]
[i [class "folder open icon"][]
,text "Select ..."
]
]
filterMime: Settings -> List File -> List File
filterMime : Settings -> List File -> List File
filterMime settings files =
let
pred f =
List.member (File.mime f) settings.contentTypes
in
List.filter pred files
List.filter pred files
dropDecoder : D.Decoder Msg
dropDecoder =
D.at ["dataTransfer","files"] (D.oneOrMore GotFiles File.decoder)
D.at [ "dataTransfer", "files" ] (D.oneOrMore GotFiles File.decoder)
hijackOn : String -> D.Decoder msg -> Attribute msg
hijackOn event decoder =
preventDefaultOn event (D.map hijack decoder)
preventDefaultOn event (D.map hijack decoder)
hijack : msg -> (msg, Bool)
hijack : msg -> ( msg, Bool )
hijack msg =
(msg, True)
( msg, True )

View File

@ -1,62 +1,74 @@
module Comp.EquipmentForm exposing ( Model
, emptyModel
, Msg(..)
, view
, update
, isValid
, getEquipment)
module Comp.EquipmentForm exposing
( Model
, Msg(..)
, emptyModel
, getEquipment
, isValid
, update
, view
)
import Api.Model.Equipment exposing (Equipment)
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
import Data.Flags exposing (Flags)
import Api.Model.Equipment exposing (Equipment)
type alias Model =
{ equipment: Equipment
, name: String
{ equipment : Equipment
, name : String
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ equipment = Api.Model.Equipment.empty
, name = ""
}
isValid: Model -> Bool
isValid : Model -> Bool
isValid model =
model.name /= ""
getEquipment: Model -> Equipment
getEquipment : Model -> Equipment
getEquipment model =
Equipment model.equipment.id model.name model.equipment.created
type Msg
= SetName String
| SetEquipment Equipment
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetEquipment t ->
({model | equipment = t, name = t.name }, Cmd.none)
( { model | equipment = t, name = t.name }, Cmd.none )
SetName n ->
({model | name = n}, Cmd.none)
( { model | name = n }, Cmd.none )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div [class "ui form"]
[div [classList [("field", True)
,("error", not (isValid model))
]
]
[label [][text "Name*"]
,input [type_ "text"
,onInput SetName
,placeholder "Name"
,value model.name
][]
]
div [ class "ui form" ]
[ div
[ classList
[ ( "field", True )
, ( "error", not (isValid model) )
]
]
[ label [] [ text "Name*" ]
, input
[ type_ "text"
, onInput SetName
, placeholder "Name"
, value model.name
]
[]
]
]

View File

@ -1,36 +1,43 @@
module Comp.EquipmentManage exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.EquipmentManage exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Http
import Api
import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.Equipment
import Api.Model.EquipmentList exposing (EquipmentList)
import Comp.EquipmentForm
import Comp.EquipmentTable
import Comp.YesNoDimmer
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onSubmit)
import Data.Flags exposing (Flags)
import Comp.EquipmentTable
import Comp.EquipmentForm
import Comp.YesNoDimmer
import Api.Model.Equipment
import Api.Model.EquipmentList exposing (EquipmentList)
import Api.Model.BasicResult exposing (BasicResult)
import Util.Maybe
import Http
import Util.Http
import Util.Maybe
type alias Model =
{ tableModel: Comp.EquipmentTable.Model
, formModel: Comp.EquipmentForm.Model
, viewMode: ViewMode
, formError: Maybe String
, loading: Bool
, deleteConfirm: Comp.YesNoDimmer.Model
{ tableModel : Comp.EquipmentTable.Model
, formModel : Comp.EquipmentForm.Model
, viewMode : ViewMode
, formError : Maybe String
, loading : Bool
, deleteConfirm : Comp.YesNoDimmer.Model
}
type ViewMode = Table | Form
emptyModel: Model
type ViewMode
= Table
| Form
emptyModel : Model
emptyModel =
{ tableModel = Comp.EquipmentTable.emptyModel
, formModel = Comp.EquipmentForm.emptyModel
@ -40,6 +47,7 @@ emptyModel =
, deleteConfirm = Comp.YesNoDimmer.emptyModel
}
type Msg
= TableMsg Comp.EquipmentTable.Msg
| FormMsg Comp.EquipmentForm.Msg
@ -52,155 +60,210 @@ type Msg
| YesNoMsg Comp.YesNoDimmer.Msg
| RequestDelete
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
TableMsg m ->
let
(tm, tc) = Comp.EquipmentTable.update flags m model.tableModel
(m2, c2) = ({model | tableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError = if Util.Maybe.nonEmpty tm.selected then Nothing else model.formError
}
, Cmd.map TableMsg tc
)
(m3, c3) = case tm.selected of
( tm, tc ) =
Comp.EquipmentTable.update flags m model.tableModel
( m2, c2 ) =
( { model
| tableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError =
if Util.Maybe.nonEmpty tm.selected then
Nothing
else
model.formError
}
, Cmd.map TableMsg tc
)
( m3, c3 ) =
case tm.selected of
Just equipment ->
update flags (FormMsg (Comp.EquipmentForm.SetEquipment equipment)) m2
Nothing ->
(m2, Cmd.none)
( m2, Cmd.none )
in
(m3, Cmd.batch [c2, c3])
( m3, Cmd.batch [ c2, c3 ] )
FormMsg m ->
let
(m2, c2) = Comp.EquipmentForm.update flags m model.formModel
( m2, c2 ) =
Comp.EquipmentForm.update flags m model.formModel
in
({model | formModel = m2}, Cmd.map FormMsg c2)
( { model | formModel = m2 }, Cmd.map FormMsg c2 )
LoadEquipments ->
({model| loading = True}, Api.getEquipments flags EquipmentResp)
( { model | loading = True }, Api.getEquipments flags EquipmentResp )
EquipmentResp (Ok equipments) ->
let
m2 = {model|viewMode = Table, loading = False}
m2 =
{ model | viewMode = Table, loading = False }
in
update flags (TableMsg (Comp.EquipmentTable.SetEquipments equipments.items)) m2
update flags (TableMsg (Comp.EquipmentTable.SetEquipments equipments.items)) m2
EquipmentResp (Err err) ->
({model|loading = False}, Cmd.none)
EquipmentResp (Err _) ->
( { model | loading = False }, Cmd.none )
SetViewMode m ->
let
m2 = {model | viewMode = m }
m2 =
{ model | viewMode = m }
in
case m of
Table ->
update flags (TableMsg Comp.EquipmentTable.Deselect) m2
Form ->
(m2, Cmd.none)
case m of
Table ->
update flags (TableMsg Comp.EquipmentTable.Deselect) m2
Form ->
( m2, Cmd.none )
InitNewEquipment ->
let
nm = {model | viewMode = Form, formError = Nothing }
equipment = Api.Model.Equipment.empty
nm =
{ model | viewMode = Form, formError = Nothing }
equipment =
Api.Model.Equipment.empty
in
update flags (FormMsg (Comp.EquipmentForm.SetEquipment equipment)) nm
update flags (FormMsg (Comp.EquipmentForm.SetEquipment equipment)) nm
Submit ->
let
equipment = Comp.EquipmentForm.getEquipment model.formModel
valid = Comp.EquipmentForm.isValid model.formModel
in if valid then
({model|loading = True}, Api.postEquipment flags equipment SubmitResp)
else
({model|formError = Just "Please correct the errors in the form."}, Cmd.none)
equipment =
Comp.EquipmentForm.getEquipment model.formModel
valid =
Comp.EquipmentForm.isValid model.formModel
in
if valid then
( { model | loading = True }, Api.postEquipment flags equipment SubmitResp )
else
( { model | formError = Just "Please correct the errors in the form." }, Cmd.none )
SubmitResp (Ok res) ->
if res.success then
let
(m2, c2) = update flags (SetViewMode Table) model
(m3, c3) = update flags LoadEquipments m2
( m2, c2 ) =
update flags (SetViewMode Table) model
( m3, c3 ) =
update flags LoadEquipments m2
in
({m3|loading = False}, Cmd.batch [c2,c3])
( { m3 | loading = False }, Cmd.batch [ c2, c3 ] )
else
({model | formError = Just res.message, loading = False }, Cmd.none)
( { model | formError = Just res.message, loading = False }, Cmd.none )
SubmitResp (Err err) ->
({model | formError = Just (Util.Http.errorToString err), loading = False}, Cmd.none)
( { model | formError = Just (Util.Http.errorToString err), loading = False }, Cmd.none )
RequestDelete ->
update flags (YesNoMsg Comp.YesNoDimmer.activate) model
YesNoMsg m ->
let
(cm, confirmed) = Comp.YesNoDimmer.update m model.deleteConfirm
equip = Comp.EquipmentForm.getEquipment model.formModel
cmd = if confirmed then Api.deleteEquip flags equip.id SubmitResp else Cmd.none
( cm, confirmed ) =
Comp.YesNoDimmer.update m model.deleteConfirm
equip =
Comp.EquipmentForm.getEquipment model.formModel
cmd =
if confirmed then
Api.deleteEquip flags equip.id SubmitResp
else
Cmd.none
in
({model | deleteConfirm = cm}, cmd)
( { model | deleteConfirm = cm }, cmd )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
if model.viewMode == Table then viewTable model
else viewForm model
if model.viewMode == Table then
viewTable model
viewTable: Model -> Html Msg
else
viewForm model
viewTable : Model -> Html Msg
viewTable model =
div []
[button [class "ui basic button", onClick InitNewEquipment]
[i [class "plus icon"][]
,text "Create new"
]
,Html.map TableMsg (Comp.EquipmentTable.view model.tableModel)
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
[ button [ class "ui basic button", onClick InitNewEquipment ]
[ i [ class "plus icon" ] []
, text "Create new"
]
, Html.map TableMsg (Comp.EquipmentTable.view model.tableModel)
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]
viewForm: Model -> Html Msg
viewForm : Model -> Html Msg
viewForm model =
let
newEquipment = model.formModel.equipment.id == ""
newEquipment =
model.formModel.equipment.id == ""
in
Html.form [class "ui segment", onSubmit Submit]
[Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
,if newEquipment then
h3 [class "ui dividing header"]
[text "Create new equipment"
]
else
h3 [class "ui dividing header"]
[text ("Edit equipment: " ++ model.formModel.equipment.name)
,div [class "sub header"]
[text "Id: "
,text model.formModel.equipment.id
]
]
,Html.map FormMsg (Comp.EquipmentForm.view model.formModel)
,div [classList [("ui error message", True)
,("invisible", Util.Maybe.isEmpty model.formError)
]
]
[Maybe.withDefault "" model.formError |> text
]
,div [class "ui horizontal divider"][]
,button [class "ui primary button", type_ "submit"]
[text "Submit"
Html.form [ class "ui segment", onSubmit Submit ]
[ Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
, if newEquipment then
h3 [ class "ui dividing header" ]
[ text "Create new equipment"
]
,a [class "ui secondary button", onClick (SetViewMode Table), href ""]
[text "Cancel"
else
h3 [ class "ui dividing header" ]
[ text ("Edit equipment: " ++ model.formModel.equipment.name)
, div [ class "sub header" ]
[ text "Id: "
, text model.formModel.equipment.id
]
]
, Html.map FormMsg (Comp.EquipmentForm.view model.formModel)
, div
[ classList
[ ( "ui error message", True )
, ( "invisible", Util.Maybe.isEmpty model.formError )
]
,if not newEquipment then
a [class "ui right floated red button", href "", onClick RequestDelete]
[text "Delete"]
else
span[][]
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
]
]
[ Maybe.withDefault "" model.formError |> text
]
, div [ class "ui horizontal divider" ] []
, button [ class "ui primary button", type_ "submit" ]
[ text "Submit"
]
, a [ class "ui secondary button", onClick (SetViewMode Table), href "" ]
[ text "Cancel"
]
, if not newEquipment then
a [ class "ui right floated red button", href "", onClick RequestDelete ]
[ text "Delete" ]
else
span [] []
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]

View File

@ -1,62 +1,70 @@
module Comp.EquipmentTable exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.EquipmentTable exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Api.Model.Equipment exposing (Equipment)
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Data.Flags exposing (Flags)
import Api.Model.Equipment exposing (Equipment)
type alias Model =
{ equips: List Equipment
, selected: Maybe Equipment
{ equips : List Equipment
, selected : Maybe Equipment
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ equips = []
, selected = Nothing
}
type Msg
= SetEquipments (List Equipment)
| Select Equipment
| Deselect
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetEquipments list ->
({model | equips = list, selected = Nothing }, Cmd.none)
( { model | equips = list, selected = Nothing }, Cmd.none )
Select equip ->
({model | selected = Just equip}, Cmd.none)
( { model | selected = Just equip }, Cmd.none )
Deselect ->
({model | selected = Nothing}, Cmd.none)
( { model | selected = Nothing }, Cmd.none )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
table [class "ui selectable table"]
[thead []
[tr []
[th [][text "Name"]
]
]
,tbody []
table [ class "ui selectable table" ]
[ thead []
[ tr []
[ th [] [ text "Name" ]
]
]
, tbody []
(List.map (renderEquipmentLine model) model.equips)
]
renderEquipmentLine: Model -> Equipment -> Html Msg
renderEquipmentLine : Model -> Equipment -> Html Msg
renderEquipmentLine model equip =
tr [classList [("active", model.selected == Just equip)]
,onClick (Select equip)
]
[td []
[text equip.name
]
tr
[ classList [ ( "active", model.selected == Just equip ) ]
, onClick (Select equip)
]
[ td []
[ text equip.name
]
]

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +1,41 @@
module Comp.ItemList exposing (Model
, emptyModel
, Msg(..)
, prevItem
, nextItem
, update
, view)
module Comp.ItemList exposing
( Model
, Msg(..)
, emptyModel
, nextItem
, prevItem
, update
, view
)
import Set exposing (Set)
import Api.Model.ItemLight exposing (ItemLight)
import Api.Model.ItemLightGroup exposing (ItemLightGroup)
import Api.Model.ItemLightList exposing (ItemLightList)
import Data.Direction
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Api.Model.ItemLightList exposing (ItemLightList)
import Api.Model.ItemLightGroup exposing (ItemLightGroup)
import Api.Model.ItemLight exposing (ItemLight)
import Data.Flags exposing (Flags)
import Data.Direction
import Set exposing (Set)
import Util.List
import Util.Maybe
import Util.String
import Util.Time
import Util.Maybe
type alias Model =
{ results: ItemLightList
, openGroups: Set String
{ results : ItemLightList
, openGroups : Set String
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ results = Api.Model.ItemLightList.empty
, openGroups = Set.empty
}
type Msg
= SetResults ItemLightList
| ToggleGroupState ItemLightGroup
@ -38,198 +43,243 @@ type Msg
| ExpandAll
| SelectItem ItemLight
nextItem: Model -> String -> Maybe ItemLight
nextItem : Model -> String -> Maybe ItemLight
nextItem model id =
List.concatMap .items model.results.groups
|> Util.List.findNext (\i -> i.id == id)
prevItem: Model -> String -> Maybe ItemLight
prevItem : Model -> String -> Maybe ItemLight
prevItem model id =
List.concatMap .items model.results.groups
|> Util.List.findPrev (\i -> i.id == id)
openAllGroups: Model -> Set String
openAllGroups : Model -> Set String
openAllGroups model =
List.foldl
(\g -> \set -> Set.insert g.name set)
model.openGroups
model.results.groups
update: Flags -> Msg -> Model -> (Model, Cmd Msg, Maybe ItemLight)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Maybe ItemLight )
update flags msg model =
case msg of
SetResults list ->
let
newModel = { model | results = list, openGroups = Set.empty }
newModel =
{ model | results = list, openGroups = Set.empty }
in
({newModel|openGroups = openAllGroups newModel}, Cmd.none, Nothing)
( { newModel | openGroups = openAllGroups newModel }, Cmd.none, Nothing )
ToggleGroupState group ->
let
m2 = if isGroupOpen model group then closeGroup model group
else openGroup model group
m2 =
if isGroupOpen model group then
closeGroup model group
else
openGroup model group
in
(m2, Cmd.none, Nothing)
( m2, Cmd.none, Nothing )
CollapseAll ->
({model | openGroups = Set.empty }, Cmd.none, Nothing)
( { model | openGroups = Set.empty }, Cmd.none, Nothing )
ExpandAll ->
let
open = openAllGroups model
open =
openAllGroups model
in
({model | openGroups = open }, Cmd.none, Nothing)
( { model | openGroups = open }, Cmd.none, Nothing )
SelectItem item ->
(model, Cmd.none, Just item)
( model, Cmd.none, Just item )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div []
[div [class "ui ablue-comp menu"]
[div [class "right floated menu"]
[a [class "item"
,title "Expand all"
,onClick ExpandAll
,href ""
]
[i [class "double angle down icon"][]
]
,a [class "item"
,title "Collapse all"
,onClick CollapseAll
,href ""
]
[i [class "double angle up icon"][]
]
]
]
,div [class "ui middle aligned very relaxed divided basic list segment"]
[ div [ class "ui ablue-comp menu" ]
[ div [ class "right floated menu" ]
[ a
[ class "item"
, title "Expand all"
, onClick ExpandAll
, href ""
]
[ i [ class "double angle down icon" ] []
]
, a
[ class "item"
, title "Collapse all"
, onClick CollapseAll
, href ""
]
[ i [ class "double angle up icon" ] []
]
]
]
, div [ class "ui middle aligned very relaxed divided basic list segment" ]
(List.map (viewGroup model) model.results.groups)
]
isGroupOpen: Model -> ItemLightGroup -> Bool
isGroupOpen : Model -> ItemLightGroup -> Bool
isGroupOpen model group =
Set.member group.name model.openGroups
openGroup: Model -> ItemLightGroup -> Model
openGroup : Model -> ItemLightGroup -> Model
openGroup model group =
{ model | openGroups = Set.insert group.name model.openGroups }
closeGroup: Model -> ItemLightGroup -> Model
closeGroup : Model -> ItemLightGroup -> Model
closeGroup model group =
{ model | openGroups = Set.remove group.name model.openGroups }
viewGroup: Model -> ItemLightGroup -> Html Msg
viewGroup : Model -> ItemLightGroup -> Html Msg
viewGroup model group =
let
groupOpen = isGroupOpen model group
groupOpen =
isGroupOpen model group
children =
[i [classList [("large middle aligned icon", True)
,("caret right", not groupOpen)
,("caret down", groupOpen)
]][]
,div [class "content"]
[div [class "right floated content"]
[div [class "ui blue label"]
[List.length group.items |> String.fromInt |> text
]
]
,a [class "header"
,onClick (ToggleGroupState group)
,href ""
]
[text group.name
]
,div [class "description"]
[makeSummary group |> text
[ i
[ classList
[ ( "large middle aligned icon", True )
, ( "caret right", not groupOpen )
, ( "caret down", groupOpen )
]
]
[]
, div [ class "content" ]
[ div [ class "right floated content" ]
[ div [ class "ui blue label" ]
[ List.length group.items |> String.fromInt |> text
]
]
, a
[ class "header"
, onClick (ToggleGroupState group)
, href ""
]
[ text group.name
]
, div [ class "description" ]
[ makeSummary group |> text
]
]
]
itemTable =
div [class "ui basic content segment no-margin"]
[(renderItemTable model group.items)
div [ class "ui basic content segment no-margin" ]
[ renderItemTable model group.items
]
in
if isGroupOpen model group then
div [class "item"]
(List.append children [itemTable])
else
div [class "item"]
children
if isGroupOpen model group then
div [ class "item" ]
(List.append children [ itemTable ])
else
div [ class "item" ]
children
renderItemTable: Model -> List ItemLight -> Html Msg
renderItemTable : Model -> List ItemLight -> Html Msg
renderItemTable model items =
table [class "ui selectable padded table"]
[thead []
[tr []
[th [class "collapsing"][]
,th [class "collapsing"][text "Name"]
,th [class "collapsing"][text "Date"]
,th [class "collapsing"][text "Source"]
,th [][text "Correspondent"]
,th [][text "Concerning"]
]
]
,tbody[]
table [ class "ui selectable padded table" ]
[ thead []
[ tr []
[ th [ class "collapsing" ] []
, th [ class "collapsing" ] [ text "Name" ]
, th [ class "collapsing" ] [ text "Date" ]
, th [ class "collapsing" ] [ text "Source" ]
, th [] [ text "Correspondent" ]
, th [] [ text "Concerning" ]
]
]
, tbody []
(List.map (renderItemLine model) items)
]
renderItemLine: Model -> ItemLight -> Html Msg
renderItemLine : Model -> ItemLight -> Html Msg
renderItemLine model item =
let
dirIcon = i [class (Data.Direction.iconFromMaybe item.direction)][]
corr = List.filterMap identity [item.corrOrg, item.corrPerson]
|> List.map .name
|> List.intersperse ", "
|> String.concat
conc = List.filterMap identity [item.concPerson, item.concEquip]
|> List.map .name
|> List.intersperse ", "
|> String.concat
dirIcon =
i [ class (Data.Direction.iconFromMaybe item.direction) ] []
corr =
List.filterMap identity [ item.corrOrg, item.corrPerson ]
|> List.map .name
|> List.intersperse ", "
|> String.concat
conc =
List.filterMap identity [ item.concPerson, item.concEquip ]
|> List.map .name
|> List.intersperse ", "
|> String.concat
in
tr [onClick (SelectItem item)]
[td [class "collapsing"]
[div [classList [("ui teal ribbon label", True)
,("invisible", item.state /= "created")
]
][text "New"
]
]
,td [class "collapsing"]
[ dirIcon
, Util.String.ellipsis 45 item.name |> text
]
,td [class "collapsing"]
[Util.Time.formatDateShort item.date |> text
,span [classList [("invisible", Util.Maybe.isEmpty item.dueDate)
]
]
[text " "
,div [class "ui basic label"]
[i [class "bell icon"][]
,Maybe.map Util.Time.formatDateShort item.dueDate |> Maybe.withDefault "" |> text
]
tr [ onClick (SelectItem item) ]
[ td [ class "collapsing" ]
[ div
[ classList
[ ( "ui teal ribbon label", True )
, ( "invisible", item.state /= "created" )
]
]
,td [class "collapsing"][text item.source]
,td [][text corr]
,td [][text conc]
[ text "New"
]
]
, td [ class "collapsing" ]
[ dirIcon
, Util.String.ellipsis 45 item.name |> text
]
, td [ class "collapsing" ]
[ Util.Time.formatDateShort item.date |> text
, span
[ classList
[ ( "invisible", Util.Maybe.isEmpty item.dueDate )
]
]
[ text " "
, div [ class "ui basic label" ]
[ i [ class "bell icon" ] []
, Maybe.map Util.Time.formatDateShort item.dueDate |> Maybe.withDefault "" |> text
]
]
]
, td [ class "collapsing" ] [ text item.source ]
, td [] [ text corr ]
, td [] [ text conc ]
]
makeSummary: ItemLightGroup -> String
makeSummary : ItemLightGroup -> String
makeSummary group =
let
corrOrgs = List.filterMap .corrOrg group.items
corrPers = List.filterMap .corrPerson group.items
concPers = List.filterMap .concPerson group.items
concEqui = List.filterMap .concEquip group.items
all = List.concat [corrOrgs, corrPers, concPers, concEqui]
corrOrgs =
List.filterMap .corrOrg group.items
corrPers =
List.filterMap .corrPerson group.items
concPers =
List.filterMap .concPerson group.items
concEqui =
List.filterMap .concEquip group.items
all =
List.concat [ corrOrgs, corrPers, concPers, concEqui ]
in
List.map .name all
|> Util.List.distinct
|> List.intersperse ", "
|> String.concat
List.map .name all
|> Util.List.distinct
|> List.intersperse ", "
|> String.concat

View File

@ -1,28 +1,32 @@
module Comp.OrgForm exposing ( Model
, emptyModel
, Msg(..)
, view
, update
, isValid
, getOrg)
module Comp.OrgForm exposing
( Model
, Msg(..)
, emptyModel
, getOrg
, isValid
, update
, view
)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
import Data.Flags exposing (Flags)
import Api.Model.Organization exposing (Organization)
import Comp.AddressForm
import Comp.ContactField
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
type alias Model =
{ org: Organization
, name: String
, addressModel: Comp.AddressForm.Model
, contactModel: Comp.ContactField.Model
, notes: Maybe String
{ org : Organization
, name : String
, addressModel : Comp.AddressForm.Model
, contactModel : Comp.ContactField.Model
, notes : Maybe String
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ org = Api.Model.Organization.empty
, name = ""
@ -31,20 +35,25 @@ emptyModel =
, notes = Nothing
}
isValid: Model -> Bool
isValid : Model -> Bool
isValid model =
model.name /= ""
getOrg: Model -> Organization
getOrg : Model -> Organization
getOrg model =
let
o = model.org
o =
model.org
in
{ o | name = model.name
, address = Comp.AddressForm.getAddress model.addressModel
, contacts = Comp.ContactField.getContacts model.contactModel
, notes = model.notes
}
{ o
| name = model.name
, address = Comp.AddressForm.getAddress model.addressModel
, contacts = Comp.ContactField.getContacts model.contactModel
, notes = model.notes
}
type Msg
= SetName String
@ -53,61 +62,80 @@ type Msg
| ContactMsg Comp.ContactField.Msg
| SetNotes String
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetOrg t ->
let
(m1, c1) = update flags (AddressMsg (Comp.AddressForm.SetAddress t.address)) model
(m2, c2) = update flags (ContactMsg (Comp.ContactField.SetItems t.contacts)) m1
( m1, c1 ) =
update flags (AddressMsg (Comp.AddressForm.SetAddress t.address)) model
( m2, c2 ) =
update flags (ContactMsg (Comp.ContactField.SetItems t.contacts)) m1
in
({m2 | org = t, name = t.name, notes = t.notes }, Cmd.none)
( { m2 | org = t, name = t.name, notes = t.notes }, Cmd.batch [ c1, c2 ] )
AddressMsg am ->
let
(m1, c1) = Comp.AddressForm.update am model.addressModel
( m1, c1 ) =
Comp.AddressForm.update am model.addressModel
in
({model | addressModel = m1}, Cmd.map AddressMsg c1)
( { model | addressModel = m1 }, Cmd.map AddressMsg c1 )
ContactMsg m ->
let
(m1, c1) = Comp.ContactField.update m model.contactModel
( m1, c1 ) =
Comp.ContactField.update m model.contactModel
in
({model | contactModel = m1}, Cmd.map ContactMsg c1)
( { model | contactModel = m1 }, Cmd.map ContactMsg c1 )
SetName n ->
({model | name = n}, Cmd.none)
( { model | name = n }, Cmd.none )
SetNotes str ->
({model | notes = if str == "" then Nothing else Just str}, Cmd.none)
( { model
| notes =
if str == "" then
Nothing
else
Just str
}
, Cmd.none
)
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div [class "ui form"]
[div [classList [("field", True)
,("error", not (isValid model))
]
]
[label [][text "Name*"]
,input [type_ "text"
,onInput SetName
,placeholder "Name"
,value model.name
][]
]
,h3 [class "ui dividing header"]
[text "Address"
div [ class "ui form" ]
[ div
[ classList
[ ( "field", True )
, ( "error", not (isValid model) )
]
]
,Html.map AddressMsg (Comp.AddressForm.view model.addressModel)
,h3 [class "ui dividing header"]
[text "Contacts"
[ label [] [ text "Name*" ]
, input
[ type_ "text"
, onInput SetName
, placeholder "Name"
, value model.name
]
[]
]
,Html.map ContactMsg (Comp.ContactField.view model.contactModel)
,h3 [class "ui dividing header"]
[text "Notes"
, h3 [ class "ui dividing header" ]
[ text "Address"
]
,div [class "field"]
[textarea [onInput SetNotes][Maybe.withDefault "" model.notes |> text ]
, Html.map AddressMsg (Comp.AddressForm.view model.addressModel)
, h3 [ class "ui dividing header" ]
[ text "Contacts"
]
, Html.map ContactMsg (Comp.ContactField.view model.contactModel)
, h3 [ class "ui dividing header" ]
[ text "Notes"
]
, div [ class "field" ]
[ textarea [ onInput SetNotes ] [ Maybe.withDefault "" model.notes |> text ]
]
]

View File

@ -1,36 +1,43 @@
module Comp.OrgManage exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.OrgManage exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Http
import Api
import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.Organization
import Api.Model.OrganizationList exposing (OrganizationList)
import Comp.OrgForm
import Comp.OrgTable
import Comp.YesNoDimmer
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onSubmit)
import Data.Flags exposing (Flags)
import Comp.OrgTable
import Comp.OrgForm
import Comp.YesNoDimmer
import Api.Model.Organization
import Api.Model.OrganizationList exposing (OrganizationList)
import Api.Model.BasicResult exposing (BasicResult)
import Util.Maybe
import Http
import Util.Http
import Util.Maybe
type alias Model =
{ tableModel: Comp.OrgTable.Model
, formModel: Comp.OrgForm.Model
, viewMode: ViewMode
, formError: Maybe String
, loading: Bool
, deleteConfirm: Comp.YesNoDimmer.Model
{ tableModel : Comp.OrgTable.Model
, formModel : Comp.OrgForm.Model
, viewMode : ViewMode
, formError : Maybe String
, loading : Bool
, deleteConfirm : Comp.YesNoDimmer.Model
}
type ViewMode = Table | Form
emptyModel: Model
type ViewMode
= Table
| Form
emptyModel : Model
emptyModel =
{ tableModel = Comp.OrgTable.emptyModel
, formModel = Comp.OrgForm.emptyModel
@ -40,6 +47,7 @@ emptyModel =
, deleteConfirm = Comp.YesNoDimmer.emptyModel
}
type Msg
= TableMsg Comp.OrgTable.Msg
| FormMsg Comp.OrgForm.Msg
@ -52,155 +60,210 @@ type Msg
| YesNoMsg Comp.YesNoDimmer.Msg
| RequestDelete
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
TableMsg m ->
let
(tm, tc) = Comp.OrgTable.update flags m model.tableModel
(m2, c2) = ({model | tableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError = if Util.Maybe.nonEmpty tm.selected then Nothing else model.formError
}
, Cmd.map TableMsg tc
)
(m3, c3) = case tm.selected of
( tm, tc ) =
Comp.OrgTable.update flags m model.tableModel
( m2, c2 ) =
( { model
| tableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError =
if Util.Maybe.nonEmpty tm.selected then
Nothing
else
model.formError
}
, Cmd.map TableMsg tc
)
( m3, c3 ) =
case tm.selected of
Just org ->
update flags (FormMsg (Comp.OrgForm.SetOrg org)) m2
Nothing ->
(m2, Cmd.none)
( m2, Cmd.none )
in
(m3, Cmd.batch [c2, c3])
( m3, Cmd.batch [ c2, c3 ] )
FormMsg m ->
let
(m2, c2) = Comp.OrgForm.update flags m model.formModel
( m2, c2 ) =
Comp.OrgForm.update flags m model.formModel
in
({model | formModel = m2}, Cmd.map FormMsg c2)
( { model | formModel = m2 }, Cmd.map FormMsg c2 )
LoadOrgs ->
({model| loading = True}, Api.getOrganizations flags OrgResp)
( { model | loading = True }, Api.getOrganizations flags OrgResp )
OrgResp (Ok orgs) ->
let
m2 = {model|viewMode = Table, loading = False}
m2 =
{ model | viewMode = Table, loading = False }
in
update flags (TableMsg (Comp.OrgTable.SetOrgs orgs.items)) m2
update flags (TableMsg (Comp.OrgTable.SetOrgs orgs.items)) m2
OrgResp (Err err) ->
({model|loading = False}, Cmd.none)
OrgResp (Err _) ->
( { model | loading = False }, Cmd.none )
SetViewMode m ->
let
m2 = {model | viewMode = m }
m2 =
{ model | viewMode = m }
in
case m of
Table ->
update flags (TableMsg Comp.OrgTable.Deselect) m2
Form ->
(m2, Cmd.none)
case m of
Table ->
update flags (TableMsg Comp.OrgTable.Deselect) m2
Form ->
( m2, Cmd.none )
InitNewOrg ->
let
nm = {model | viewMode = Form, formError = Nothing }
org = Api.Model.Organization.empty
nm =
{ model | viewMode = Form, formError = Nothing }
org =
Api.Model.Organization.empty
in
update flags (FormMsg (Comp.OrgForm.SetOrg org)) nm
update flags (FormMsg (Comp.OrgForm.SetOrg org)) nm
Submit ->
let
org = Comp.OrgForm.getOrg model.formModel
valid = Comp.OrgForm.isValid model.formModel
in if valid then
({model|loading = True}, Api.postOrg flags org SubmitResp)
else
({model|formError = Just "Please correct the errors in the form."}, Cmd.none)
org =
Comp.OrgForm.getOrg model.formModel
valid =
Comp.OrgForm.isValid model.formModel
in
if valid then
( { model | loading = True }, Api.postOrg flags org SubmitResp )
else
( { model | formError = Just "Please correct the errors in the form." }, Cmd.none )
SubmitResp (Ok res) ->
if res.success then
let
(m2, c2) = update flags (SetViewMode Table) model
(m3, c3) = update flags LoadOrgs m2
( m2, c2 ) =
update flags (SetViewMode Table) model
( m3, c3 ) =
update flags LoadOrgs m2
in
({m3|loading = False}, Cmd.batch [c2,c3])
( { m3 | loading = False }, Cmd.batch [ c2, c3 ] )
else
({model | formError = Just res.message, loading = False }, Cmd.none)
( { model | formError = Just res.message, loading = False }, Cmd.none )
SubmitResp (Err err) ->
({model | formError = Just (Util.Http.errorToString err), loading = False}, Cmd.none)
( { model | formError = Just (Util.Http.errorToString err), loading = False }, Cmd.none )
RequestDelete ->
update flags (YesNoMsg Comp.YesNoDimmer.activate) model
YesNoMsg m ->
let
(cm, confirmed) = Comp.YesNoDimmer.update m model.deleteConfirm
org = Comp.OrgForm.getOrg model.formModel
cmd = if confirmed then Api.deleteOrg flags org.id SubmitResp else Cmd.none
( cm, confirmed ) =
Comp.YesNoDimmer.update m model.deleteConfirm
org =
Comp.OrgForm.getOrg model.formModel
cmd =
if confirmed then
Api.deleteOrg flags org.id SubmitResp
else
Cmd.none
in
({model | deleteConfirm = cm}, cmd)
( { model | deleteConfirm = cm }, cmd )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
if model.viewMode == Table then viewTable model
else viewForm model
if model.viewMode == Table then
viewTable model
viewTable: Model -> Html Msg
else
viewForm model
viewTable : Model -> Html Msg
viewTable model =
div []
[button [class "ui basic button", onClick InitNewOrg]
[i [class "plus icon"][]
,text "Create new"
]
,Html.map TableMsg (Comp.OrgTable.view model.tableModel)
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
[ button [ class "ui basic button", onClick InitNewOrg ]
[ i [ class "plus icon" ] []
, text "Create new"
]
, Html.map TableMsg (Comp.OrgTable.view model.tableModel)
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]
viewForm: Model -> Html Msg
viewForm : Model -> Html Msg
viewForm model =
let
newOrg = model.formModel.org.id == ""
newOrg =
model.formModel.org.id == ""
in
Html.form [class "ui segment", onSubmit Submit]
[Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
,if newOrg then
h3 [class "ui dividing header"]
[text "Create new organization"
]
else
h3 [class "ui dividing header"]
[text ("Edit org: " ++ model.formModel.org.name)
,div [class "sub header"]
[text "Id: "
,text model.formModel.org.id
]
]
,Html.map FormMsg (Comp.OrgForm.view model.formModel)
,div [classList [("ui error message", True)
,("invisible", Util.Maybe.isEmpty model.formError)
]
]
[Maybe.withDefault "" model.formError |> text
]
,div [class "ui horizontal divider"][]
,button [class "ui primary button", type_ "submit"]
[text "Submit"
Html.form [ class "ui segment", onSubmit Submit ]
[ Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
, if newOrg then
h3 [ class "ui dividing header" ]
[ text "Create new organization"
]
,a [class "ui secondary button", onClick (SetViewMode Table), href ""]
[text "Cancel"
else
h3 [ class "ui dividing header" ]
[ text ("Edit org: " ++ model.formModel.org.name)
, div [ class "sub header" ]
[ text "Id: "
, text model.formModel.org.id
]
]
, Html.map FormMsg (Comp.OrgForm.view model.formModel)
, div
[ classList
[ ( "ui error message", True )
, ( "invisible", Util.Maybe.isEmpty model.formError )
]
,if not newOrg then
a [class "ui right floated red button", href "", onClick RequestDelete]
[text "Delete"]
else
span[][]
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
]
]
[ Maybe.withDefault "" model.formError |> text
]
, div [ class "ui horizontal divider" ] []
, button [ class "ui primary button", type_ "submit" ]
[ text "Submit"
]
, a [ class "ui secondary button", onClick (SetViewMode Table), href "" ]
[ text "Cancel"
]
, if not newOrg then
a [ class "ui right floated red button", href "", onClick RequestDelete ]
[ text "Delete" ]
else
span [] []
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]

View File

@ -1,74 +1,80 @@
module Comp.OrgTable exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.OrgTable exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Api.Model.Organization exposing (Organization)
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Data.Flags exposing (Flags)
import Api.Model.Organization exposing (Organization)
import Api.Model.Address exposing (Address)
import Api.Model.Contact exposing (Contact)
import Util.Address
import Util.Contact
type alias Model =
{ equips: List Organization
, selected: Maybe Organization
{ equips : List Organization
, selected : Maybe Organization
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ equips = []
, selected = Nothing
}
type Msg
= SetOrgs (List Organization)
| Select Organization
| Deselect
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetOrgs list ->
({model | equips = list, selected = Nothing }, Cmd.none)
( { model | equips = list, selected = Nothing }, Cmd.none )
Select equip ->
({model | selected = Just equip}, Cmd.none)
( { model | selected = Just equip }, Cmd.none )
Deselect ->
({model | selected = Nothing}, Cmd.none)
( { model | selected = Nothing }, Cmd.none )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
table [class "ui selectable table"]
[thead []
[tr []
[th [class "collapsing"][text "Name"]
,th [][text "Address"]
,th [][text "Contact"]
]
]
,tbody []
table [ class "ui selectable table" ]
[ thead []
[ tr []
[ th [ class "collapsing" ] [ text "Name" ]
, th [] [ text "Address" ]
, th [] [ text "Contact" ]
]
]
, tbody []
(List.map (renderOrgLine model) model.equips)
]
renderOrgLine: Model -> Organization -> Html Msg
renderOrgLine : Model -> Organization -> Html Msg
renderOrgLine model org =
tr [classList [("active", model.selected == Just org)]
,onClick (Select org)
]
[td [class "collapsing"]
[text org.name
]
,td []
[Util.Address.toString org.address |> text
tr
[ classList [ ( "active", model.selected == Just org ) ]
, onClick (Select org)
]
[ td [ class "collapsing" ]
[ text org.name
]
,td []
[Util.Contact.toString org.contacts |> text
, td []
[ Util.Address.toString org.address |> text
]
, td []
[ Util.Contact.toString org.contacts |> text
]
]

View File

@ -1,30 +1,33 @@
module Comp.PersonForm exposing ( Model
, emptyModel
, Msg(..)
, view
, update
, isValid
, getPerson)
module Comp.PersonForm exposing
( Model
, Msg(..)
, emptyModel
, getPerson
, isValid
, update
, view
)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput, onCheck)
import Data.Flags exposing (Flags)
import Api.Model.Person exposing (Person)
import Comp.AddressForm
import Comp.ContactField
import Comp.YesNoDimmer
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onCheck, onInput)
type alias Model =
{ org: Person
, name: String
, addressModel: Comp.AddressForm.Model
, contactModel: Comp.ContactField.Model
, notes: Maybe String
, concerning: Bool
{ org : Person
, name : String
, addressModel : Comp.AddressForm.Model
, contactModel : Comp.ContactField.Model
, notes : Maybe String
, concerning : Bool
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ org = Api.Model.Person.empty
, name = ""
@ -34,21 +37,26 @@ emptyModel =
, concerning = False
}
isValid: Model -> Bool
isValid : Model -> Bool
isValid model =
model.name /= ""
getPerson: Model -> Person
getPerson : Model -> Person
getPerson model =
let
o = model.org
o =
model.org
in
{ o | name = model.name
, address = Comp.AddressForm.getAddress model.addressModel
, contacts = Comp.ContactField.getContacts model.contactModel
, notes = model.notes
, concerning = model.concerning
}
{ o
| name = model.name
, address = Comp.AddressForm.getAddress model.addressModel
, contacts = Comp.ContactField.getContacts model.contactModel
, notes = model.notes
, concerning = model.concerning
}
type Msg
= SetName String
@ -58,72 +66,101 @@ type Msg
| SetNotes String
| SetConcerning Bool
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetPerson t ->
let
(m1, c1) = update flags (AddressMsg (Comp.AddressForm.SetAddress t.address)) model
(m2, c2) = update flags (ContactMsg (Comp.ContactField.SetItems t.contacts)) m1
( m1, c1 ) =
update flags (AddressMsg (Comp.AddressForm.SetAddress t.address)) model
( m2, c2 ) =
update flags (ContactMsg (Comp.ContactField.SetItems t.contacts)) m1
in
({m2 | org = t, name = t.name, notes = t.notes, concerning = t.concerning }, Cmd.none)
( { m2
| org = t
, name = t.name
, notes = t.notes
, concerning = t.concerning
}
, Cmd.batch [ c1, c2 ]
)
AddressMsg am ->
let
(m1, c1) = Comp.AddressForm.update am model.addressModel
( m1, c1 ) =
Comp.AddressForm.update am model.addressModel
in
({model | addressModel = m1}, Cmd.map AddressMsg c1)
( { model | addressModel = m1 }, Cmd.map AddressMsg c1 )
ContactMsg m ->
let
(m1, c1) = Comp.ContactField.update m model.contactModel
( m1, c1 ) =
Comp.ContactField.update m model.contactModel
in
({model | contactModel = m1}, Cmd.map ContactMsg c1)
( { model | contactModel = m1 }, Cmd.map ContactMsg c1 )
SetName n ->
({model | name = n}, Cmd.none)
( { model | name = n }, Cmd.none )
SetNotes str ->
({model | notes = if str == "" then Nothing else Just str}, Cmd.none)
( { model
| notes =
if str == "" then
Nothing
SetConcerning flag ->
({model | concerning = not model.concerning}, Cmd.none)
else
Just str
}
, Cmd.none
)
SetConcerning _ ->
( { model | concerning = not model.concerning }, Cmd.none )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div [class "ui form"]
[div [classList [("field", True)
,("error", not (isValid model))
]
]
[label [][text "Name*"]
,input [type_ "text"
,onInput SetName
,placeholder "Name"
,value model.name
][]
]
,div [class "inline field"]
[div [class "ui checkbox"]
[input [type_ "checkbox"
, checked model.concerning
, onCheck SetConcerning][]
,label [][text "Use for concerning person suggestion only"]
]
div [ class "ui form" ]
[ div
[ classList
[ ( "field", True )
, ( "error", not (isValid model) )
]
]
,h3 [class "ui dividing header"]
[text "Address"
[ label [] [ text "Name*" ]
, input
[ type_ "text"
, onInput SetName
, placeholder "Name"
, value model.name
]
[]
]
,Html.map AddressMsg (Comp.AddressForm.view model.addressModel)
,h3 [class "ui dividing header"]
[text "Contacts"
, div [ class "inline field" ]
[ div [ class "ui checkbox" ]
[ input
[ type_ "checkbox"
, checked model.concerning
, onCheck SetConcerning
]
[]
, label [] [ text "Use for concerning person suggestion only" ]
]
]
,Html.map ContactMsg (Comp.ContactField.view model.contactModel)
,h3 [class "ui dividing header"]
[text "Notes"
, h3 [ class "ui dividing header" ]
[ text "Address"
]
,div [class "field"]
[textarea [onInput SetNotes][Maybe.withDefault "" model.notes |> text ]
, Html.map AddressMsg (Comp.AddressForm.view model.addressModel)
, h3 [ class "ui dividing header" ]
[ text "Contacts"
]
, Html.map ContactMsg (Comp.ContactField.view model.contactModel)
, h3 [ class "ui dividing header" ]
[ text "Notes"
]
, div [ class "field" ]
[ textarea [ onInput SetNotes ] [ Maybe.withDefault "" model.notes |> text ]
]
]

View File

@ -1,36 +1,43 @@
module Comp.PersonManage exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.PersonManage exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Http
import Api
import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.Person
import Api.Model.PersonList exposing (PersonList)
import Comp.PersonForm
import Comp.PersonTable
import Comp.YesNoDimmer
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onSubmit)
import Data.Flags exposing (Flags)
import Comp.PersonTable
import Comp.PersonForm
import Comp.YesNoDimmer
import Api.Model.Person
import Api.Model.PersonList exposing (PersonList)
import Api.Model.BasicResult exposing (BasicResult)
import Util.Maybe
import Http
import Util.Http
import Util.Maybe
type alias Model =
{ tableModel: Comp.PersonTable.Model
, formModel: Comp.PersonForm.Model
, viewMode: ViewMode
, formError: Maybe String
, loading: Bool
, deleteConfirm: Comp.YesNoDimmer.Model
{ tableModel : Comp.PersonTable.Model
, formModel : Comp.PersonForm.Model
, viewMode : ViewMode
, formError : Maybe String
, loading : Bool
, deleteConfirm : Comp.YesNoDimmer.Model
}
type ViewMode = Table | Form
emptyModel: Model
type ViewMode
= Table
| Form
emptyModel : Model
emptyModel =
{ tableModel = Comp.PersonTable.emptyModel
, formModel = Comp.PersonForm.emptyModel
@ -40,6 +47,7 @@ emptyModel =
, deleteConfirm = Comp.YesNoDimmer.emptyModel
}
type Msg
= TableMsg Comp.PersonTable.Msg
| FormMsg Comp.PersonForm.Msg
@ -52,156 +60,210 @@ type Msg
| YesNoMsg Comp.YesNoDimmer.Msg
| RequestDelete
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
TableMsg m ->
let
(tm, tc) = Comp.PersonTable.update flags m model.tableModel
(m2, c2) = ({model | tableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError = if Util.Maybe.nonEmpty tm.selected then Nothing else model.formError
}
, Cmd.map TableMsg tc
)
(m3, c3) = case tm.selected of
( tm, tc ) =
Comp.PersonTable.update flags m model.tableModel
( m2, c2 ) =
( { model
| tableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError =
if Util.Maybe.nonEmpty tm.selected then
Nothing
else
model.formError
}
, Cmd.map TableMsg tc
)
( m3, c3 ) =
case tm.selected of
Just org ->
update flags (FormMsg (Comp.PersonForm.SetPerson org)) m2
Nothing ->
(m2, Cmd.none)
( m2, Cmd.none )
in
(m3, Cmd.batch [c2, c3])
( m3, Cmd.batch [ c2, c3 ] )
FormMsg m ->
let
(m2, c2) = Comp.PersonForm.update flags m model.formModel
( m2, c2 ) =
Comp.PersonForm.update flags m model.formModel
in
({model | formModel = m2}, Cmd.map FormMsg c2)
( { model | formModel = m2 }, Cmd.map FormMsg c2 )
LoadPersons ->
({model| loading = True}, Api.getPersons flags PersonResp)
( { model | loading = True }, Api.getPersons flags PersonResp )
PersonResp (Ok orgs) ->
let
m2 = {model|viewMode = Table, loading = False}
m2 =
{ model | viewMode = Table, loading = False }
in
update flags (TableMsg (Comp.PersonTable.SetPersons orgs.items)) m2
update flags (TableMsg (Comp.PersonTable.SetPersons orgs.items)) m2
PersonResp (Err err) ->
({model|loading = False}, Cmd.none)
PersonResp (Err _) ->
( { model | loading = False }, Cmd.none )
SetViewMode m ->
let
m2 = {model | viewMode = m }
m2 =
{ model | viewMode = m }
in
case m of
Table ->
update flags (TableMsg Comp.PersonTable.Deselect) m2
Form ->
(m2, Cmd.none)
case m of
Table ->
update flags (TableMsg Comp.PersonTable.Deselect) m2
Form ->
( m2, Cmd.none )
InitNewPerson ->
let
nm = {model | viewMode = Form, formError = Nothing }
org = Api.Model.Person.empty
nm =
{ model | viewMode = Form, formError = Nothing }
org =
Api.Model.Person.empty
in
update flags (FormMsg (Comp.PersonForm.SetPerson org)) nm
update flags (FormMsg (Comp.PersonForm.SetPerson org)) nm
Submit ->
let
person = Comp.PersonForm.getPerson model.formModel
valid = Comp.PersonForm.isValid model.formModel
in if valid then
({model|loading = True}, Api.postPerson flags person SubmitResp)
else
({model|formError = Just "Please correct the errors in the form."}, Cmd.none)
person =
Comp.PersonForm.getPerson model.formModel
valid =
Comp.PersonForm.isValid model.formModel
in
if valid then
( { model | loading = True }, Api.postPerson flags person SubmitResp )
else
( { model | formError = Just "Please correct the errors in the form." }, Cmd.none )
SubmitResp (Ok res) ->
if res.success then
let
(m2, c2) = update flags (SetViewMode Table) model
(m3, c3) = update flags LoadPersons m2
( m2, c2 ) =
update flags (SetViewMode Table) model
( m3, c3 ) =
update flags LoadPersons m2
in
({m3|loading = False}, Cmd.batch [c2,c3])
( { m3 | loading = False }, Cmd.batch [ c2, c3 ] )
else
({model | formError = Just res.message, loading = False }, Cmd.none)
( { model | formError = Just res.message, loading = False }, Cmd.none )
SubmitResp (Err err) ->
({model | formError = Just (Util.Http.errorToString err), loading = False}, Cmd.none)
( { model | formError = Just (Util.Http.errorToString err), loading = False }, Cmd.none )
RequestDelete ->
update flags (YesNoMsg Comp.YesNoDimmer.activate) model
YesNoMsg m ->
let
(cm, confirmed) = Comp.YesNoDimmer.update m model.deleteConfirm
person = Comp.PersonForm.getPerson model.formModel
cmd = if confirmed then Api.deletePerson flags person.id SubmitResp else Cmd.none
( cm, confirmed ) =
Comp.YesNoDimmer.update m model.deleteConfirm
person =
Comp.PersonForm.getPerson model.formModel
cmd =
if confirmed then
Api.deletePerson flags person.id SubmitResp
else
Cmd.none
in
({model | deleteConfirm = cm}, cmd)
( { model | deleteConfirm = cm }, cmd )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
if model.viewMode == Table then viewTable model
else viewForm model
if model.viewMode == Table then
viewTable model
viewTable: Model -> Html Msg
else
viewForm model
viewTable : Model -> Html Msg
viewTable model =
div []
[button [class "ui basic button", onClick InitNewPerson]
[i [class "plus icon"][]
,text "Create new"
]
,Html.map TableMsg (Comp.PersonTable.view model.tableModel)
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
[ button [ class "ui basic button", onClick InitNewPerson ]
[ i [ class "plus icon" ] []
, text "Create new"
]
, Html.map TableMsg (Comp.PersonTable.view model.tableModel)
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]
viewForm: Model -> Html Msg
viewForm : Model -> Html Msg
viewForm model =
let
newPerson = model.formModel.org.id == ""
newPerson =
model.formModel.org.id == ""
in
Html.form [class "ui segment", onSubmit Submit]
[Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
,if newPerson then
h3 [class "ui dividing header"]
[text "Create new person"
]
else
h3 [class "ui dividing header"]
[text ("Edit org: " ++ model.formModel.org.name)
,div [class "sub header"]
[text "Id: "
,text model.formModel.org.id
]
]
,Html.map FormMsg (Comp.PersonForm.view model.formModel)
,div [classList [("ui error message", True)
,("invisible", Util.Maybe.isEmpty model.formError)
]
]
[Maybe.withDefault "" model.formError |> text
]
,div [class "ui horizontal divider"][]
,button [class "ui primary button", type_ "submit"]
[text "Submit"
Html.form [ class "ui segment", onSubmit Submit ]
[ Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
, if newPerson then
h3 [ class "ui dividing header" ]
[ text "Create new person"
]
,a [class "ui secondary button", onClick (SetViewMode Table), href ""]
[text "Cancel"
else
h3 [ class "ui dividing header" ]
[ text ("Edit org: " ++ model.formModel.org.name)
, div [ class "sub header" ]
[ text "Id: "
, text model.formModel.org.id
]
]
, Html.map FormMsg (Comp.PersonForm.view model.formModel)
, div
[ classList
[ ( "ui error message", True )
, ( "invisible", Util.Maybe.isEmpty model.formError )
]
,if not newPerson then
a [class "ui right floated red button", href "", onClick RequestDelete]
[text "Delete"]
else
span[][]
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
]
]
[ Maybe.withDefault "" model.formError |> text
]
, div [ class "ui horizontal divider" ] []
, button [ class "ui primary button", type_ "submit" ]
[ text "Submit"
]
, a [ class "ui secondary button", onClick (SetViewMode Table), href "" ]
[ text "Cancel"
]
, if not newPerson then
a [ class "ui right floated red button", href "", onClick RequestDelete ]
[ text "Delete" ]
else
span [] []
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]

View File

@ -1,81 +1,88 @@
module Comp.PersonTable exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.PersonTable exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Api.Model.Person exposing (Person)
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Data.Flags exposing (Flags)
import Api.Model.Person exposing (Person)
import Api.Model.Address exposing (Address)
import Api.Model.Contact exposing (Contact)
import Util.Address
import Util.Contact
type alias Model =
{ equips: List Person
, selected: Maybe Person
{ equips : List Person
, selected : Maybe Person
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ equips = []
, selected = Nothing
}
type Msg
= SetPersons (List Person)
| Select Person
| Deselect
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetPersons list ->
({model | equips = list, selected = Nothing }, Cmd.none)
( { model | equips = list, selected = Nothing }, Cmd.none )
Select equip ->
({model | selected = Just equip}, Cmd.none)
( { model | selected = Just equip }, Cmd.none )
Deselect ->
({model | selected = Nothing}, Cmd.none)
( { model | selected = Nothing }, Cmd.none )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
table [class "ui selectable table"]
[thead []
[tr []
[th [class "collapsing"][text "Name"]
,th [class "collapsing"][text "Concerning"]
,th [][text "Address"]
,th [][text "Contact"]
]
]
,tbody []
table [ class "ui selectable table" ]
[ thead []
[ tr []
[ th [ class "collapsing" ] [ text "Name" ]
, th [ class "collapsing" ] [ text "Concerning" ]
, th [] [ text "Address" ]
, th [] [ text "Contact" ]
]
]
, tbody []
(List.map (renderPersonLine model) model.equips)
]
renderPersonLine: Model -> Person -> Html Msg
renderPersonLine : Model -> Person -> Html Msg
renderPersonLine model person =
tr [classList [("active", model.selected == Just person)]
,onClick (Select person)
]
[td [class "collapsing"]
[text person.name
]
,td [class "collapsing"]
[if person.concerning then
i [class "check square outline icon"][]
else
i [class "minus square outline icon"][]
tr
[ classList [ ( "active", model.selected == Just person ) ]
, onClick (Select person)
]
[ td [ class "collapsing" ]
[ text person.name
]
,td []
[Util.Address.toString person.address |> text
, td [ class "collapsing" ]
[ if person.concerning then
i [ class "check square outline icon" ] []
else
i [ class "minus square outline icon" ] []
]
,td []
[Util.Contact.toString person.contacts |> text
, td []
[ Util.Address.toString person.address |> text
]
, td []
[ Util.Contact.toString person.contacts |> text
]
]

View File

@ -1,87 +1,96 @@
module Comp.SearchMenu exposing ( Model
, emptyModel
, Msg(..)
, update
, NextState
, view
, getItemSearch
)
module Comp.SearchMenu exposing
( Model
, Msg(..)
, NextState
, emptyModel
, getItemSearch
, update
, view
)
import Http
import Api
import Api.Model.Equipment exposing (Equipment)
import Api.Model.EquipmentList exposing (EquipmentList)
import Api.Model.IdName exposing (IdName)
import Api.Model.ItemSearch exposing (ItemSearch)
import Api.Model.ReferenceList exposing (ReferenceList)
import Api.Model.Tag exposing (Tag)
import Api.Model.TagList exposing (TagList)
import Comp.DatePicker
import Comp.Dropdown exposing (isDropdownChangeMsg)
import Data.Direction exposing (Direction)
import Data.Flags exposing (Flags)
import DatePicker exposing (DatePicker)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onCheck, onInput)
import Data.Direction exposing (Direction)
import Data.Flags exposing (Flags)
import Comp.Dropdown exposing (isDropdownChangeMsg)
import Comp.DatePicker
import DatePicker exposing (DatePicker)
import Api
import Api.Model.IdName exposing (IdName)
import Api.Model.ItemSearch exposing (ItemSearch)
import Api.Model.TagList exposing (TagList)
import Api.Model.Tag exposing (Tag)
import Api.Model.Equipment exposing (Equipment)
import Api.Model.ReferenceList exposing (ReferenceList)
import Api.Model.EquipmentList exposing (EquipmentList)
import Http
import Util.Maybe
import Util.Update
-- Data Model
type alias Model =
{ tagInclModel: Comp.Dropdown.Model Tag
, tagExclModel: Comp.Dropdown.Model Tag
, directionModel: Comp.Dropdown.Model Direction
, orgModel: Comp.Dropdown.Model IdName
, corrPersonModel: Comp.Dropdown.Model IdName
, concPersonModel: Comp.Dropdown.Model IdName
, concEquipmentModel: Comp.Dropdown.Model Equipment
, inboxCheckbox: Bool
, fromDateModel: DatePicker
, fromDate: Maybe Int
, untilDateModel: DatePicker
, untilDate: Maybe Int
, fromDueDateModel: DatePicker
, fromDueDate: Maybe Int
, untilDueDateModel: DatePicker
, untilDueDate: Maybe Int
, nameModel: Maybe String
{ tagInclModel : Comp.Dropdown.Model Tag
, tagExclModel : Comp.Dropdown.Model Tag
, directionModel : Comp.Dropdown.Model Direction
, orgModel : Comp.Dropdown.Model IdName
, corrPersonModel : Comp.Dropdown.Model IdName
, concPersonModel : Comp.Dropdown.Model IdName
, concEquipmentModel : Comp.Dropdown.Model Equipment
, inboxCheckbox : Bool
, fromDateModel : DatePicker
, fromDate : Maybe Int
, untilDateModel : DatePicker
, untilDate : Maybe Int
, fromDueDateModel : DatePicker
, fromDueDate : Maybe Int
, untilDueDateModel : DatePicker
, untilDueDate : Maybe Int
, nameModel : Maybe String
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ tagInclModel = makeTagModel
, tagExclModel = makeTagModel
, 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
}
, orgModel = Comp.Dropdown.makeModel
{ multiple = False
, searchable = \n -> n > 5
, makeOption = \e -> {value = e.id, text = e.name}
, labelColor = \_ -> ""
, placeholder = "Choose an organization"
}
, corrPersonModel = Comp.Dropdown.makeSingle
{ makeOption = \e -> {value = e.id, text = e.name}
, placeholder = "Choose a person"
}
, concPersonModel = Comp.Dropdown.makeSingle
{ makeOption = \e -> {value = e.id, text = e.name}
, placeholder = "Choose a person"
}
, concEquipmentModel = Comp.Dropdown.makeModel
{ multiple = False
, searchable = \n -> n > 5
, makeOption = \e -> {value = e.id, text = e.name}
, labelColor = \_ -> ""
, placeholder = "Choosa an equipment"
}
, 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
}
, orgModel =
Comp.Dropdown.makeModel
{ multiple = False
, searchable = \n -> n > 5
, makeOption = \e -> { value = e.id, text = e.name }
, labelColor = \_ -> ""
, placeholder = "Choose an organization"
}
, corrPersonModel =
Comp.Dropdown.makeSingle
{ makeOption = \e -> { value = e.id, text = e.name }
, placeholder = "Choose a person"
}
, concPersonModel =
Comp.Dropdown.makeSingle
{ makeOption = \e -> { value = e.id, text = e.name }
, placeholder = "Choose a person"
}
, concEquipmentModel =
Comp.Dropdown.makeModel
{ multiple = False
, searchable = \n -> n > 5
, makeOption = \e -> { value = e.id, text = e.name }
, labelColor = \_ -> ""
, placeholder = "Choosa an equipment"
}
, inboxCheckbox = False
, fromDateModel = Comp.DatePicker.emptyModel
, fromDate = Nothing
@ -94,6 +103,7 @@ emptyModel =
, nameModel = Nothing
}
type Msg
= Init
| TagIncMsg (Comp.Dropdown.Msg Tag)
@ -115,315 +125,386 @@ type Msg
| SetName String
makeTagModel: Comp.Dropdown.Model Tag
makeTagModel : Comp.Dropdown.Model Tag
makeTagModel =
Comp.Dropdown.makeModel
{ multiple = True
, searchable = \n -> n > 4
, makeOption = \tag -> { value = tag.id, text = tag.name }
, labelColor = \tag -> if Util.Maybe.nonEmpty tag.category then "basic blue" else ""
, labelColor =
\tag ->
if Util.Maybe.nonEmpty tag.category then
"basic blue"
else
""
, placeholder = "Choose a tag"
}
getDirection: Model -> Maybe Direction
getDirection : Model -> Maybe Direction
getDirection model =
let
selection = Comp.Dropdown.getSelected model.directionModel
selection =
Comp.Dropdown.getSelected model.directionModel
in
case selection of
[d] -> Just d
_ -> Nothing
case selection of
[ d ] ->
Just d
getItemSearch: Model -> ItemSearch
_ ->
Nothing
getItemSearch : Model -> ItemSearch
getItemSearch model =
let e = Api.Model.ItemSearch.empty in
{ e | tagsInclude = Comp.Dropdown.getSelected model.tagInclModel |> List.map .id
, tagsExclude = Comp.Dropdown.getSelected model.tagExclModel |> List.map .id
, corrPerson = Comp.Dropdown.getSelected model.corrPersonModel |> List.map .id |> List.head
, corrOrg = Comp.Dropdown.getSelected model.orgModel |> List.map .id |> List.head
, concPerson = Comp.Dropdown.getSelected model.concPersonModel |> List.map .id |> List.head
, concEquip = Comp.Dropdown.getSelected model.concEquipmentModel |> List.map .id |> List.head
, direction = Comp.Dropdown.getSelected model.directionModel |> List.head |> Maybe.map Data.Direction.toString
, inbox = model.inboxCheckbox
, dateFrom = model.fromDate
, dateUntil = model.untilDate
, dueDateFrom = model.fromDueDate
, dueDateUntil = model.untilDueDate
, name = model.nameModel
let
e =
Api.Model.ItemSearch.empty
in
{ e
| tagsInclude = Comp.Dropdown.getSelected model.tagInclModel |> List.map .id
, tagsExclude = Comp.Dropdown.getSelected model.tagExclModel |> List.map .id
, corrPerson = Comp.Dropdown.getSelected model.corrPersonModel |> List.map .id |> List.head
, corrOrg = Comp.Dropdown.getSelected model.orgModel |> List.map .id |> List.head
, concPerson = Comp.Dropdown.getSelected model.concPersonModel |> List.map .id |> List.head
, concEquip = Comp.Dropdown.getSelected model.concEquipmentModel |> List.map .id |> List.head
, direction = Comp.Dropdown.getSelected model.directionModel |> List.head |> Maybe.map Data.Direction.toString
, inbox = model.inboxCheckbox
, dateFrom = model.fromDate
, dateUntil = model.untilDate
, dueDateFrom = model.fromDueDate
, dueDateUntil = model.untilDueDate
, name = model.nameModel
}
-- Update
type alias NextState
= { modelCmd: (Model, Cmd Msg)
, stateChange: Bool
}
noChange: (Model, Cmd Msg) -> NextState
type alias NextState =
{ modelCmd : ( Model, Cmd Msg )
, stateChange : Bool
}
noChange : ( Model, Cmd Msg ) -> NextState
noChange p =
NextState p False
update: Flags -> Msg -> Model -> NextState
update : Flags -> Msg -> Model -> NextState
update flags msg model =
case msg of
Init ->
let
(dp, dpc) = Comp.DatePicker.init
( dp, dpc ) =
Comp.DatePicker.init
in
noChange ({model|untilDateModel = dp, fromDateModel = dp, untilDueDateModel = dp, fromDueDateModel = dp}
, Cmd.batch
[Api.getTags flags GetTagsResp
,Api.getOrgLight flags GetOrgResp
,Api.getEquipments flags GetEquipResp
,Api.getPersonsLight flags GetPersonResp
,Cmd.map UntilDateMsg dpc
,Cmd.map FromDateMsg dpc
,Cmd.map UntilDueDateMsg dpc
,Cmd.map FromDueDateMsg dpc
]
)
noChange
( { model | untilDateModel = dp, fromDateModel = dp, untilDueDateModel = dp, fromDueDateModel = dp }
, Cmd.batch
[ Api.getTags flags GetTagsResp
, Api.getOrgLight flags GetOrgResp
, Api.getEquipments flags GetEquipResp
, Api.getPersonsLight flags GetPersonResp
, Cmd.map UntilDateMsg dpc
, Cmd.map FromDateMsg dpc
, Cmd.map UntilDueDateMsg dpc
, Cmd.map FromDueDateMsg dpc
]
)
GetTagsResp (Ok tags) ->
let
tagList = Comp.Dropdown.SetOptions tags.items
tagList =
Comp.Dropdown.SetOptions tags.items
in
noChange <|
Util.Update.andThen1
[ update flags (TagIncMsg tagList) >> .modelCmd
, update flags (TagExcMsg tagList) >> .modelCmd
]
model
noChange <|
Util.Update.andThen1
[ update flags (TagIncMsg tagList) >> .modelCmd
, update flags (TagExcMsg tagList) >> .modelCmd
]
model
GetTagsResp (Err err) ->
noChange (model, Cmd.none)
GetTagsResp (Err _) ->
noChange ( model, Cmd.none )
GetEquipResp (Ok equips) ->
let
opts = Comp.Dropdown.SetOptions equips.items
opts =
Comp.Dropdown.SetOptions equips.items
in
update flags (ConcEquipmentMsg opts) model
update flags (ConcEquipmentMsg opts) model
GetEquipResp (Err err) ->
noChange (model, Cmd.none)
GetEquipResp (Err _) ->
noChange ( model, Cmd.none )
GetOrgResp (Ok orgs) ->
let
opts = Comp.Dropdown.SetOptions orgs.items
opts =
Comp.Dropdown.SetOptions orgs.items
in
update flags (OrgMsg opts) model
update flags (OrgMsg opts) model
GetOrgResp (Err err) ->
noChange (model, Cmd.none)
GetOrgResp (Err _) ->
noChange ( model, Cmd.none )
GetPersonResp (Ok ps) ->
let
opts = Comp.Dropdown.SetOptions ps.items
opts =
Comp.Dropdown.SetOptions ps.items
in
noChange <|
Util.Update.andThen1
[ update flags (CorrPersonMsg opts) >> .modelCmd
, update flags (ConcPersonMsg opts) >> .modelCmd
]
model
noChange <|
Util.Update.andThen1
[ update flags (CorrPersonMsg opts) >> .modelCmd
, update flags (ConcPersonMsg opts) >> .modelCmd
]
model
GetPersonResp (Err err) ->
noChange (model, Cmd.none)
GetPersonResp (Err _) ->
noChange ( model, Cmd.none )
TagIncMsg m ->
let
(m2, c2) = Comp.Dropdown.update m model.tagInclModel
( m2, c2 ) =
Comp.Dropdown.update m model.tagInclModel
in
NextState ({model|tagInclModel = m2}, Cmd.map TagIncMsg c2) (isDropdownChangeMsg m)
NextState ( { model | tagInclModel = m2 }, Cmd.map TagIncMsg c2 ) (isDropdownChangeMsg m)
TagExcMsg m ->
let
(m2, c2) = Comp.Dropdown.update m model.tagExclModel
( m2, c2 ) =
Comp.Dropdown.update m model.tagExclModel
in
NextState ({model|tagExclModel = m2}, Cmd.map TagExcMsg c2) (isDropdownChangeMsg m)
NextState ( { model | tagExclModel = m2 }, Cmd.map TagExcMsg c2 ) (isDropdownChangeMsg m)
DirectionMsg m ->
let
(m2, c2) = Comp.Dropdown.update m model.directionModel
( m2, c2 ) =
Comp.Dropdown.update m model.directionModel
in
NextState ({model|directionModel = m2}, Cmd.map DirectionMsg c2) (isDropdownChangeMsg m)
NextState ( { model | directionModel = m2 }, Cmd.map DirectionMsg c2 ) (isDropdownChangeMsg m)
OrgMsg m ->
let
(m2, c2) = Comp.Dropdown.update m model.orgModel
( m2, c2 ) =
Comp.Dropdown.update m model.orgModel
in
NextState ({model|orgModel = m2}, Cmd.map OrgMsg c2) (isDropdownChangeMsg m)
NextState ( { model | orgModel = m2 }, Cmd.map OrgMsg c2 ) (isDropdownChangeMsg m)
CorrPersonMsg m ->
let
(m2, c2) = Comp.Dropdown.update m model.corrPersonModel
( m2, c2 ) =
Comp.Dropdown.update m model.corrPersonModel
in
NextState ({model|corrPersonModel = m2}, Cmd.map CorrPersonMsg c2) (isDropdownChangeMsg m)
NextState ( { model | corrPersonModel = m2 }, Cmd.map CorrPersonMsg c2 ) (isDropdownChangeMsg m)
ConcPersonMsg m ->
let
(m2, c2) = Comp.Dropdown.update m model.concPersonModel
( m2, c2 ) =
Comp.Dropdown.update m model.concPersonModel
in
NextState ({model|concPersonModel = m2}, Cmd.map ConcPersonMsg c2) (isDropdownChangeMsg m)
NextState ( { model | concPersonModel = m2 }, Cmd.map ConcPersonMsg c2 ) (isDropdownChangeMsg m)
ConcEquipmentMsg m ->
let
(m2, c2) = Comp.Dropdown.update m model.concEquipmentModel
( m2, c2 ) =
Comp.Dropdown.update m model.concEquipmentModel
in
NextState ({model|concEquipmentModel = m2}, Cmd.map ConcEquipmentMsg c2) (isDropdownChangeMsg m)
NextState ( { model | concEquipmentModel = m2 }, Cmd.map ConcEquipmentMsg c2 ) (isDropdownChangeMsg m)
ToggleInbox ->
let
current = model.inboxCheckbox
current =
model.inboxCheckbox
in
NextState ({model | inboxCheckbox = not current }, Cmd.none) True
NextState ( { model | inboxCheckbox = not current }, Cmd.none ) True
FromDateMsg m ->
let
(dp, event) = Comp.DatePicker.updateDefault m model.fromDateModel
nextDate = case event of
DatePicker.Picked date ->
Just (Comp.DatePicker.startOfDay date)
_ ->
Nothing
( dp, event ) =
Comp.DatePicker.updateDefault m model.fromDateModel
nextDate =
case event of
DatePicker.Picked date ->
Just (Comp.DatePicker.startOfDay date)
_ ->
Nothing
in
NextState ({model|fromDateModel = dp, fromDate = nextDate}, Cmd.none) (model.fromDate /= nextDate)
NextState ( { model | fromDateModel = dp, fromDate = nextDate }, Cmd.none ) (model.fromDate /= nextDate)
UntilDateMsg m ->
let
(dp, event) = Comp.DatePicker.updateDefault m model.untilDateModel
nextDate = case event of
DatePicker.Picked date ->
Just (Comp.DatePicker.endOfDay date)
_ ->
Nothing
( dp, event ) =
Comp.DatePicker.updateDefault m model.untilDateModel
nextDate =
case event of
DatePicker.Picked date ->
Just (Comp.DatePicker.endOfDay date)
_ ->
Nothing
in
NextState ({model|untilDateModel = dp, untilDate = nextDate}, Cmd.none) (model.untilDate /= nextDate)
NextState ( { model | untilDateModel = dp, untilDate = nextDate }, Cmd.none ) (model.untilDate /= nextDate)
FromDueDateMsg m ->
let
(dp, event) = Comp.DatePicker.updateDefault m model.fromDueDateModel
nextDate = case event of
DatePicker.Picked date ->
Just (Comp.DatePicker.startOfDay date)
_ ->
Nothing
( dp, event ) =
Comp.DatePicker.updateDefault m model.fromDueDateModel
nextDate =
case event of
DatePicker.Picked date ->
Just (Comp.DatePicker.startOfDay date)
_ ->
Nothing
in
NextState ({model|fromDueDateModel = dp, fromDueDate = nextDate}, Cmd.none) (model.fromDueDate /= nextDate)
NextState ( { model | fromDueDateModel = dp, fromDueDate = nextDate }, Cmd.none ) (model.fromDueDate /= nextDate)
UntilDueDateMsg m ->
let
(dp, event) = Comp.DatePicker.updateDefault m model.untilDueDateModel
nextDate = case event of
DatePicker.Picked date ->
Just (Comp.DatePicker.endOfDay date)
_ ->
Nothing
( dp, event ) =
Comp.DatePicker.updateDefault m model.untilDueDateModel
nextDate =
case event of
DatePicker.Picked date ->
Just (Comp.DatePicker.endOfDay date)
_ ->
Nothing
in
NextState ({model|untilDueDateModel = dp, untilDueDate = nextDate}, Cmd.none) (model.untilDueDate /= nextDate)
NextState ( { model | untilDueDateModel = dp, untilDueDate = nextDate }, Cmd.none ) (model.untilDueDate /= nextDate)
SetName str ->
let
next = if str == "" then Nothing else Just str
next =
if str == "" then
Nothing
else
Just str
in
NextState ({model|nameModel = next}, Cmd.none) (model.nameModel /= next)
NextState ( { model | nameModel = next }, Cmd.none ) (model.nameModel /= next)
-- View
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div [class "ui form"]
[div [class "inline field"]
[div [class "ui checkbox"]
[input [type_ "checkbox"
, onCheck (\_ -> ToggleInbox)
, checked model.inboxCheckbox][]
,label [][text "Only New"
]
]
]
,div [class "field"]
[label [][text "Name"]
,input [type_ "text"
,onInput SetName
,model.nameModel |> Maybe.withDefault "" |> value
][]
,span [class "small-info"]
[text "May contain wildcard "
,code [][text "*"]
,text " at beginning or end"
]
]
,div [class "field"]
[label [][text "Direction"]
,Html.map DirectionMsg (Comp.Dropdown.view model.directionModel)
]
,h3 [class "ui header"]
[text "Tags"
div [ class "ui form" ]
[ div [ class "inline field" ]
[ div [ class "ui checkbox" ]
[ input
[ type_ "checkbox"
, onCheck (\_ -> ToggleInbox)
, checked model.inboxCheckbox
]
[]
, label []
[ text "Only New"
]
]
]
,div [class "field"]
[label [][text "Include (and)"]
,Html.map TagIncMsg (Comp.Dropdown.view model.tagInclModel)
]
,div [class "field"]
[label [][text "Exclude (or)"]
,Html.map TagExcMsg (Comp.Dropdown.view model.tagExclModel)
]
,h3 [class "ui header"]
, div [ class "field" ]
[ label [] [ text "Name" ]
, input
[ type_ "text"
, onInput SetName
, model.nameModel |> Maybe.withDefault "" |> value
]
[]
, span [ class "small-info" ]
[ text "May contain wildcard "
, code [] [ text "*" ]
, text " at beginning or end"
]
]
, div [ class "field" ]
[ label [] [ text "Direction" ]
, Html.map DirectionMsg (Comp.Dropdown.view model.directionModel)
]
, h3 [ class "ui header" ]
[ text "Tags"
]
, div [ class "field" ]
[ label [] [ text "Include (and)" ]
, Html.map TagIncMsg (Comp.Dropdown.view model.tagInclModel)
]
, div [ class "field" ]
[ label [] [ text "Exclude (or)" ]
, Html.map TagExcMsg (Comp.Dropdown.view model.tagExclModel)
]
, h3 [ class "ui header" ]
[ case getDirection model of
Just Data.Direction.Incoming -> text "Sender"
Just Data.Direction.Outgoing -> text "Recipient"
Nothing -> text "Correspondent"
Just Data.Direction.Incoming ->
text "Sender"
Just Data.Direction.Outgoing ->
text "Recipient"
Nothing ->
text "Correspondent"
]
,div [class "field"]
[label [][text "Organization"]
,Html.map OrgMsg (Comp.Dropdown.view model.orgModel)
]
,div [class "field"]
[label [][text "Person"]
,Html.map CorrPersonMsg (Comp.Dropdown.view model.corrPersonModel)
]
,h3 [class "ui header"]
[text "Concerned"
, div [ class "field" ]
[ label [] [ text "Organization" ]
, Html.map OrgMsg (Comp.Dropdown.view model.orgModel)
]
,div [class "field"]
[label [][text "Person"]
,Html.map ConcPersonMsg (Comp.Dropdown.view model.concPersonModel)
]
,div [class "field"]
[label [][text "Equipment"]
,Html.map ConcEquipmentMsg (Comp.Dropdown.view model.concEquipmentModel)
]
,h3 [class "ui header"]
[text "Date"
, div [ class "field" ]
[ label [] [ text "Person" ]
, Html.map CorrPersonMsg (Comp.Dropdown.view model.corrPersonModel)
]
,div [class "fields"]
[div [class "field"]
[label [][text "From"
]
,Html.map FromDateMsg (Comp.DatePicker.viewTimeDefault model.fromDate model.fromDateModel)
]
,div [class "field"]
[label [][text "To"
]
,Html.map UntilDateMsg (Comp.DatePicker.viewTimeDefault model.untilDate model.untilDateModel)
]
]
,h3 [class "ui header"]
[text "Due Date"
, h3 [ class "ui header" ]
[ text "Concerned"
]
, div [ class "field" ]
[ label [] [ text "Person" ]
, Html.map ConcPersonMsg (Comp.Dropdown.view model.concPersonModel)
]
, div [ class "field" ]
[ label [] [ text "Equipment" ]
, Html.map ConcEquipmentMsg (Comp.Dropdown.view model.concEquipmentModel)
]
, h3 [ class "ui header" ]
[ text "Date"
]
, div [ class "fields" ]
[ div [ class "field" ]
[ label []
[ text "From"
]
, Html.map FromDateMsg (Comp.DatePicker.viewTimeDefault model.fromDate model.fromDateModel)
]
, div [ class "field" ]
[ label []
[ text "To"
]
, Html.map UntilDateMsg (Comp.DatePicker.viewTimeDefault model.untilDate model.untilDateModel)
]
]
, h3 [ class "ui header" ]
[ text "Due Date"
]
, div [ class "fields" ]
[ div [ class "field" ]
[ label []
[ text "Due From"
]
, Html.map FromDueDateMsg (Comp.DatePicker.viewTimeDefault model.fromDueDate model.fromDueDateModel)
]
, div [ class "field" ]
[ label []
[ text "Due To"
]
, Html.map UntilDueDateMsg (Comp.DatePicker.viewTimeDefault model.untilDueDate model.untilDueDateModel)
]
]
,div [class "fields"]
[div [class "field"]
[label [][text "Due From"
]
,Html.map FromDueDateMsg (Comp.DatePicker.viewTimeDefault model.fromDueDate model.fromDueDateModel)
]
,div [class "field"]
[label [][text "Due To"
]
,Html.map UntilDueDateMsg (Comp.DatePicker.viewTimeDefault model.untilDueDate model.untilDueDateModel)
]
]
]

View File

@ -1,62 +1,88 @@
module Comp.Settings exposing (..)
module Comp.Settings exposing
( Model
, Msg
, getSettings
, init
, update
, view
)
import Api.Model.CollectiveSettings exposing (CollectiveSettings)
import Comp.Dropdown
import Data.Flags exposing (Flags)
import Data.Language exposing (Language)
import Html exposing (..)
import Html.Attributes exposing (..)
import Data.Language exposing (Language)
import Data.Flags exposing (Flags)
import Comp.Dropdown
import Api.Model.CollectiveSettings exposing (CollectiveSettings)
type alias Model =
{ langModel: Comp.Dropdown.Model Language
, initSettings: CollectiveSettings
{ langModel : Comp.Dropdown.Model Language
, initSettings : CollectiveSettings
}
init: CollectiveSettings -> Model
init : CollectiveSettings -> Model
init settings =
let
lang = Data.Language.fromString settings.language |> Maybe.withDefault Data.Language.German
lang =
Data.Language.fromString settings.language
|> Maybe.withDefault Data.Language.German
in
{ langModel = Comp.Dropdown.makeSingleList
{ makeOption = \l -> { value = Data.Language.toIso3 l, text = Data.Language.toName l }
, placeholder = ""
, options = Data.Language.all
, selected = Just lang
}
, initSettings = settings
}
{ langModel =
Comp.Dropdown.makeSingleList
{ makeOption =
\l ->
{ value = Data.Language.toIso3 l
, text = Data.Language.toName l
}
, placeholder = ""
, options = Data.Language.all
, selected = Just lang
}
, initSettings = settings
}
getSettings: Model -> CollectiveSettings
getSettings : Model -> CollectiveSettings
getSettings model =
CollectiveSettings
(Comp.Dropdown.getSelected model.langModel
|> List.head
|> Maybe.map Data.Language.toIso3
|> Maybe.withDefault model.initSettings.language
|> List.head
|> Maybe.map Data.Language.toIso3
|> Maybe.withDefault model.initSettings.language
)
type Msg
= LangDropdownMsg (Comp.Dropdown.Msg Language)
update: Flags -> Msg -> Model -> (Model, Cmd Msg, Maybe CollectiveSettings)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Maybe CollectiveSettings )
update flags msg model =
case msg of
LangDropdownMsg m ->
let
(m2, c2) = Comp.Dropdown.update m model.langModel
nextModel = {model|langModel = m2}
nextSettings = if Comp.Dropdown.isDropdownChangeMsg m then Just (getSettings nextModel)
else Nothing
( m2, c2 ) =
Comp.Dropdown.update m model.langModel
nextModel =
{ model | langModel = m2 }
nextSettings =
if Comp.Dropdown.isDropdownChangeMsg m then
Just (getSettings nextModel)
else
Nothing
in
(nextModel, Cmd.map LangDropdownMsg c2, nextSettings)
( nextModel, Cmd.map LangDropdownMsg c2, nextSettings )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div [class "ui form"]
[div [class "field"]
[label [][text "Document Language"]
,Html.map LangDropdownMsg (Comp.Dropdown.view model.langModel)
]
div [ class "ui form" ]
[ div [ class "field" ]
[ label [] [ text "Document Language" ]
, Html.map LangDropdownMsg (Comp.Dropdown.view model.langModel)
]
]

View File

@ -1,60 +1,68 @@
module Comp.SourceForm exposing ( Model
, emptyModel
, Msg(..)
, view
, update
, isValid
, getSource)
module Comp.SourceForm exposing
( Model
, Msg(..)
, emptyModel
, getSource
, isValid
, update
, view
)
import Api.Model.Source exposing (Source)
import Comp.Dropdown
import Data.Flags exposing (Flags)
import Data.Priority exposing (Priority)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput, onCheck)
import Data.Flags exposing (Flags)
import Data.SourceState exposing (SourceState)
import Data.Priority exposing (Priority)
import Comp.Dropdown
import Api.Model.Source exposing (Source)
import Util.Maybe
import Html.Events exposing (onCheck, onInput)
type alias Model =
{ source: Source
, abbrev: String
, description: Maybe String
, priority: Comp.Dropdown.Model Priority
, enabled: Bool
{ source : Source
, abbrev : String
, description : Maybe String
, priority : Comp.Dropdown.Model Priority
, enabled : Bool
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ source = Api.Model.Source.empty
, abbrev = ""
, description = Nothing
, priority = Comp.Dropdown.makeSingleList
{ makeOption = \p -> { text = Data.Priority.toName p, value = Data.Priority.toName p }
, placeholder = ""
, options = Data.Priority.all
, selected = Nothing
}
, priority =
Comp.Dropdown.makeSingleList
{ makeOption = \p -> { text = Data.Priority.toName p, value = Data.Priority.toName p }
, placeholder = ""
, options = Data.Priority.all
, selected = Nothing
}
, enabled = False
}
isValid: Model -> Bool
isValid : Model -> Bool
isValid model =
model.abbrev /= ""
getSource: Model -> Source
getSource : Model -> Source
getSource model =
let
s = model.source
s =
model.source
in
{s | abbrev = model.abbrev
, description = model.description
, enabled = model.enabled
, priority = Comp.Dropdown.getSelected model.priority
|> List.head
|> Maybe.map Data.Priority.toName
|> Maybe.withDefault s.priority
}
{ s
| abbrev = model.abbrev
, description = model.description
, enabled = model.enabled
, priority =
Comp.Dropdown.getSelected model.priority
|> List.head
|> Maybe.map Data.Priority.toName
|> Maybe.withDefault s.priority
}
type Msg
@ -64,105 +72,138 @@ type Msg
| ToggleEnabled
| PrioDropdownMsg (Comp.Dropdown.Msg Priority)
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetSource t ->
let
post = model.source
np = {post | id = t.id
, abbrev = t.abbrev
, description = t.description
, priority = t.priority
, enabled = t.enabled
}
post =
model.source
np =
{ post
| id = t.id
, abbrev = t.abbrev
, description = t.description
, priority = t.priority
, enabled = t.enabled
}
in
({model | source = np
, abbrev = t.abbrev
, description = t.description
, priority = Comp.Dropdown.makeSingleList
{ makeOption = \p -> { text = Data.Priority.toName p, value = Data.Priority.toName p }
, placeholder = ""
, options = Data.Priority.all
, selected = Data.Priority.fromString t.priority
}
, enabled = t.enabled }, Cmd.none)
( { model
| source = np
, abbrev = t.abbrev
, description = t.description
, priority =
Comp.Dropdown.makeSingleList
{ makeOption = \p -> { text = Data.Priority.toName p, value = Data.Priority.toName p }
, placeholder = ""
, options = Data.Priority.all
, selected = Data.Priority.fromString t.priority
}
, enabled = t.enabled
}
, Cmd.none
)
ToggleEnabled ->
let
_ = Debug.log "got" model.enabled
in
({model | enabled = not model.enabled}, Cmd.none)
( { model | enabled = not model.enabled }, Cmd.none )
SetAbbrev n ->
({model | abbrev = n}, Cmd.none)
( { model | abbrev = n }, Cmd.none )
SetDescr d ->
({model | description = if d /= "" then Just d else Nothing }, Cmd.none)
( { model
| description =
if d /= "" then
Just d
else
Nothing
}
, Cmd.none
)
PrioDropdownMsg m ->
let
(m2, c2) = Comp.Dropdown.update m model.priority
( m2, c2 ) =
Comp.Dropdown.update m model.priority
in
({model | priority = m2 }, Cmd.map PrioDropdownMsg c2)
( { model | priority = m2 }, Cmd.map PrioDropdownMsg c2 )
view: Flags -> Model -> Html Msg
view : Flags -> Model -> Html Msg
view flags model =
div [class "ui form"]
[div [classList [("field", True)
,("error", not (isValid model))
]
]
[label [][text "Abbrev*"]
,input [type_ "text"
,onInput SetAbbrev
,placeholder "Abbrev"
,value model.abbrev
][]
]
,div [class "field"]
[label [][text "Description"]
,textarea [onInput SetDescr][model.description |> Maybe.withDefault "" |> text]
div [ class "ui form" ]
[ div
[ classList
[ ( "field", True )
, ( "error", not (isValid model) )
]
]
,div [class "inline field"]
[div [class "ui checkbox"]
[input [type_ "checkbox"
, onCheck (\_ -> ToggleEnabled)
, checked model.enabled][]
,label [][text "Enabled"]
]
]
,div [class "field"]
[label [][text "Priority"]
,Html.map PrioDropdownMsg (Comp.Dropdown.view model.priority)
[ label [] [ text "Abbrev*" ]
, input
[ type_ "text"
, onInput SetAbbrev
, placeholder "Abbrev"
, value model.abbrev
]
[]
]
,urlInfoMessage flags model
, div [ class "field" ]
[ label [] [ text "Description" ]
, textarea [ onInput SetDescr ] [ model.description |> Maybe.withDefault "" |> text ]
]
, div [ class "inline field" ]
[ div [ class "ui checkbox" ]
[ input
[ type_ "checkbox"
, onCheck (\_ -> ToggleEnabled)
, checked model.enabled
]
[]
, label [] [ text "Enabled" ]
]
]
, div [ class "field" ]
[ label [] [ text "Priority" ]
, Html.map PrioDropdownMsg (Comp.Dropdown.view model.priority)
]
, urlInfoMessage flags model
]
urlInfoMessage: Flags -> Model -> Html Msg
urlInfoMessage : Flags -> Model -> Html Msg
urlInfoMessage flags model =
div [classList [("ui info icon message", True)
,("hidden", not model.enabled || model.source.id == "")
]]
[i [class "info icon"][]
,div [class "content"]
[div [class "header"]
[text "Public Uploads"
]
,p [][text "This source defines URLs that can be used by anyone to send files to "
,text "you. There is a web page that you can share or tha API url can be used "
,text "with other clients."
]
,dl [class "ui list"]
[dt [][text "Public Upload Page"]
,dd [][let
url = flags.config.baseUrl ++ "/app/index.html#/upload/" ++ model.source.id
in
a [href url, target "_blank"][code [][text url]]
]
,dt [][text "Public API Upload URL"]
,dd [][code [][text (flags.config.baseUrl ++ "/api/v1/open/upload/item/" ++ model.source.id)]
]
div
[ classList
[ ( "ui info icon message", True )
, ( "hidden", not model.enabled || model.source.id == "" )
]
]
[ i [ class "info icon" ] []
, div [ class "content" ]
[ div [ class "header" ]
[ text "Public Uploads"
]
, p []
[ text "This source defines URLs that can be used by anyone to send files to "
, text "you. There is a web page that you can share or tha API url can be used "
, text "with other clients."
]
, dl [ class "ui list" ]
[ dt [] [ text "Public Upload Page" ]
, dd []
[ let
url =
flags.config.baseUrl ++ "/app/index.html#/upload/" ++ model.source.id
in
a [ href url, target "_blank" ] [ code [] [ text url ] ]
]
, dt [] [ text "Public API Upload URL" ]
, dd []
[ code [] [ text (flags.config.baseUrl ++ "/api/v1/open/upload/item/" ++ model.source.id) ]
]
]
]
]

View File

@ -1,36 +1,43 @@
module Comp.SourceManage exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.SourceManage exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Http
import Api
import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.Source
import Api.Model.SourceList exposing (SourceList)
import Comp.SourceForm
import Comp.SourceTable
import Comp.YesNoDimmer
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onSubmit)
import Data.Flags exposing (Flags)
import Comp.SourceTable
import Comp.SourceForm
import Comp.YesNoDimmer
import Api.Model.Source
import Api.Model.SourceList exposing (SourceList)
import Api.Model.BasicResult exposing (BasicResult)
import Util.Maybe
import Http
import Util.Http
import Util.Maybe
type alias Model =
{ tableModel: Comp.SourceTable.Model
, formModel: Comp.SourceForm.Model
, viewMode: ViewMode
, formError: Maybe String
, loading: Bool
, deleteConfirm: Comp.YesNoDimmer.Model
{ tableModel : Comp.SourceTable.Model
, formModel : Comp.SourceForm.Model
, viewMode : ViewMode
, formError : Maybe String
, loading : Bool
, deleteConfirm : Comp.YesNoDimmer.Model
}
type ViewMode = Table | Form
emptyModel: Model
type ViewMode
= Table
| Form
emptyModel : Model
emptyModel =
{ tableModel = Comp.SourceTable.emptyModel
, formModel = Comp.SourceForm.emptyModel
@ -40,6 +47,7 @@ emptyModel =
, deleteConfirm = Comp.YesNoDimmer.emptyModel
}
type Msg
= TableMsg Comp.SourceTable.Msg
| FormMsg Comp.SourceForm.Msg
@ -52,156 +60,211 @@ type Msg
| YesNoMsg Comp.YesNoDimmer.Msg
| RequestDelete
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
TableMsg m ->
let
(tm, tc) = Comp.SourceTable.update flags m model.tableModel
(m2, c2) = ({model | tableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError = if Util.Maybe.nonEmpty tm.selected then Nothing else model.formError
}
, Cmd.map TableMsg tc
)
(m3, c3) = case tm.selected of
( tm, tc ) =
Comp.SourceTable.update flags m model.tableModel
( m2, c2 ) =
( { model
| tableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError =
if Util.Maybe.nonEmpty tm.selected then
Nothing
else
model.formError
}
, Cmd.map TableMsg tc
)
( m3, c3 ) =
case tm.selected of
Just source ->
update flags (FormMsg (Comp.SourceForm.SetSource source)) m2
Nothing ->
(m2, Cmd.none)
( m2, Cmd.none )
in
(m3, Cmd.batch [c2, c3])
( m3, Cmd.batch [ c2, c3 ] )
FormMsg m ->
let
(m2, c2) = Comp.SourceForm.update flags m model.formModel
( m2, c2 ) =
Comp.SourceForm.update flags m model.formModel
in
({model | formModel = m2}, Cmd.map FormMsg c2)
( { model | formModel = m2 }, Cmd.map FormMsg c2 )
LoadSources ->
({model| loading = True}, Api.getSources flags SourceResp)
( { model | loading = True }, Api.getSources flags SourceResp )
SourceResp (Ok sources) ->
let
m2 = {model|viewMode = Table, loading = False}
m2 =
{ model | viewMode = Table, loading = False }
in
update flags (TableMsg (Comp.SourceTable.SetSources sources.items)) m2
update flags (TableMsg (Comp.SourceTable.SetSources sources.items)) m2
SourceResp (Err err) ->
({model|loading = False}, Cmd.none)
SourceResp (Err _) ->
( { model | loading = False }, Cmd.none )
SetViewMode m ->
let
m2 = {model | viewMode = m }
m2 =
{ model | viewMode = m }
in
case m of
Table ->
update flags (TableMsg Comp.SourceTable.Deselect) m2
Form ->
(m2, Cmd.none)
case m of
Table ->
update flags (TableMsg Comp.SourceTable.Deselect) m2
Form ->
( m2, Cmd.none )
InitNewSource ->
let
nm = {model | viewMode = Form, formError = Nothing }
source = Api.Model.Source.empty
nm =
{ model | viewMode = Form, formError = Nothing }
source =
Api.Model.Source.empty
in
update flags (FormMsg (Comp.SourceForm.SetSource source)) nm
update flags (FormMsg (Comp.SourceForm.SetSource source)) nm
Submit ->
let
source = Comp.SourceForm.getSource model.formModel
valid = Comp.SourceForm.isValid model.formModel
in if valid then
({model|loading = True}, Api.postSource flags source SubmitResp)
else
({model|formError = Just "Please correct the errors in the form."}, Cmd.none)
source =
Comp.SourceForm.getSource model.formModel
valid =
Comp.SourceForm.isValid model.formModel
in
if valid then
( { model | loading = True }, Api.postSource flags source SubmitResp )
else
( { model | formError = Just "Please correct the errors in the form." }, Cmd.none )
SubmitResp (Ok res) ->
if res.success then
let
(m2, c2) = update flags (SetViewMode Table) model
(m3, c3) = update flags LoadSources m2
( m2, c2 ) =
update flags (SetViewMode Table) model
( m3, c3 ) =
update flags LoadSources m2
in
({m3|loading = False}, Cmd.batch [c2,c3])
( { m3 | loading = False }, Cmd.batch [ c2, c3 ] )
else
({model | formError = Just res.message, loading = False }, Cmd.none)
( { model | formError = Just res.message, loading = False }, Cmd.none )
SubmitResp (Err err) ->
({model | formError = Just (Util.Http.errorToString err), loading = False}, Cmd.none)
( { model | formError = Just (Util.Http.errorToString err), loading = False }, Cmd.none )
RequestDelete ->
update flags (YesNoMsg Comp.YesNoDimmer.activate) model
YesNoMsg m ->
let
(cm, confirmed) = Comp.YesNoDimmer.update m model.deleteConfirm
src = Comp.SourceForm.getSource model.formModel
cmd = if confirmed then Api.deleteSource flags src.id SubmitResp else Cmd.none
( cm, confirmed ) =
Comp.YesNoDimmer.update m model.deleteConfirm
src =
Comp.SourceForm.getSource model.formModel
cmd =
if confirmed then
Api.deleteSource flags src.id SubmitResp
else
Cmd.none
in
({model | deleteConfirm = cm}, cmd)
( { model | deleteConfirm = cm }, cmd )
view: Flags -> Model -> Html Msg
view : Flags -> Model -> Html Msg
view flags model =
if model.viewMode == Table then viewTable model
else div [](viewForm flags model)
if model.viewMode == Table then
viewTable model
viewTable: Model -> Html Msg
else
div [] (viewForm flags model)
viewTable : Model -> Html Msg
viewTable model =
div []
[button [class "ui basic button", onClick InitNewSource]
[i [class "plus icon"][]
,text "Create new"
]
,Html.map TableMsg (Comp.SourceTable.view model.tableModel)
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
[ button [ class "ui basic button", onClick InitNewSource ]
[ i [ class "plus icon" ] []
, text "Create new"
]
, Html.map TableMsg (Comp.SourceTable.view model.tableModel)
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]
viewForm: Flags -> Model -> List (Html Msg)
viewForm : Flags -> Model -> List (Html Msg)
viewForm flags model =
let
newSource = model.formModel.source.id == ""
newSource =
model.formModel.source.id == ""
in
[if newSource then
h3 [class "ui top attached header"]
[text "Create new source"
]
else
h3 [class "ui top attached header"]
[text ("Edit: " ++ model.formModel.source.abbrev)
,div [class "sub header"]
[text "Id: "
,text model.formModel.source.id
]
]
,Html.form [class "ui attached segment", onSubmit Submit]
[Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
,Html.map FormMsg (Comp.SourceForm.view flags model.formModel)
,div [classList [("ui error message", True)
,("invisible", Util.Maybe.isEmpty model.formError)
]
]
[Maybe.withDefault "" model.formError |> text
]
,div [class "ui horizontal divider"][]
,button [class "ui primary button", type_ "submit"]
[text "Submit"
[ if newSource then
h3 [ class "ui top attached header" ]
[ text "Create new source"
]
else
h3 [ class "ui top attached header" ]
[ text ("Edit: " ++ model.formModel.source.abbrev)
, div [ class "sub header" ]
[ text "Id: "
, text model.formModel.source.id
]
,a [class "ui secondary button", onClick (SetViewMode Table), href ""]
[text "Cancel"
]
, Html.form [ class "ui attached segment", onSubmit Submit ]
[ Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
, Html.map FormMsg (Comp.SourceForm.view flags model.formModel)
, div
[ classList
[ ( "ui error message", True )
, ( "invisible", Util.Maybe.isEmpty model.formError )
]
,if not newSource then
a [class "ui right floated red button", href "", onClick RequestDelete]
[text "Delete"]
else
span[][]
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
]
]
[ Maybe.withDefault "" model.formError |> text
]
, div [ class "ui horizontal divider" ] []
, button [ class "ui primary button", type_ "submit" ]
[ text "Submit"
]
, a [ class "ui secondary button", onClick (SetViewMode Table), href "" ]
[ text "Cancel"
]
, if not newSource then
a [ class "ui right floated red button", href "", onClick RequestDelete ]
[ text "Delete" ]
else
span [] []
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]
]

View File

@ -1,85 +1,94 @@
module Comp.SourceTable exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.SourceTable exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Api.Model.Source exposing (Source)
import Data.Flags exposing (Flags)
import Data.Priority
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Data.Flags exposing (Flags)
import Data.Priority exposing (Priority)
import Api.Model.Source exposing (Source)
type alias Model =
{ sources: List Source
, selected: Maybe Source
{ sources : List Source
, selected : Maybe Source
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ sources = []
, selected = Nothing
}
type Msg
= SetSources (List Source)
| Select Source
| Deselect
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetSources list ->
({model | sources = list, selected = Nothing }, Cmd.none)
( { model | sources = list, selected = Nothing }, Cmd.none )
Select source ->
({model | selected = Just source}, Cmd.none)
( { model | selected = Just source }, Cmd.none )
Deselect ->
({model | selected = Nothing}, Cmd.none)
( { model | selected = Nothing }, Cmd.none )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
table [class "ui selectable table"]
[thead []
[tr []
[th [class "collapsing"][text "Abbrev"]
,th [class "collapsing"][text "Enabled"]
,th [class "collapsing"][text "Counter"]
,th [class "collapsing"][text "Priority"]
,th [][text "Id"]
]
]
,tbody []
table [ class "ui selectable table" ]
[ thead []
[ tr []
[ th [ class "collapsing" ] [ text "Abbrev" ]
, th [ class "collapsing" ] [ text "Enabled" ]
, th [ class "collapsing" ] [ text "Counter" ]
, th [ class "collapsing" ] [ text "Priority" ]
, th [] [ text "Id" ]
]
]
, tbody []
(List.map (renderSourceLine model) model.sources)
]
renderSourceLine: Model -> Source -> Html Msg
renderSourceLine : Model -> Source -> Html Msg
renderSourceLine model source =
tr [classList [("active", model.selected == Just source)]
,onClick (Select source)
]
[td [class "collapsing"]
[text source.abbrev
]
,td [class "collapsing"]
[if source.enabled then
i [class "check square outline icon"][]
else
i [class "minus square outline icon"][]
tr
[ classList [ ( "active", model.selected == Just source ) ]
, onClick (Select source)
]
[ td [ class "collapsing" ]
[ text source.abbrev
]
,td [class "collapsing"]
[source.counter |> String.fromInt |> text
, td [ class "collapsing" ]
[ if source.enabled then
i [ class "check square outline icon" ] []
else
i [ class "minus square outline icon" ] []
]
,td [class "collapsing"]
[Data.Priority.fromString source.priority
|> Maybe.map Data.Priority.toName
|> Maybe.withDefault source.priority
|> text
, td [ class "collapsing" ]
[ source.counter |> String.fromInt |> text
]
,td []
[text source.id
, td [ class "collapsing" ]
[ Data.Priority.fromString source.priority
|> Maybe.map Data.Priority.toName
|> Maybe.withDefault source.priority
|> text
]
, td []
[ text source.id
]
]

View File

@ -1,76 +1,90 @@
module Comp.TagForm exposing ( Model
, emptyModel
, Msg(..)
, view
, update
, isValid
, getTag)
module Comp.TagForm exposing
( Model
, Msg(..)
, emptyModel
, getTag
, isValid
, update
, view
)
import Api.Model.Tag exposing (Tag)
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
import Data.Flags exposing (Flags)
import Api.Model.Tag exposing (Tag)
type alias Model =
{ tag: Tag
, name: String
, category: Maybe String
{ tag : Tag
, name : String
, category : Maybe String
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ tag = Api.Model.Tag.empty
, name = ""
, category = Nothing
}
isValid: Model -> Bool
isValid : Model -> Bool
isValid model =
model.name /= ""
getTag: Model -> Tag
getTag : Model -> Tag
getTag model =
Tag model.tag.id model.name model.category 0
type Msg
= SetName String
| SetCategory String
| SetTag Tag
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetTag t ->
({model | tag = t, name = t.name, category = t.category }, Cmd.none)
( { model | tag = t, name = t.name, category = t.category }, Cmd.none )
SetName n ->
({model | name = n}, Cmd.none)
( { model | name = n }, Cmd.none )
SetCategory n ->
({model | category = Just n}, Cmd.none)
( { model | category = Just n }, Cmd.none )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div [class "ui form"]
[div [classList [("field", True)
,("error", not (isValid model))
]
]
[label [][text "Name*"]
,input [type_ "text"
,onInput SetName
,placeholder "Name"
,value model.name
][]
]
,div [class "field"]
[label [][text "Category"]
,input [type_ "text"
,onInput SetCategory
,placeholder "Category (optional)"
,value (Maybe.withDefault "" model.category)
][]
div [ class "ui form" ]
[ div
[ classList
[ ( "field", True )
, ( "error", not (isValid model) )
]
]
[ label [] [ text "Name*" ]
, input
[ type_ "text"
, onInput SetName
, placeholder "Name"
, value model.name
]
[]
]
, div [ class "field" ]
[ label [] [ text "Category" ]
, input
[ type_ "text"
, onInput SetCategory
, placeholder "Category (optional)"
, value (Maybe.withDefault "" model.category)
]
[]
]
]

View File

@ -1,36 +1,43 @@
module Comp.TagManage exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.TagManage exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Http
import Api
import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.Tag
import Api.Model.TagList exposing (TagList)
import Comp.TagForm
import Comp.TagTable
import Comp.YesNoDimmer
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onSubmit)
import Data.Flags exposing (Flags)
import Comp.TagTable
import Comp.TagForm
import Comp.YesNoDimmer
import Api.Model.Tag
import Api.Model.TagList exposing (TagList)
import Api.Model.BasicResult exposing (BasicResult)
import Util.Maybe
import Http
import Util.Http
import Util.Maybe
type alias Model =
{ tagTableModel: Comp.TagTable.Model
, tagFormModel: Comp.TagForm.Model
, viewMode: ViewMode
, formError: Maybe String
, loading: Bool
, deleteConfirm: Comp.YesNoDimmer.Model
{ tagTableModel : Comp.TagTable.Model
, tagFormModel : Comp.TagForm.Model
, viewMode : ViewMode
, formError : Maybe String
, loading : Bool
, deleteConfirm : Comp.YesNoDimmer.Model
}
type ViewMode = Table | Form
emptyModel: Model
type ViewMode
= Table
| Form
emptyModel : Model
emptyModel =
{ tagTableModel = Comp.TagTable.emptyModel
, tagFormModel = Comp.TagForm.emptyModel
@ -40,6 +47,7 @@ emptyModel =
, deleteConfirm = Comp.YesNoDimmer.emptyModel
}
type Msg
= TableMsg Comp.TagTable.Msg
| FormMsg Comp.TagForm.Msg
@ -52,155 +60,210 @@ type Msg
| YesNoMsg Comp.YesNoDimmer.Msg
| RequestDelete
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
TableMsg m ->
let
(tm, tc) = Comp.TagTable.update flags m model.tagTableModel
(m2, c2) = ({model | tagTableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError = if Util.Maybe.nonEmpty tm.selected then Nothing else model.formError
}
, Cmd.map TableMsg tc
)
(m3, c3) = case tm.selected of
( tm, tc ) =
Comp.TagTable.update flags m model.tagTableModel
( m2, c2 ) =
( { model
| tagTableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError =
if Util.Maybe.nonEmpty tm.selected then
Nothing
else
model.formError
}
, Cmd.map TableMsg tc
)
( m3, c3 ) =
case tm.selected of
Just tag ->
update flags (FormMsg (Comp.TagForm.SetTag tag)) m2
Nothing ->
(m2, Cmd.none)
( m2, Cmd.none )
in
(m3, Cmd.batch [c2, c3])
( m3, Cmd.batch [ c2, c3 ] )
FormMsg m ->
let
(m2, c2) = Comp.TagForm.update flags m model.tagFormModel
( m2, c2 ) =
Comp.TagForm.update flags m model.tagFormModel
in
({model | tagFormModel = m2}, Cmd.map FormMsg c2)
( { model | tagFormModel = m2 }, Cmd.map FormMsg c2 )
LoadTags ->
({model| loading = True}, Api.getTags flags TagResp)
( { model | loading = True }, Api.getTags flags TagResp )
TagResp (Ok tags) ->
let
m2 = {model|viewMode = Table, loading = False}
m2 =
{ model | viewMode = Table, loading = False }
in
update flags (TableMsg (Comp.TagTable.SetTags tags.items)) m2
update flags (TableMsg (Comp.TagTable.SetTags tags.items)) m2
TagResp (Err err) ->
({model|loading = False}, Cmd.none)
TagResp (Err _) ->
( { model | loading = False }, Cmd.none )
SetViewMode m ->
let
m2 = {model | viewMode = m }
m2 =
{ model | viewMode = m }
in
case m of
Table ->
update flags (TableMsg Comp.TagTable.Deselect) m2
Form ->
(m2, Cmd.none)
case m of
Table ->
update flags (TableMsg Comp.TagTable.Deselect) m2
Form ->
( m2, Cmd.none )
InitNewTag ->
let
nm = {model | viewMode = Form, formError = Nothing }
tag = Api.Model.Tag.empty
nm =
{ model | viewMode = Form, formError = Nothing }
tag =
Api.Model.Tag.empty
in
update flags (FormMsg (Comp.TagForm.SetTag tag)) nm
update flags (FormMsg (Comp.TagForm.SetTag tag)) nm
Submit ->
let
tag = Comp.TagForm.getTag model.tagFormModel
valid = Comp.TagForm.isValid model.tagFormModel
in if valid then
({model|loading = True}, Api.postTag flags tag SubmitResp)
else
({model|formError = Just "Please correct the errors in the form."}, Cmd.none)
tag =
Comp.TagForm.getTag model.tagFormModel
valid =
Comp.TagForm.isValid model.tagFormModel
in
if valid then
( { model | loading = True }, Api.postTag flags tag SubmitResp )
else
( { model | formError = Just "Please correct the errors in the form." }, Cmd.none )
SubmitResp (Ok res) ->
if res.success then
let
(m2, c2) = update flags (SetViewMode Table) model
(m3, c3) = update flags LoadTags m2
( m2, c2 ) =
update flags (SetViewMode Table) model
( m3, c3 ) =
update flags LoadTags m2
in
({m3|loading = False}, Cmd.batch [c2,c3])
( { m3 | loading = False }, Cmd.batch [ c2, c3 ] )
else
({model | formError = Just res.message, loading = False }, Cmd.none)
( { model | formError = Just res.message, loading = False }, Cmd.none )
SubmitResp (Err err) ->
({model | formError = Just (Util.Http.errorToString err), loading = False}, Cmd.none)
( { model | formError = Just (Util.Http.errorToString err), loading = False }, Cmd.none )
RequestDelete ->
update flags (YesNoMsg Comp.YesNoDimmer.activate) model
YesNoMsg m ->
let
(cm, confirmed) = Comp.YesNoDimmer.update m model.deleteConfirm
tag = Comp.TagForm.getTag model.tagFormModel
cmd = if confirmed then Api.deleteTag flags tag.id SubmitResp else Cmd.none
( cm, confirmed ) =
Comp.YesNoDimmer.update m model.deleteConfirm
tag =
Comp.TagForm.getTag model.tagFormModel
cmd =
if confirmed then
Api.deleteTag flags tag.id SubmitResp
else
Cmd.none
in
({model | deleteConfirm = cm}, cmd)
( { model | deleteConfirm = cm }, cmd )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
if model.viewMode == Table then viewTable model
else viewForm model
if model.viewMode == Table then
viewTable model
viewTable: Model -> Html Msg
else
viewForm model
viewTable : Model -> Html Msg
viewTable model =
div []
[button [class "ui basic button", onClick InitNewTag]
[i [class "plus icon"][]
,text "Create new"
]
,Html.map TableMsg (Comp.TagTable.view model.tagTableModel)
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
[ button [ class "ui basic button", onClick InitNewTag ]
[ i [ class "plus icon" ] []
, text "Create new"
]
, Html.map TableMsg (Comp.TagTable.view model.tagTableModel)
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]
viewForm: Model -> Html Msg
viewForm : Model -> Html Msg
viewForm model =
let
newTag = model.tagFormModel.tag.id == ""
newTag =
model.tagFormModel.tag.id == ""
in
Html.form [class "ui segment", onSubmit Submit]
[Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
,if newTag then
h3 [class "ui dividing header"]
[text "Create new tag"
]
else
h3 [class "ui dividing header"]
[text ("Edit tag: " ++ model.tagFormModel.tag.name)
,div [class "sub header"]
[text "Id: "
,text model.tagFormModel.tag.id
]
]
,Html.map FormMsg (Comp.TagForm.view model.tagFormModel)
,div [classList [("ui error message", True)
,("invisible", Util.Maybe.isEmpty model.formError)
]
]
[Maybe.withDefault "" model.formError |> text
]
,div [class "ui horizontal divider"][]
,button [class "ui primary button", type_ "submit"]
[text "Submit"
Html.form [ class "ui segment", onSubmit Submit ]
[ Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
, if newTag then
h3 [ class "ui dividing header" ]
[ text "Create new tag"
]
,a [class "ui secondary button", onClick (SetViewMode Table), href ""]
[text "Cancel"
else
h3 [ class "ui dividing header" ]
[ text ("Edit tag: " ++ model.tagFormModel.tag.name)
, div [ class "sub header" ]
[ text "Id: "
, text model.tagFormModel.tag.id
]
]
, Html.map FormMsg (Comp.TagForm.view model.tagFormModel)
, div
[ classList
[ ( "ui error message", True )
, ( "invisible", Util.Maybe.isEmpty model.formError )
]
,if not newTag then
a [class "ui right floated red button", href "", onClick RequestDelete]
[text "Delete"]
else
span[][]
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
]
]
[ Maybe.withDefault "" model.formError |> text
]
, div [ class "ui horizontal divider" ] []
, button [ class "ui primary button", type_ "submit" ]
[ text "Submit"
]
, a [ class "ui secondary button", onClick (SetViewMode Table), href "" ]
[ text "Cancel"
]
, if not newTag then
a [ class "ui right floated red button", href "", onClick RequestDelete ]
[ text "Delete" ]
else
span [] []
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]

View File

@ -1,66 +1,74 @@
module Comp.TagTable exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.TagTable exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Api.Model.Tag exposing (Tag)
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Data.Flags exposing (Flags)
import Api.Model.Tag exposing (Tag)
type alias Model =
{ tags: List Tag
, selected: Maybe Tag
{ tags : List Tag
, selected : Maybe Tag
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ tags = []
, selected = Nothing
}
type Msg
= SetTags (List Tag)
| Select Tag
| Deselect
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetTags list ->
({model | tags = list, selected = Nothing }, Cmd.none)
( { model | tags = list, selected = Nothing }, Cmd.none )
Select tag ->
({model | selected = Just tag}, Cmd.none)
( { model | selected = Just tag }, Cmd.none )
Deselect ->
({model | selected = Nothing}, Cmd.none)
( { model | selected = Nothing }, Cmd.none )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
table [class "ui selectable table"]
[thead []
[tr []
[th [][text "Name"]
,th [][text "Category"]
]
]
,tbody []
table [ class "ui selectable table" ]
[ thead []
[ tr []
[ th [] [ text "Name" ]
, th [] [ text "Category" ]
]
]
, tbody []
(List.map (renderTagLine model) model.tags)
]
renderTagLine: Model -> Tag -> Html Msg
renderTagLine : Model -> Tag -> Html Msg
renderTagLine model tag =
tr [classList [("active", model.selected == Just tag)]
,onClick (Select tag)
]
[td []
[text tag.name
]
,td []
[Maybe.withDefault "-" tag.category |> text
tr
[ classList [ ( "active", model.selected == Just tag ) ]
, onClick (Select tag)
]
[ td []
[ text tag.name
]
, td []
[ Maybe.withDefault "-" tag.category |> text
]
]

View File

@ -1,68 +1,85 @@
module Comp.UserForm exposing ( Model
, emptyModel
, Msg(..)
, view
, update
, isValid
, isNewUser
, getUser)
module Comp.UserForm exposing
( Model
, Msg(..)
, emptyModel
, getUser
, isNewUser
, isValid
, update
, view
)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput, onCheck)
import Api.Model.User exposing (User)
import Comp.Dropdown
import Data.Flags exposing (Flags)
import Data.UserState exposing (UserState)
import Api.Model.User exposing (User)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
import Util.Maybe
import Comp.Dropdown
type alias Model =
{ user: User
, login: String
, email: Maybe String
, state: Comp.Dropdown.Model UserState
, password: Maybe String
{ user : User
, login : String
, email : Maybe String
, state : Comp.Dropdown.Model UserState
, password : Maybe String
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ user = Api.Model.User.empty
, login = ""
, email = Nothing
, password = Nothing
, state = Comp.Dropdown.makeSingleList
{ makeOption = \s -> { value = Data.UserState.toString s, text = Data.UserState.toString s }
, placeholder = ""
, options = Data.UserState.all
, selected = List.head Data.UserState.all
}
, state =
Comp.Dropdown.makeSingleList
{ makeOption =
\s ->
{ value = Data.UserState.toString s
, text = Data.UserState.toString s
}
, placeholder = ""
, options = Data.UserState.all
, selected = List.head Data.UserState.all
}
}
isValid: Model -> Bool
isValid : Model -> Bool
isValid model =
if model.user.login == "" then
model.login /= "" && Util.Maybe.nonEmpty model.password
else
True
isNewUser: Model -> Bool
isNewUser : Model -> Bool
isNewUser model =
model.user.login == ""
getUser: Model -> User
getUser : Model -> User
getUser model =
let
s = model.user
state = Comp.Dropdown.getSelected model.state
s =
model.user
state =
Comp.Dropdown.getSelected model.state
|> List.head
|> Maybe.withDefault Data.UserState.Active
|> Data.UserState.toString
in
{s | login = model.login
, email = model.email
, state = state
, password = model.password
}
{ s
| login = model.login
, email = model.email
, state = state
, password = model.password
}
type Msg
@ -72,79 +89,115 @@ type Msg
| StateMsg (Comp.Dropdown.Msg UserState)
| SetPassword String
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetUser t ->
let
state = Comp.Dropdown.makeSingleList
state =
Comp.Dropdown.makeSingleList
{ makeOption = \s -> { value = Data.UserState.toString s, text = Data.UserState.toString s }
, placeholder = ""
, options = Data.UserState.all
, selected = Data.UserState.fromString t.state
|> Maybe.map (\u -> List.filter ((==) u) Data.UserState.all)
|> Maybe.andThen List.head
|> Util.Maybe.withDefault (List.head Data.UserState.all)
, selected =
Data.UserState.fromString t.state
|> Maybe.map (\u -> List.filter ((==) u) Data.UserState.all)
|> Maybe.andThen List.head
|> Util.Maybe.withDefault (List.head Data.UserState.all)
}
in
({model | user = t
, login = t.login
, email = t.email
, password = t.password
, state = state }, Cmd.none)
( { model
| user = t
, login = t.login
, email = t.email
, password = t.password
, state = state
}
, Cmd.none
)
StateMsg m ->
let
(m1, c1) = Comp.Dropdown.update m model.state
( m1, c1 ) =
Comp.Dropdown.update m model.state
in
({model | state = m1}, Cmd.map StateMsg c1)
( { model | state = m1 }, Cmd.map StateMsg c1 )
SetLogin n ->
({model | login = n}, Cmd.none)
( { model | login = n }, Cmd.none )
SetEmail e ->
({model | email = if e == "" then Nothing else Just e }, Cmd.none)
( { model
| email =
if e == "" then
Nothing
else
Just e
}
, Cmd.none
)
SetPassword p ->
({model | password = if p == "" then Nothing else Just p}, Cmd.none)
( { model
| password =
if p == "" then
Nothing
else
Just p
}
, Cmd.none
)
view: Model -> Html Msg
view : Model -> Html Msg
view model =
div [class "ui form"]
[div [classList [("field", True)
,("error", model.login == "")
,("invisible", model.user.login /= "")
]
]
[label [][text "Login*"]
,input [type_ "text"
,onInput SetLogin
,placeholder "Login"
,value model.login
][]
]
,div [class "field"]
[label [][text "E-Mail"]
,input [ onInput SetEmail
, model.email |> Maybe.withDefault "" |> value
, placeholder "E-Mail"
][]
div [ class "ui form" ]
[ div
[ classList
[ ( "field", True )
, ( "error", model.login == "" )
, ( "invisible", model.user.login /= "" )
]
]
,div [class "field"]
[label [][text "State"]
,Html.map StateMsg (Comp.Dropdown.view model.state)
[ label [] [ text "Login*" ]
, input
[ type_ "text"
, onInput SetLogin
, placeholder "Login"
, value model.login
]
[]
]
,div [classList [("field", True)
,("invisible", model.user.login /= "")
,("error", Util.Maybe.isEmpty model.password)
]
]
[label [][text "Password*"]
,input [type_ "text"
, onInput SetPassword
, placeholder "Password"
, Maybe.withDefault "" model.password |> value
][]
, div [ class "field" ]
[ label [] [ text "E-Mail" ]
, input
[ onInput SetEmail
, model.email |> Maybe.withDefault "" |> value
, placeholder "E-Mail"
]
[]
]
, div [ class "field" ]
[ label [] [ text "State" ]
, Html.map StateMsg (Comp.Dropdown.view model.state)
]
, div
[ classList
[ ( "field", True )
, ( "invisible", model.user.login /= "" )
, ( "error", Util.Maybe.isEmpty model.password )
]
]
[ label [] [ text "Password*" ]
, input
[ type_ "text"
, onInput SetPassword
, placeholder "Password"
, Maybe.withDefault "" model.password |> value
]
[]
]
]

View File

@ -1,36 +1,43 @@
module Comp.UserManage exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.UserManage exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Http
import Api
import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.User
import Api.Model.UserList exposing (UserList)
import Comp.UserForm
import Comp.UserTable
import Comp.YesNoDimmer
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onSubmit)
import Data.Flags exposing (Flags)
import Comp.UserTable
import Comp.UserForm
import Comp.YesNoDimmer
import Api.Model.User
import Api.Model.UserList exposing (UserList)
import Api.Model.BasicResult exposing (BasicResult)
import Util.Maybe
import Http
import Util.Http
import Util.Maybe
type alias Model =
{ tableModel: Comp.UserTable.Model
, formModel: Comp.UserForm.Model
, viewMode: ViewMode
, formError: Maybe String
, loading: Bool
, deleteConfirm: Comp.YesNoDimmer.Model
{ tableModel : Comp.UserTable.Model
, formModel : Comp.UserForm.Model
, viewMode : ViewMode
, formError : Maybe String
, loading : Bool
, deleteConfirm : Comp.YesNoDimmer.Model
}
type ViewMode = Table | Form
emptyModel: Model
type ViewMode
= Table
| Form
emptyModel : Model
emptyModel =
{ tableModel = Comp.UserTable.emptyModel
, formModel = Comp.UserForm.emptyModel
@ -40,6 +47,7 @@ emptyModel =
, deleteConfirm = Comp.YesNoDimmer.emptyModel
}
type Msg
= TableMsg Comp.UserTable.Msg
| FormMsg Comp.UserForm.Msg
@ -52,154 +60,213 @@ type Msg
| YesNoMsg Comp.YesNoDimmer.Msg
| RequestDelete
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
TableMsg m ->
let
(tm, tc) = Comp.UserTable.update flags m model.tableModel
(m2, c2) = ({model | tableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError = if Util.Maybe.nonEmpty tm.selected then Nothing else model.formError
}
, Cmd.map TableMsg tc
)
(m3, c3) = case tm.selected of
( tm, tc ) =
Comp.UserTable.update flags m model.tableModel
( m2, c2 ) =
( { model
| tableModel = tm
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
, formError =
if Util.Maybe.nonEmpty tm.selected then
Nothing
else
model.formError
}
, Cmd.map TableMsg tc
)
( m3, c3 ) =
case tm.selected of
Just user ->
update flags (FormMsg (Comp.UserForm.SetUser user)) m2
Nothing ->
(m2, Cmd.none)
( m2, Cmd.none )
in
(m3, Cmd.batch [c2, c3])
( m3, Cmd.batch [ c2, c3 ] )
FormMsg m ->
let
(m2, c2) = Comp.UserForm.update flags m model.formModel
( m2, c2 ) =
Comp.UserForm.update flags m model.formModel
in
({model | formModel = m2}, Cmd.map FormMsg c2)
( { model | formModel = m2 }, Cmd.map FormMsg c2 )
LoadUsers ->
({model| loading = True}, Api.getUsers flags UserResp)
( { model | loading = True }, Api.getUsers flags UserResp )
UserResp (Ok users) ->
let
m2 = {model|viewMode = Table, loading = False}
m2 =
{ model | viewMode = Table, loading = False }
in
update flags (TableMsg (Comp.UserTable.SetUsers users.items)) m2
update flags (TableMsg (Comp.UserTable.SetUsers users.items)) m2
UserResp (Err err) ->
({model|loading = False}, Cmd.none)
UserResp (Err _) ->
( { model | loading = False }, Cmd.none )
SetViewMode m ->
let
m2 = {model | viewMode = m }
m2 =
{ model | viewMode = m }
in
case m of
Table ->
update flags (TableMsg Comp.UserTable.Deselect) m2
Form ->
(m2, Cmd.none)
case m of
Table ->
update flags (TableMsg Comp.UserTable.Deselect) m2
Form ->
( m2, Cmd.none )
InitNewUser ->
let
nm = {model | viewMode = Form, formError = Nothing }
user = Api.Model.User.empty
nm =
{ model | viewMode = Form, formError = Nothing }
user =
Api.Model.User.empty
in
update flags (FormMsg (Comp.UserForm.SetUser user)) nm
update flags (FormMsg (Comp.UserForm.SetUser user)) nm
Submit ->
let
user = Comp.UserForm.getUser model.formModel
valid = Comp.UserForm.isValid model.formModel
cmd = if Comp.UserForm.isNewUser model.formModel
then Api.postNewUser flags user SubmitResp
else Api.putUser flags user SubmitResp
in if valid then
({model|loading = True}, cmd)
else
({model|formError = Just "Please correct the errors in the form."}, Cmd.none)
user =
Comp.UserForm.getUser model.formModel
valid =
Comp.UserForm.isValid model.formModel
cmd =
if Comp.UserForm.isNewUser model.formModel then
Api.postNewUser flags user SubmitResp
else
Api.putUser flags user SubmitResp
in
if valid then
( { model | loading = True }, cmd )
else
( { model | formError = Just "Please correct the errors in the form." }, Cmd.none )
SubmitResp (Ok res) ->
if res.success then
let
(m2, c2) = update flags (SetViewMode Table) model
(m3, c3) = update flags LoadUsers m2
( m2, c2 ) =
update flags (SetViewMode Table) model
( m3, c3 ) =
update flags LoadUsers m2
in
({m3|loading = False}, Cmd.batch [c2,c3])
( { m3 | loading = False }, Cmd.batch [ c2, c3 ] )
else
({model | formError = Just res.message, loading = False }, Cmd.none)
( { model | formError = Just res.message, loading = False }, Cmd.none )
SubmitResp (Err err) ->
({model | formError = Just (Util.Http.errorToString err), loading = False}, Cmd.none)
( { model | formError = Just (Util.Http.errorToString err), loading = False }, Cmd.none )
RequestDelete ->
update flags (YesNoMsg Comp.YesNoDimmer.activate) model
YesNoMsg m ->
let
(cm, confirmed) = Comp.YesNoDimmer.update m model.deleteConfirm
user = Comp.UserForm.getUser model.formModel
cmd = if confirmed then Api.deleteUser flags user.login SubmitResp else Cmd.none
( cm, confirmed ) =
Comp.YesNoDimmer.update m model.deleteConfirm
user =
Comp.UserForm.getUser model.formModel
cmd =
if confirmed then
Api.deleteUser flags user.login SubmitResp
else
Cmd.none
in
({model | deleteConfirm = cm}, cmd)
( { model | deleteConfirm = cm }, cmd )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
if model.viewMode == Table then viewTable model
else viewForm model
if model.viewMode == Table then
viewTable model
viewTable: Model -> Html Msg
else
viewForm model
viewTable : Model -> Html Msg
viewTable model =
div []
[button [class "ui basic button", onClick InitNewUser]
[i [class "plus icon"][]
,text "Create new"
]
,Html.map TableMsg (Comp.UserTable.view model.tableModel)
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
[ button [ class "ui basic button", onClick InitNewUser ]
[ i [ class "plus icon" ] []
, text "Create new"
]
, Html.map TableMsg (Comp.UserTable.view model.tableModel)
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]
viewForm: Model -> Html Msg
viewForm : Model -> Html Msg
viewForm model =
let
newUser = Comp.UserForm.isNewUser model.formModel
newUser =
Comp.UserForm.isNewUser model.formModel
in
Html.form [class "ui segment", onSubmit Submit]
[Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
,if newUser then
h3 [class "ui dividing header"]
[text "Create new user"
]
else
h3 [class "ui dividing header"]
[text ("Edit user: " ++ model.formModel.user.login)
]
,Html.map FormMsg (Comp.UserForm.view model.formModel)
,div [classList [("ui error message", True)
,("invisible", Util.Maybe.isEmpty model.formError)
]
]
[Maybe.withDefault "" model.formError |> text
]
,div [class "ui horizontal divider"][]
,button [class "ui primary button", type_ "submit"]
[text "Submit"
Html.form [ class "ui segment", onSubmit Submit ]
[ Html.map YesNoMsg (Comp.YesNoDimmer.view model.deleteConfirm)
, if newUser then
h3 [ class "ui dividing header" ]
[ text "Create new user"
]
,a [class "ui secondary button", onClick (SetViewMode Table), href ""]
[text "Cancel"
else
h3 [ class "ui dividing header" ]
[ text ("Edit user: " ++ model.formModel.user.login)
]
, Html.map FormMsg (Comp.UserForm.view model.formModel)
, div
[ classList
[ ( "ui error message", True )
, ( "invisible", Util.Maybe.isEmpty model.formError )
]
,if not newUser then
a [class "ui right floated red button", href "", onClick RequestDelete]
[text "Delete"]
else
span[][]
,div [classList [("ui dimmer", True)
,("active", model.loading)
]]
[div [class "ui loader"][]
]
]
[ Maybe.withDefault "" model.formError |> text
]
, div [ class "ui horizontal divider" ] []
, button [ class "ui primary button", type_ "submit" ]
[ text "Submit"
]
, a [ class "ui secondary button", onClick (SetViewMode Table), href "" ]
[ text "Cancel"
]
, if not newUser then
a [ class "ui right floated red button", href "", onClick RequestDelete ]
[ text "Delete" ]
else
span [] []
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]

View File

@ -1,83 +1,91 @@
module Comp.UserTable exposing ( Model
, emptyModel
, Msg(..)
, view
, update)
module Comp.UserTable exposing
( Model
, Msg(..)
, emptyModel
, update
, view
)
import Api.Model.User exposing (User)
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Data.Flags exposing (Flags)
import Api.Model.User exposing (User)
import Util.Time exposing (formatDateTime)
type alias Model =
{ users: List User
, selected: Maybe User
{ users : List User
, selected : Maybe User
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ users = []
, selected = Nothing
}
type Msg
= SetUsers (List User)
| Select User
| Deselect
update: Flags -> Msg -> Model -> (Model, Cmd Msg)
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
SetUsers list ->
({model | users = list, selected = Nothing }, Cmd.none)
( { model | users = list, selected = Nothing }, Cmd.none )
Select user ->
({model | selected = Just user}, Cmd.none)
( { model | selected = Just user }, Cmd.none )
Deselect ->
({model | selected = Nothing}, Cmd.none)
( { model | selected = Nothing }, Cmd.none )
view: Model -> Html Msg
view : Model -> Html Msg
view model =
table [class "ui selectable table"]
[thead []
[tr []
[th [class "collapsing"][text "Login"]
,th [class "collapsing"][text "State"]
,th [class "collapsing"][text "Email"]
,th [class "collapsing"][text "Logins"]
,th [class "collapsing"][text "Last Login"]
,th [class "collapsing"][text "Created"]
]
]
,tbody []
table [ class "ui selectable table" ]
[ thead []
[ tr []
[ th [ class "collapsing" ] [ text "Login" ]
, th [ class "collapsing" ] [ text "State" ]
, th [ class "collapsing" ] [ text "Email" ]
, th [ class "collapsing" ] [ text "Logins" ]
, th [ class "collapsing" ] [ text "Last Login" ]
, th [ class "collapsing" ] [ text "Created" ]
]
]
, tbody []
(List.map (renderUserLine model) model.users)
]
renderUserLine: Model -> User -> Html Msg
renderUserLine : Model -> User -> Html Msg
renderUserLine model user =
tr [classList [("active", model.selected == Just user)]
,onClick (Select user)
]
[td [class "collapsing"]
[text user.login
]
,td [class "collapsing"]
[text user.state
tr
[ classList [ ( "active", model.selected == Just user ) ]
, onClick (Select user)
]
[ td [ class "collapsing" ]
[ text user.login
]
,td [class "collapsing"]
[Maybe.withDefault "" user.email |> text
, td [ class "collapsing" ]
[ text user.state
]
,td [class "collapsing"]
[String.fromInt user.loginCount |> text
, td [ class "collapsing" ]
[ Maybe.withDefault "" user.email |> text
]
,td [class "collapsing"]
[Maybe.map formatDateTime user.lastLogin |> Maybe.withDefault "" |> text
, td [ class "collapsing" ]
[ String.fromInt user.loginCount |> text
]
,td [class "collapsing"]
[formatDateTime user.created |> text
, td [ class "collapsing" ]
[ Maybe.map formatDateTime user.lastLogin |> Maybe.withDefault "" |> text
]
, td [ class "collapsing" ]
[ formatDateTime user.created |> text
]
]

View File

@ -1,43 +1,49 @@
module Comp.YesNoDimmer exposing ( Model
, Msg(..)
, emptyModel
, update
, view
, view2
, activate
, disable
, Settings
, defaultSettings
)
module Comp.YesNoDimmer exposing
( Model
, Msg(..)
, Settings
, activate
, defaultSettings
, disable
, emptyModel
, update
, view
, view2
)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
type alias Model =
{ active: Bool
{ active : Bool
}
emptyModel: Model
emptyModel : Model
emptyModel =
{ active = False
}
type Msg
= Activate
| Disable
| ConfirmDelete
type alias Settings =
{ message: String
, headerIcon: String
, headerClass: String
, confirmButton: String
, cancelButton: String
, invertedDimmer: Bool
{ message : String
, headerIcon : String
, headerClass : String
, confirmButton : String
, cancelButton : String
, invertedDimmer : Bool
}
defaultSettings: Settings
defaultSettings : Settings
defaultSettings =
{ message = "Delete this item permanently?"
, headerIcon = "exclamation icon"
@ -48,48 +54,62 @@ defaultSettings =
}
activate: Msg
activate = Activate
activate : Msg
activate =
Activate
disable: Msg
disable = Disable
update: Msg -> Model -> (Model, Bool)
disable : Msg
disable =
Disable
update : Msg -> Model -> ( Model, Bool )
update msg model =
case msg of
Activate ->
({model | active = True}, False)
Disable ->
({model | active = False}, False)
ConfirmDelete ->
({model | active = False}, True)
( { model | active = True }, False )
view: Model -> Html Msg
Disable ->
( { model | active = False }, False )
ConfirmDelete ->
( { model | active = False }, True )
view : Model -> Html Msg
view model =
view2 True defaultSettings model
view2: Bool -> Settings -> Model -> Html Msg
view2 : Bool -> Settings -> Model -> Html Msg
view2 active settings model =
div [classList [("ui dimmer", True)
,("inverted", settings.invertedDimmer)
,("active", (active && model.active))
]
]
[div [class "content"]
[h3 [class settings.headerClass]
[if settings.headerIcon == "" then span[][] else i [class settings.headerIcon][]
,text settings.message
]
]
,div [class "content"]
[div [class "ui buttons"]
[a [class "ui primary button", onClick ConfirmDelete, href ""]
[text settings.confirmButton
]
,div [class "or"][]
,a [class "ui secondary button", onClick Disable, href ""]
[text settings.cancelButton
]
]
]
]
div
[ classList
[ ( "ui dimmer", True )
, ( "inverted", settings.invertedDimmer )
, ( "active", active && model.active )
]
]
[ div [ class "content" ]
[ h3 [ class settings.headerClass ]
[ if settings.headerIcon == "" then
span [] []
else
i [ class settings.headerIcon ] []
, text settings.message
]
]
, div [ class "content" ]
[ div [ class "ui buttons" ]
[ a [ class "ui primary button", onClick ConfirmDelete, href "" ]
[ text settings.confirmButton
]
, div [ class "or" ] []
, a [ class "ui secondary button", onClick Disable, href "" ]
[ text settings.cancelButton
]
]
]
]