docspell/modules/webapp/src/main/elm/Comp/EquipmentManage.elm
Eike Kettner dd935454c9 First version of new ui based on tailwind
This drops fomantic-ui as css toolkit and introduces tailwindcss. With
tailwind there are no predefined components, but it's very easy to
create those. So customizing the look&feel is much simpler, most of
the time no additional css is needed.

This requires a complete rewrite of the markup + styles. Luckily all
logic can be kept as is. The now old ui is not removed, it is still
available by using a request header `Docspell-Ui` with a value of `1`
for the old ui and `2` for the new ui.

Another addition is "dev mode", where docspell serves assets with a
no-cache header, to disable browser caching. This makes developing a
lot easier.
2021-02-14 01:46:13 +01:00

428 lines
12 KiB
Elm

module Comp.EquipmentManage exposing
( Model
, Msg(..)
, emptyModel
, update
, view
, view2
)
import Api
import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.Equipment
import Api.Model.EquipmentList exposing (EquipmentList)
import Comp.Basic as B
import Comp.EquipmentForm
import Comp.EquipmentTable
import Comp.MenuBar as MB
import Comp.YesNoDimmer
import Data.Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput, onSubmit)
import Http
import Styles as S
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
, query : String
}
type ViewMode
= Table
| Form
emptyModel : Model
emptyModel =
{ tableModel = Comp.EquipmentTable.emptyModel
, formModel = Comp.EquipmentForm.emptyModel
, viewMode = Table
, formError = Nothing
, loading = False
, deleteConfirm = Comp.YesNoDimmer.emptyModel
, query = ""
}
type Msg
= TableMsg Comp.EquipmentTable.Msg
| FormMsg Comp.EquipmentForm.Msg
| LoadEquipments
| EquipmentResp (Result Http.Error EquipmentList)
| SetViewMode ViewMode
| InitNewEquipment
| Submit
| SubmitResp (Result Http.Error BasicResult)
| YesNoMsg Comp.YesNoDimmer.Msg
| RequestDelete
| SetQuery String
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
Just equipment ->
update flags (FormMsg (Comp.EquipmentForm.SetEquipment equipment)) m2
Nothing ->
( m2, Cmd.none )
in
( m3, Cmd.batch [ c2, c3 ] )
FormMsg m ->
let
( m2, c2 ) =
Comp.EquipmentForm.update flags m model.formModel
in
( { model | formModel = m2 }, Cmd.map FormMsg c2 )
LoadEquipments ->
( { model | loading = True }, Api.getEquipments flags "" EquipmentResp )
EquipmentResp (Ok equipments) ->
let
m2 =
{ model | viewMode = Table, loading = False }
in
update flags (TableMsg (Comp.EquipmentTable.SetEquipments equipments.items)) m2
EquipmentResp (Err _) ->
( { model | loading = False }, Cmd.none )
SetViewMode m ->
let
m2 =
{ model | viewMode = m }
in
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
in
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 )
SubmitResp (Ok res) ->
if res.success then
let
( m2, c2 ) =
update flags (SetViewMode Table) model
( m3, c3 ) =
update flags LoadEquipments m2
in
( { m3 | loading = False }, Cmd.batch [ c2, c3 ] )
else
( { model | formError = Just res.message, loading = False }, Cmd.none )
SubmitResp (Err err) ->
( { 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
in
( { model | deleteConfirm = cm }, cmd )
SetQuery str ->
let
m =
{ model | query = str }
in
( m, Api.getEquipments flags str EquipmentResp )
view : Model -> Html Msg
view model =
if model.viewMode == Table then
viewTable model
else
viewForm model
viewTable : Model -> Html Msg
viewTable model =
div []
[ div [ class "ui secondary menu" ]
[ div [ class "horizontally fitted item" ]
[ div [ class "ui icon input" ]
[ input
[ type_ "text"
, onInput SetQuery
, value model.query
, placeholder "Search"
]
[]
, i [ class "ui search icon" ]
[]
]
]
, div [ class "right menu" ]
[ div [ class "item" ]
[ a
[ class "ui primary button"
, href "#"
, onClick InitNewEquipment
]
[ i [ class "plus icon" ] []
, text "New Equipment"
]
]
]
]
, 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 =
let
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"
]
, 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" ] []
]
]
--- View2
view2 : Model -> Html Msg
view2 model =
if model.viewMode == Table then
viewTable2 model
else
viewForm2 model
viewTable2 : Model -> Html Msg
viewTable2 model =
div [ class "flex flex-col" ]
[ MB.view
{ start =
[ MB.TextInput
{ tagger = SetQuery
, value = model.query
, placeholder = "Search"
, icon = Just "fa fa-search"
}
]
, end =
[ MB.PrimaryButton
{ tagger = InitNewEquipment
, title = "Create a new equipment"
, icon = Just "fa fa-plus"
, label = "New Equipment"
}
]
, rootClasses = "mb-4"
}
, Html.map TableMsg (Comp.EquipmentTable.view2 model.tableModel)
, div
[ classList
[ ( "ui dimmer", True )
, ( "active", model.loading )
]
]
[ div [ class "ui loader" ] []
]
]
viewForm2 : Model -> Html Msg
viewForm2 model =
let
newEquipment =
model.formModel.equipment.id == ""
dimmerSettings2 =
Comp.YesNoDimmer.defaultSettings2 "Really delete this equipment?"
in
Html.form
[ class "relative flex flex-col"
, onSubmit Submit
]
[ Html.map YesNoMsg
(Comp.YesNoDimmer.viewN
True
dimmerSettings2
model.deleteConfirm
)
, if newEquipment then
h1 [ class S.header2 ]
[ text "Create new equipment"
]
else
h1 [ class S.header2 ]
[ text model.formModel.equipment.name
, div [ class "opacity-50 text-sm" ]
[ text "Id: "
, text model.formModel.equipment.id
]
]
, MB.view
{ start =
[ MB.PrimaryButton
{ tagger = Submit
, title = "Submit this form"
, icon = Just "fa fa-save"
, label = "Submit"
}
, MB.SecondaryButton
{ tagger = SetViewMode Table
, title = "Back to list"
, icon = Just "fa fa-arrow-left"
, label = "Cancel"
}
]
, end =
if not newEquipment then
[ MB.DeleteButton
{ tagger = RequestDelete
, title = "Delete this equipment"
, icon = Just "fa fa-trash"
, label = "Delete"
}
]
else
[]
, rootClasses = "mb-4"
}
, Html.map FormMsg (Comp.EquipmentForm.view2 model.formModel)
, div
[ classList
[ ( "hidden", Util.Maybe.isEmpty model.formError )
]
, class S.errorMessage
]
[ Maybe.withDefault "" model.formError |> text
]
, B.loadingDimmer model.loading
]