mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-23 19:08:26 +00:00
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.
This commit is contained in:
265
modules/webapp/src/main/elm/Page/CollectiveSettings/View2.elm
Normal file
265
modules/webapp/src/main/elm/Page/CollectiveSettings/View2.elm
Normal file
@ -0,0 +1,265 @@
|
||||
module Page.CollectiveSettings.View2 exposing (viewContent, viewSidebar)
|
||||
|
||||
import Api.Model.TagCount exposing (TagCount)
|
||||
import Comp.Basic as B
|
||||
import Comp.CollectiveSettingsForm
|
||||
import Comp.SourceManage
|
||||
import Comp.UserManage
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.Icons as Icons
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
import Page.CollectiveSettings.Data exposing (..)
|
||||
import Styles as S
|
||||
import Util.Maybe
|
||||
import Util.Size
|
||||
|
||||
|
||||
viewSidebar : Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewSidebar visible _ _ model =
|
||||
div
|
||||
[ id "sidebar"
|
||||
, class S.sidebar
|
||||
, class S.sidebarBg
|
||||
, classList [ ( "hidden", not visible ) ]
|
||||
]
|
||||
[ div [ class "" ]
|
||||
[ h1 [ class S.header1 ]
|
||||
[ text "Collective Settings"
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-2" ]
|
||||
[ a
|
||||
[ href "#"
|
||||
, onClick (SetTab InsightsTab)
|
||||
, menuEntryActive model InsightsTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ i [ class "fa fa-chart-bar" ] []
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Insights" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab SourceTab)
|
||||
, class S.sidebarLink
|
||||
, menuEntryActive model SourceTab
|
||||
]
|
||||
[ Icons.sourceIcon2 ""
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Sources" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab SettingsTab)
|
||||
, menuEntryActive model SettingsTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ i [ class "fa fa-cog" ] []
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Settings" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab UserTab)
|
||||
, menuEntryActive model UserTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ i [ class "fa fa-user" ] []
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Users" ]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
viewContent : Flags -> UiSettings -> Model -> Html Msg
|
||||
viewContent flags settings model =
|
||||
div
|
||||
[ id "content"
|
||||
, class S.content
|
||||
]
|
||||
(case model.currentTab of
|
||||
Just UserTab ->
|
||||
viewUsers settings model
|
||||
|
||||
Just SettingsTab ->
|
||||
viewSettings flags settings model
|
||||
|
||||
Just InsightsTab ->
|
||||
viewInsights flags model
|
||||
|
||||
Just SourceTab ->
|
||||
viewSources flags settings model
|
||||
|
||||
Nothing ->
|
||||
[]
|
||||
)
|
||||
|
||||
|
||||
|
||||
--- Helper
|
||||
|
||||
|
||||
menuEntryActive : Model -> Tab -> Attribute msg
|
||||
menuEntryActive model tab =
|
||||
if model.currentTab == Just tab then
|
||||
class S.sidebarMenuItemActive
|
||||
|
||||
else
|
||||
class ""
|
||||
|
||||
|
||||
viewInsights : Flags -> Model -> List (Html Msg)
|
||||
viewInsights flags model =
|
||||
let
|
||||
( coll, user ) =
|
||||
Maybe.map (\a -> ( a.collective, a.user )) flags.account
|
||||
|> Maybe.withDefault ( "", "" )
|
||||
in
|
||||
[ h1 [ class S.header1 ]
|
||||
[ i [ class "fa fa-chart-bar font-thin" ] []
|
||||
, span [ class "ml-2" ]
|
||||
[ text "Insights"
|
||||
]
|
||||
]
|
||||
, div [ class "mb-4" ]
|
||||
[ hr [ class S.border ] []
|
||||
]
|
||||
, h2 [ class S.header3 ]
|
||||
[ div [ class "flex flex-row space-x-6" ]
|
||||
[ div
|
||||
[ class ""
|
||||
, title "Collective"
|
||||
]
|
||||
[ i [ class "fa fa-users" ] []
|
||||
, span [ class "ml-2" ]
|
||||
[ text coll
|
||||
]
|
||||
]
|
||||
, div
|
||||
[ class ""
|
||||
, title "User"
|
||||
]
|
||||
[ i [ class "fa fa-user font-thin" ] []
|
||||
, span [ class "ml-2" ]
|
||||
[ text user
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
, div
|
||||
[ class "py-2"
|
||||
]
|
||||
[ h4 [ class S.header3 ]
|
||||
[ text "Items"
|
||||
]
|
||||
, div [ class "flex px-4 flex-wrap" ]
|
||||
[ stats (String.fromInt (model.insights.incomingCount + model.insights.outgoingCount)) "Items"
|
||||
, stats (String.fromInt model.insights.incomingCount) "Incoming"
|
||||
, stats (String.fromInt model.insights.outgoingCount) "Outgoing"
|
||||
]
|
||||
]
|
||||
, div
|
||||
[ class "py-2"
|
||||
]
|
||||
[ h4 [ class S.header3 ]
|
||||
[ text "Size"
|
||||
]
|
||||
, div [ class "flex px-4 flex-wrap" ]
|
||||
[ stats (toFloat model.insights.itemSize |> Util.Size.bytesReadable Util.Size.B) "Size"
|
||||
]
|
||||
]
|
||||
, div
|
||||
[ class "py-2"
|
||||
]
|
||||
[ h4 [ class S.header3 ]
|
||||
[ text "Tags"
|
||||
]
|
||||
, div [ class "flex px-4 flex-wrap" ]
|
||||
(List.map makeTagStats
|
||||
(List.sortBy .count model.insights.tagCloud.items
|
||||
|> List.reverse
|
||||
)
|
||||
)
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
stats : String -> String -> Html msg
|
||||
stats value label =
|
||||
B.stats
|
||||
{ rootClass = "mb-4"
|
||||
, valueClass = "text-6xl"
|
||||
, value = value
|
||||
, label = label
|
||||
}
|
||||
|
||||
|
||||
makeTagStats : TagCount -> Html Msg
|
||||
makeTagStats nc =
|
||||
stats (String.fromInt nc.count) nc.tag.name
|
||||
|
||||
|
||||
viewSources : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
viewSources flags settings model =
|
||||
[ h1
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ Icons.sourceIcon2 ""
|
||||
, div [ class "ml-3" ]
|
||||
[ text "Sources"
|
||||
]
|
||||
]
|
||||
, Html.map SourceMsg (Comp.SourceManage.view2 flags settings model.sourceModel)
|
||||
]
|
||||
|
||||
|
||||
viewUsers : UiSettings -> Model -> List (Html Msg)
|
||||
viewUsers settings model =
|
||||
[ h1
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ i [ class "fa fa-user" ] []
|
||||
, div [ class "ml-3" ]
|
||||
[ text "Users"
|
||||
]
|
||||
]
|
||||
, Html.map UserMsg (Comp.UserManage.view2 settings model.userModel)
|
||||
]
|
||||
|
||||
|
||||
viewSettings : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
viewSettings flags settings model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ i [ class "fa fa-cog" ] []
|
||||
, span [ class "ml-3" ]
|
||||
[ text "Collective Settings"
|
||||
]
|
||||
]
|
||||
, Html.map SettingsFormMsg
|
||||
(Comp.CollectiveSettingsForm.view2 flags settings model.settingsModel)
|
||||
, div
|
||||
[ classList
|
||||
[ ( "hidden", Util.Maybe.isEmpty model.submitResult )
|
||||
, ( S.successMessage, Maybe.map .success model.submitResult |> Maybe.withDefault False )
|
||||
, ( S.errorMessage, Maybe.map .success model.submitResult |> Maybe.map not |> Maybe.withDefault False )
|
||||
]
|
||||
, class "mt-2"
|
||||
]
|
||||
[ Maybe.map .message model.submitResult
|
||||
|> Maybe.withDefault ""
|
||||
|> text
|
||||
]
|
||||
]
|
@ -7,6 +7,7 @@ module Page.Home.Data exposing
|
||||
, SelectViewModel
|
||||
, ViewMode(..)
|
||||
, doSearchCmd
|
||||
, editActive
|
||||
, init
|
||||
, initSelectViewModel
|
||||
, itemNav
|
||||
@ -23,8 +24,8 @@ import Api.Model.SearchStats exposing (SearchStats)
|
||||
import Browser.Dom as Dom
|
||||
import Comp.FixedDropdown
|
||||
import Comp.ItemCardList
|
||||
import Comp.ItemDetail.EditMenu exposing (SaveNameState(..))
|
||||
import Comp.ItemDetail.FormChange exposing (FormChange)
|
||||
import Comp.ItemDetail.MultiEditMenu exposing (SaveNameState(..))
|
||||
import Comp.LinkTarget exposing (LinkTarget)
|
||||
import Comp.SearchMenu
|
||||
import Comp.YesNoDimmer
|
||||
@ -61,7 +62,7 @@ type alias SelectViewModel =
|
||||
{ ids : Set String
|
||||
, action : SelectActionMode
|
||||
, deleteAllConfirm : Comp.YesNoDimmer.Model
|
||||
, editModel : Comp.ItemDetail.EditMenu.Model
|
||||
, editModel : Comp.ItemDetail.MultiEditMenu.Model
|
||||
, saveNameState : SaveNameState
|
||||
, saveCustomFieldState : Set String
|
||||
}
|
||||
@ -72,7 +73,7 @@ initSelectViewModel =
|
||||
{ ids = Set.empty
|
||||
, action = NoneAction
|
||||
, deleteAllConfirm = Comp.YesNoDimmer.initActive
|
||||
, editModel = Comp.ItemDetail.EditMenu.init
|
||||
, editModel = Comp.ItemDetail.MultiEditMenu.init
|
||||
, saveNameState = SaveSuccess
|
||||
, saveCustomFieldState = Set.empty
|
||||
}
|
||||
@ -148,6 +149,19 @@ selectActive model =
|
||||
True
|
||||
|
||||
|
||||
editActive : Model -> Bool
|
||||
editActive model =
|
||||
case model.viewMode of
|
||||
SimpleView ->
|
||||
False
|
||||
|
||||
SearchView ->
|
||||
False
|
||||
|
||||
SelectView svm ->
|
||||
svm.action == EditSelected
|
||||
|
||||
|
||||
type Msg
|
||||
= Init
|
||||
| SearchMenuMsg Comp.SearchMenu.Msg
|
||||
@ -162,6 +176,7 @@ type Msg
|
||||
| UpdateThrottle
|
||||
| SetBasicSearch String
|
||||
| SearchTypeMsg (Comp.FixedDropdown.Msg SearchType)
|
||||
| ToggleSearchType
|
||||
| KeyUpSearchbarMsg (Maybe KeyCode)
|
||||
| ScrollResult (Result Dom.Error ())
|
||||
| ClearItemDetailId
|
||||
@ -170,13 +185,14 @@ type Msg
|
||||
| RequestDeleteSelected
|
||||
| DeleteSelectedConfirmMsg Comp.YesNoDimmer.Msg
|
||||
| EditSelectedItems
|
||||
| EditMenuMsg Comp.ItemDetail.EditMenu.Msg
|
||||
| EditMenuMsg Comp.ItemDetail.MultiEditMenu.Msg
|
||||
| MultiUpdateResp FormChange (Result Http.Error BasicResult)
|
||||
| ReplaceChangedItemsResp (Result Http.Error ItemLightList)
|
||||
| DeleteAllResp (Result Http.Error BasicResult)
|
||||
| UiSettingsUpdated
|
||||
| SetLinkTarget LinkTarget
|
||||
| SearchStatsResp (Result Http.Error SearchStats)
|
||||
| TogglePreviewFullWidth
|
||||
|
||||
|
||||
type SearchType
|
||||
@ -206,7 +222,7 @@ searchTypeString st =
|
||||
"Names"
|
||||
|
||||
ContentOnlySearch ->
|
||||
"Contents Only"
|
||||
"Contents"
|
||||
|
||||
|
||||
itemNav : String -> Model -> ItemNav
|
||||
|
131
modules/webapp/src/main/elm/Page/Home/SideMenu.elm
Normal file
131
modules/webapp/src/main/elm/Page/Home/SideMenu.elm
Normal file
@ -0,0 +1,131 @@
|
||||
module Page.Home.SideMenu exposing (view)
|
||||
|
||||
import Comp.Basic as B
|
||||
import Comp.ItemDetail.MultiEditMenu
|
||||
import Comp.MenuBar as MB
|
||||
import Comp.SearchMenu
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
import Page.Home.Data exposing (..)
|
||||
import Set
|
||||
import Styles as S
|
||||
|
||||
|
||||
view : Flags -> UiSettings -> Model -> Html Msg
|
||||
view flags settings model =
|
||||
div
|
||||
[ class "flex flex-col"
|
||||
]
|
||||
[ MB.viewSide
|
||||
{ end =
|
||||
[ MB.CustomButton
|
||||
{ tagger = ToggleSelectView
|
||||
, label = ""
|
||||
, icon = Just "fa fa-tasks"
|
||||
, title = "Edit Mode"
|
||||
, inputClass =
|
||||
[ ( S.secondaryBasicButton, True )
|
||||
, ( "bg-gray-200 dark:bg-bluegray-600", selectActive model )
|
||||
]
|
||||
}
|
||||
, MB.CustomButton
|
||||
{ tagger = ResetSearch
|
||||
, label = ""
|
||||
, icon = Just "fa fa-sync"
|
||||
, title = "Reset search form"
|
||||
, inputClass = [ ( S.secondaryBasicButton, True ) ]
|
||||
}
|
||||
]
|
||||
, start = []
|
||||
, rootClasses = "text-sm w-full bg-blue-50 pt-2 hidden"
|
||||
}
|
||||
, div [ class "flex flex-col" ]
|
||||
(case model.viewMode of
|
||||
SelectView svm ->
|
||||
case svm.action of
|
||||
EditSelected ->
|
||||
viewEditMenu svm settings
|
||||
|
||||
_ ->
|
||||
viewSearch flags settings model
|
||||
|
||||
_ ->
|
||||
viewSearch flags settings model
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
viewSearch : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
viewSearch flags settings model =
|
||||
[ MB.viewSide
|
||||
{ start =
|
||||
[ MB.CustomElement <|
|
||||
B.secondaryBasicButton
|
||||
{ label = ""
|
||||
, icon = "fa fa-expand-alt"
|
||||
, handler = onClick (SearchMenuMsg Comp.SearchMenu.ToggleOpenAllAkkordionTabs)
|
||||
, attrs = [ href "#" ]
|
||||
, disabled = False
|
||||
}
|
||||
]
|
||||
, end = []
|
||||
, rootClasses = "my-1 text-xs hidden sm:flex"
|
||||
}
|
||||
, Html.map SearchMenuMsg
|
||||
(Comp.SearchMenu.viewDrop2 model.dragDropData
|
||||
flags
|
||||
settings
|
||||
model.searchMenuModel
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
viewEditMenu : SelectViewModel -> UiSettings -> List (Html Msg)
|
||||
viewEditMenu svm settings =
|
||||
let
|
||||
cfg_ =
|
||||
Comp.ItemDetail.MultiEditMenu.defaultViewConfig
|
||||
|
||||
cfg =
|
||||
{ cfg_
|
||||
| nameState = svm.saveNameState
|
||||
, customFieldState =
|
||||
\fId ->
|
||||
if Set.member fId svm.saveCustomFieldState then
|
||||
Comp.ItemDetail.MultiEditMenu.Saving
|
||||
|
||||
else
|
||||
Comp.ItemDetail.MultiEditMenu.SaveSuccess
|
||||
}
|
||||
in
|
||||
[ div [ class S.header2 ]
|
||||
[ i [ class "fa fa-edit" ] []
|
||||
, span [ class "ml-2" ]
|
||||
[ text "Multi-Edit"
|
||||
]
|
||||
]
|
||||
, div [ class S.infoMessage ]
|
||||
[ text "Note that a change here immediatly affects all selected items on the right!"
|
||||
]
|
||||
, MB.viewSide
|
||||
{ start =
|
||||
[ MB.CustomElement <|
|
||||
B.secondaryButton
|
||||
{ label = "Close"
|
||||
, disabled = False
|
||||
, icon = "fa fa-times"
|
||||
, handler = onClick ToggleSelectView
|
||||
, attrs =
|
||||
[ href "#"
|
||||
]
|
||||
}
|
||||
]
|
||||
, end = []
|
||||
, rootClasses = "mt-2 text-sm"
|
||||
}
|
||||
, Html.map EditMenuMsg
|
||||
(Comp.ItemDetail.MultiEditMenu.view2 cfg settings svm.editModel)
|
||||
]
|
@ -7,8 +7,8 @@ import Api.Model.ItemSearch
|
||||
import Browser.Navigation as Nav
|
||||
import Comp.FixedDropdown
|
||||
import Comp.ItemCardList
|
||||
import Comp.ItemDetail.EditMenu exposing (SaveNameState(..))
|
||||
import Comp.ItemDetail.FormChange exposing (FormChange(..))
|
||||
import Comp.ItemDetail.MultiEditMenu exposing (SaveNameState(..))
|
||||
import Comp.LinkTarget exposing (LinkTarget)
|
||||
import Comp.SearchMenu
|
||||
import Comp.YesNoDimmer
|
||||
@ -18,6 +18,7 @@ import Data.Items
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Page exposing (Page(..))
|
||||
import Page.Home.Data exposing (..)
|
||||
import Ports
|
||||
import Process
|
||||
import Scroll
|
||||
import Set exposing (Set)
|
||||
@ -268,6 +269,14 @@ update mId key flags settings msg model =
|
||||
in
|
||||
update mId key flags settings smMsg model
|
||||
|
||||
ToggleSearchType ->
|
||||
case model.searchTypeDropdownValue of
|
||||
BasicSearch ->
|
||||
update mId key flags settings (SearchMenuMsg Comp.SearchMenu.SetFulltextSearch) model
|
||||
|
||||
ContentOnlySearch ->
|
||||
update mId key flags settings (SearchMenuMsg Comp.SearchMenu.SetNamesSearch) model
|
||||
|
||||
SearchTypeMsg lm ->
|
||||
let
|
||||
( sm, mv ) =
|
||||
@ -452,7 +461,7 @@ update mId key flags settings msg model =
|
||||
SelectView svm ->
|
||||
let
|
||||
res =
|
||||
Comp.ItemDetail.EditMenu.update flags lmsg svm.editModel
|
||||
Comp.ItemDetail.MultiEditMenu.update flags lmsg svm.editModel
|
||||
|
||||
svm_ =
|
||||
{ svm
|
||||
@ -560,6 +569,16 @@ update mId key flags settings msg model =
|
||||
in
|
||||
update mId key flags settings lm { model | searchStats = stats }
|
||||
|
||||
TogglePreviewFullWidth ->
|
||||
let
|
||||
newSettings =
|
||||
{ settings | cardPreviewFullWidth = not settings.cardPreviewFullWidth }
|
||||
|
||||
cmd =
|
||||
Ports.storeUiSettings flags newSettings
|
||||
in
|
||||
noSub ( model, cmd )
|
||||
|
||||
|
||||
|
||||
--- Helpers
|
||||
@ -663,7 +682,7 @@ scrollToCard mId model =
|
||||
|
||||
loadEditModel : Flags -> Cmd Msg
|
||||
loadEditModel flags =
|
||||
Cmd.map EditMenuMsg (Comp.ItemDetail.EditMenu.loadModel flags)
|
||||
Cmd.map EditMenuMsg (Comp.ItemDetail.MultiEditMenu.loadModel flags)
|
||||
|
||||
|
||||
doSearch : SearchParam -> Model -> ( Model, Cmd Msg, Sub Msg )
|
||||
|
@ -3,7 +3,7 @@ module Page.Home.View exposing (view)
|
||||
import Api.Model.ItemSearch
|
||||
import Comp.FixedDropdown
|
||||
import Comp.ItemCardList
|
||||
import Comp.ItemDetail.EditMenu
|
||||
import Comp.ItemDetail.MultiEditMenu
|
||||
import Comp.SearchMenu
|
||||
import Comp.SearchStatsView
|
||||
import Comp.YesNoDimmer
|
||||
@ -191,7 +191,7 @@ viewLeftMenu flags settings model =
|
||||
EditSelected ->
|
||||
let
|
||||
cfg_ =
|
||||
Comp.ItemDetail.EditMenu.defaultViewConfig
|
||||
Comp.ItemDetail.MultiEditMenu.defaultViewConfig
|
||||
|
||||
cfg =
|
||||
{ cfg_
|
||||
@ -199,10 +199,10 @@ viewLeftMenu flags settings model =
|
||||
, customFieldState =
|
||||
\fId ->
|
||||
if Set.member fId svm.saveCustomFieldState then
|
||||
Comp.ItemDetail.EditMenu.Saving
|
||||
Comp.ItemDetail.MultiEditMenu.Saving
|
||||
|
||||
else
|
||||
Comp.ItemDetail.EditMenu.SaveSuccess
|
||||
Comp.ItemDetail.MultiEditMenu.SaveSuccess
|
||||
}
|
||||
in
|
||||
[ div [ class "ui dividing header" ]
|
||||
@ -212,7 +212,7 @@ viewLeftMenu flags settings model =
|
||||
[ text "Note that a change here immediatly affects all selected items on the right!"
|
||||
]
|
||||
, Html.map EditMenuMsg
|
||||
(Comp.ItemDetail.EditMenu.view cfg settings svm.editModel)
|
||||
(Comp.ItemDetail.MultiEditMenu.view cfg settings svm.editModel)
|
||||
]
|
||||
|
||||
_ ->
|
||||
@ -412,6 +412,5 @@ deleteAllDimmer =
|
||||
, headerClass = "ui inverted icon header"
|
||||
, confirmButton = "Yes"
|
||||
, cancelButton = "No"
|
||||
, invertedDimmer = False
|
||||
, extraClass = "top aligned"
|
||||
}
|
||||
|
320
modules/webapp/src/main/elm/Page/Home/View2.elm
Normal file
320
modules/webapp/src/main/elm/Page/Home/View2.elm
Normal file
@ -0,0 +1,320 @@
|
||||
module Page.Home.View2 exposing (viewContent, viewSidebar)
|
||||
|
||||
import Comp.Basic as B
|
||||
import Comp.ItemCardList
|
||||
import Comp.MenuBar as MB
|
||||
import Comp.SearchMenu
|
||||
import Comp.SearchStatsView
|
||||
import Comp.YesNoDimmer
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.ItemSelection
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick, onInput)
|
||||
import Page exposing (Page(..))
|
||||
import Page.Home.Data exposing (..)
|
||||
import Page.Home.SideMenu
|
||||
import Set
|
||||
import Styles as S
|
||||
import Util.Html
|
||||
|
||||
|
||||
viewSidebar : Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewSidebar visible flags settings model =
|
||||
div
|
||||
[ id "sidebar"
|
||||
, class S.sidebar
|
||||
, class S.sidebarBg
|
||||
, classList [ ( "hidden", not visible ) ]
|
||||
]
|
||||
[ Page.Home.SideMenu.view flags settings model
|
||||
]
|
||||
|
||||
|
||||
viewContent : Flags -> UiSettings -> Model -> Html Msg
|
||||
viewContent flags settings model =
|
||||
div
|
||||
[ id "item-card-list" -- this id is used in scroll-to-card
|
||||
, class S.content
|
||||
]
|
||||
(searchStats flags settings model
|
||||
++ itemsBar flags settings model
|
||||
++ itemCardList flags settings model
|
||||
++ deleteSelectedDimmer model
|
||||
)
|
||||
|
||||
|
||||
|
||||
--- Helpers
|
||||
|
||||
|
||||
deleteSelectedDimmer : Model -> List (Html Msg)
|
||||
deleteSelectedDimmer model =
|
||||
let
|
||||
selectAction =
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
svm.action
|
||||
|
||||
_ ->
|
||||
NoneAction
|
||||
|
||||
deleteAllDimmer : Comp.YesNoDimmer.Settings
|
||||
deleteAllDimmer =
|
||||
Comp.YesNoDimmer.defaultSettings2 "Really delete all selected items?"
|
||||
in
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
[ Html.map DeleteSelectedConfirmMsg
|
||||
(Comp.YesNoDimmer.viewN
|
||||
(selectAction == DeleteSelected)
|
||||
deleteAllDimmer
|
||||
svm.deleteAllConfirm
|
||||
)
|
||||
]
|
||||
|
||||
_ ->
|
||||
[]
|
||||
|
||||
|
||||
itemsBar : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
itemsBar flags settings model =
|
||||
case model.viewMode of
|
||||
SimpleView ->
|
||||
[ defaultMenuBar flags settings model ]
|
||||
|
||||
SearchView ->
|
||||
[ defaultMenuBar flags settings model ]
|
||||
|
||||
SelectView svm ->
|
||||
[ editMenuBar model svm ]
|
||||
|
||||
|
||||
defaultMenuBar : Flags -> UiSettings -> Model -> Html Msg
|
||||
defaultMenuBar flags settings model =
|
||||
let
|
||||
btnStyle =
|
||||
S.secondaryBasicButton ++ " text-sm"
|
||||
|
||||
searchInput =
|
||||
Comp.SearchMenu.textSearchString
|
||||
model.searchMenuModel.textSearchModel
|
||||
in
|
||||
MB.view
|
||||
{ end =
|
||||
[ MB.CustomElement <|
|
||||
B.secondaryBasicButton
|
||||
{ label = ""
|
||||
, icon =
|
||||
if model.searchInProgress then
|
||||
"fa fa-sync animate-spin"
|
||||
|
||||
else
|
||||
"fa fa-sync"
|
||||
, disabled = model.searchInProgress
|
||||
, handler = onClick ResetSearch
|
||||
, attrs = [ href "#" ]
|
||||
}
|
||||
, MB.CustomButton
|
||||
{ tagger = ToggleSelectView
|
||||
, label = ""
|
||||
, icon = Just "fa fa-tasks"
|
||||
, title = "Select Mode"
|
||||
, inputClass =
|
||||
[ ( btnStyle, True )
|
||||
, ( "bg-gray-200 dark:bg-bluegray-600", selectActive model )
|
||||
]
|
||||
}
|
||||
]
|
||||
, start =
|
||||
[ MB.CustomElement <|
|
||||
div
|
||||
[ class "relative flex flex-row" ]
|
||||
[ input
|
||||
[ type_ "text"
|
||||
, placeholder
|
||||
(case model.searchTypeDropdownValue of
|
||||
ContentOnlySearch ->
|
||||
"Content search…"
|
||||
|
||||
BasicSearch ->
|
||||
"Search in names…"
|
||||
)
|
||||
, onInput SetBasicSearch
|
||||
, Util.Html.onKeyUpCode KeyUpSearchbarMsg
|
||||
, Maybe.map value searchInput
|
||||
|> Maybe.withDefault (value "")
|
||||
, class (String.replace "rounded" "" S.textInput)
|
||||
, class "py-1 text-sm border-r-0 rounded-l"
|
||||
]
|
||||
[]
|
||||
, a
|
||||
[ class S.secondaryBasicButtonPlain
|
||||
, class "text-sm px-4 py-2 border rounded-r"
|
||||
, href "#"
|
||||
, onClick ToggleSearchType
|
||||
]
|
||||
[ i [ class "fa fa-exchange-alt" ] []
|
||||
]
|
||||
]
|
||||
, MB.CustomButton
|
||||
{ tagger = TogglePreviewFullWidth
|
||||
, label = ""
|
||||
, icon = Just "fa fa-expand"
|
||||
, title =
|
||||
if settings.cardPreviewFullWidth then
|
||||
"Full height preview"
|
||||
|
||||
else
|
||||
"Full width preview"
|
||||
, inputClass =
|
||||
[ ( btnStyle, True )
|
||||
, ( "hidden sm:inline-block", False )
|
||||
, ( "bg-gray-200 dark:bg-bluegray-600", settings.cardPreviewFullWidth )
|
||||
]
|
||||
}
|
||||
]
|
||||
, rootClasses = "mb-2 pt-1 dark:bg-bluegray-700 items-center text-sm"
|
||||
}
|
||||
|
||||
|
||||
editMenuBar : Model -> SelectViewModel -> Html Msg
|
||||
editMenuBar model svm =
|
||||
let
|
||||
selectCount =
|
||||
Set.size svm.ids |> String.fromInt
|
||||
|
||||
btnStyle =
|
||||
S.secondaryBasicButton ++ " text-sm"
|
||||
in
|
||||
MB.view
|
||||
{ start =
|
||||
[ MB.CustomButton
|
||||
{ tagger = EditSelectedItems
|
||||
, label = ""
|
||||
, icon = Just "fa fa-edit"
|
||||
, title = "Edit " ++ selectCount ++ " selected items"
|
||||
, inputClass =
|
||||
[ ( btnStyle, True )
|
||||
, ( "bg-gray-200 dark:bg-bluegray-600", svm.action == EditSelected )
|
||||
]
|
||||
}
|
||||
, MB.CustomButton
|
||||
{ tagger = RequestDeleteSelected
|
||||
, label = ""
|
||||
, icon = Just "fa fa-trash"
|
||||
, title = "Delete " ++ selectCount ++ " selected items"
|
||||
, inputClass =
|
||||
[ ( btnStyle, True )
|
||||
, ( "bg-gray-200 dark:bg-bluegray-600", svm.action == DeleteSelected )
|
||||
]
|
||||
}
|
||||
]
|
||||
, end =
|
||||
[ MB.CustomButton
|
||||
{ tagger = SelectAllItems
|
||||
, label = ""
|
||||
, icon = Just "fa fa-check-square font-thin"
|
||||
, title = "Select all visible"
|
||||
, inputClass =
|
||||
[ ( btnStyle, True )
|
||||
]
|
||||
}
|
||||
, MB.CustomButton
|
||||
{ tagger = SelectNoItems
|
||||
, label = ""
|
||||
, icon = Just "fa fa-square font-thin"
|
||||
, title = "Select none"
|
||||
, inputClass =
|
||||
[ ( btnStyle, True )
|
||||
]
|
||||
}
|
||||
, MB.TextLabel
|
||||
{ icon = ""
|
||||
, label = selectCount
|
||||
, class = "px-4 py-2 w-10 rounded-full font-bold bg-blue-100 dark:bg-lightblue-600 "
|
||||
}
|
||||
, MB.CustomButton
|
||||
{ tagger = ResetSearch
|
||||
, label = ""
|
||||
, icon = Just "fa fa-sync"
|
||||
, title = "Reset search form"
|
||||
, inputClass =
|
||||
[ ( btnStyle, True )
|
||||
, ( "hidden sm:block", True )
|
||||
]
|
||||
}
|
||||
, MB.CustomButton
|
||||
{ tagger = ToggleSelectView
|
||||
, label = ""
|
||||
, icon = Just "fa fa-tasks"
|
||||
, title = "Exit Select Mode"
|
||||
, inputClass =
|
||||
[ ( btnStyle, True )
|
||||
, ( "bg-gray-200 dark:bg-bluegray-600", selectActive model )
|
||||
]
|
||||
}
|
||||
]
|
||||
, rootClasses = "mb-2 pt-2 sticky top-0 text-sm"
|
||||
}
|
||||
|
||||
|
||||
searchStats : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
searchStats _ settings model =
|
||||
if settings.searchStatsVisible then
|
||||
[ Comp.SearchStatsView.view2 "my-2" model.searchStats
|
||||
]
|
||||
|
||||
else
|
||||
[]
|
||||
|
||||
|
||||
itemCardList : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
itemCardList flags settings model =
|
||||
let
|
||||
itemViewCfg =
|
||||
case model.viewMode of
|
||||
SelectView svm ->
|
||||
Comp.ItemCardList.ViewConfig
|
||||
model.scrollToCard
|
||||
(Data.ItemSelection.Active svm.ids)
|
||||
|
||||
_ ->
|
||||
Comp.ItemCardList.ViewConfig
|
||||
model.scrollToCard
|
||||
Data.ItemSelection.Inactive
|
||||
in
|
||||
[ Html.map ItemCardListMsg
|
||||
(Comp.ItemCardList.view2 itemViewCfg settings model.itemListModel)
|
||||
, loadMore model
|
||||
]
|
||||
|
||||
|
||||
loadMore : Model -> Html Msg
|
||||
loadMore model =
|
||||
let
|
||||
inactive =
|
||||
not model.moreAvailable || model.moreInProgress || model.searchInProgress
|
||||
in
|
||||
div
|
||||
[ class "h-40 flex flex-col items-center justify-center w-full"
|
||||
]
|
||||
[ B.secondaryBasicButton
|
||||
{ label =
|
||||
if model.moreAvailable then
|
||||
"Load more…"
|
||||
|
||||
else
|
||||
"That's all"
|
||||
, icon =
|
||||
if model.moreInProgress then
|
||||
"fa fa-circle-notch animate-spin"
|
||||
|
||||
else
|
||||
"fa fa-angle-double-down"
|
||||
, handler = onClick LoadMore
|
||||
, disabled = inactive
|
||||
, attrs = []
|
||||
}
|
||||
]
|
63
modules/webapp/src/main/elm/Page/ItemDetail/View2.elm
Normal file
63
modules/webapp/src/main/elm/Page/ItemDetail/View2.elm
Normal file
@ -0,0 +1,63 @@
|
||||
module Page.ItemDetail.View2 exposing (viewContent, viewSidebar)
|
||||
|
||||
import Comp.Basic as B
|
||||
import Comp.ItemDetail
|
||||
import Comp.ItemDetail.EditForm
|
||||
import Comp.ItemDetail.Model
|
||||
import Comp.MenuBar as MB
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.ItemNav exposing (ItemNav)
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
import Page.ItemDetail.Data exposing (..)
|
||||
import Styles as S
|
||||
|
||||
|
||||
viewSidebar : Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewSidebar visible _ settings model =
|
||||
div
|
||||
[ id "sidebar"
|
||||
, class S.sidebar
|
||||
, class S.sidebarBg
|
||||
, classList [ ( "hidden", not visible ) ]
|
||||
]
|
||||
[ div
|
||||
[ class S.header2
|
||||
, class "font-bold mt-2"
|
||||
]
|
||||
[ i [ class "fa fa-pencil-alt mr-2" ] []
|
||||
, text "Edit Metadata"
|
||||
]
|
||||
, MB.viewSide
|
||||
{ start =
|
||||
[ MB.CustomElement <|
|
||||
B.secondaryBasicButton
|
||||
{ label = ""
|
||||
, icon = "fa fa-expand-alt"
|
||||
, disabled = model.detail.item.state == "created"
|
||||
, handler = onClick (ItemDetailMsg Comp.ItemDetail.Model.ToggleOpenAllAkkordionTabs)
|
||||
, attrs =
|
||||
[ title "Collapse/Expand"
|
||||
, href "#"
|
||||
]
|
||||
}
|
||||
]
|
||||
, end = []
|
||||
, rootClasses = "text-sm mb-3 "
|
||||
}
|
||||
, Html.map ItemDetailMsg
|
||||
(Comp.ItemDetail.EditForm.view2 settings model.detail)
|
||||
]
|
||||
|
||||
|
||||
viewContent : ItemNav -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewContent inav _ settings model =
|
||||
div
|
||||
[ id "content"
|
||||
, class S.content
|
||||
]
|
||||
[ Html.map ItemDetailMsg
|
||||
(Comp.ItemDetail.view2 inav settings model.detail)
|
||||
]
|
157
modules/webapp/src/main/elm/Page/Login/View2.elm
Normal file
157
modules/webapp/src/main/elm/Page/Login/View2.elm
Normal file
@ -0,0 +1,157 @@
|
||||
module Page.Login.View2 exposing (viewContent, viewSidebar)
|
||||
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onCheck, onInput, onSubmit)
|
||||
import Page exposing (Page(..))
|
||||
import Page.Login.Data exposing (..)
|
||||
import Styles as S
|
||||
|
||||
|
||||
viewSidebar : Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewSidebar _ _ _ _ =
|
||||
div
|
||||
[ id "sidebar"
|
||||
, class "hidden"
|
||||
]
|
||||
[]
|
||||
|
||||
|
||||
viewContent : Flags -> UiSettings -> Model -> Html Msg
|
||||
viewContent flags _ model =
|
||||
div
|
||||
[ id "content"
|
||||
, class "h-full flex flex-col items-center justify-center w-full"
|
||||
, class S.content
|
||||
]
|
||||
[ div [ class ("flex flex-col px-4 sm:px-6 md:px-8 lg:px-10 py-8 rounded-md " ++ S.box) ]
|
||||
[ div [ class "self-center" ]
|
||||
[ img
|
||||
[ class "w-16 py-2"
|
||||
, src (flags.config.docspellAssetPath ++ "/img/logo-96.png")
|
||||
]
|
||||
[]
|
||||
]
|
||||
, div [ class "font-medium self-center text-xl sm:text-2xl" ]
|
||||
[ text "Login to Docspell"
|
||||
]
|
||||
, Html.form
|
||||
[ action "#"
|
||||
, onSubmit Authenticate
|
||||
, autocomplete False
|
||||
]
|
||||
[ div [ class "flex flex-col mt-6" ]
|
||||
[ label
|
||||
[ for "username"
|
||||
, class S.inputLabel
|
||||
]
|
||||
[ text "Username"
|
||||
]
|
||||
, div [ class "relative" ]
|
||||
[ div [ class S.inputIcon ]
|
||||
[ i [ class "fa fa-user" ] []
|
||||
]
|
||||
, input
|
||||
[ type_ "text"
|
||||
, name "username"
|
||||
, autocomplete False
|
||||
, onInput SetUsername
|
||||
, value model.username
|
||||
, autofocus True
|
||||
, class ("pl-10 pr-4 py-2 rounded-lg" ++ S.textInput)
|
||||
, placeholder "Collective / Login"
|
||||
]
|
||||
[]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-3" ]
|
||||
[ label
|
||||
[ for "password"
|
||||
, class S.inputLabel
|
||||
]
|
||||
[ text "Password"
|
||||
]
|
||||
, div [ class "relative" ]
|
||||
[ div [ class S.inputIcon ]
|
||||
[ i [ class "fa fa-lock" ] []
|
||||
]
|
||||
, input
|
||||
[ type_ "password"
|
||||
, name "password"
|
||||
, autocomplete False
|
||||
, onInput SetPassword
|
||||
, value model.password
|
||||
, class ("pl-10 pr-4 py-2 rounded-lg" ++ S.textInput)
|
||||
, placeholder "Password"
|
||||
]
|
||||
[]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-3" ]
|
||||
[ label
|
||||
[ class "inline-flex items-center"
|
||||
, for "rememberme"
|
||||
]
|
||||
[ input
|
||||
[ id "rememberme"
|
||||
, type_ "checkbox"
|
||||
, onCheck (\_ -> ToggleRememberMe)
|
||||
, checked model.rememberMe
|
||||
, name "rememberme"
|
||||
, class S.checkboxInput
|
||||
]
|
||||
[]
|
||||
, span
|
||||
[ class "mb-1 ml-2 text-xs sm:text-sm tracking-wide my-1"
|
||||
]
|
||||
[ text "Remember Me"
|
||||
]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-3" ]
|
||||
[ button
|
||||
[ type_ "submit"
|
||||
, class S.primaryButton
|
||||
]
|
||||
[ text "Login"
|
||||
]
|
||||
]
|
||||
, resultMessage model
|
||||
, div
|
||||
[ class "flex justify-end text-sm pt-4"
|
||||
, classList [ ( "hidden", flags.config.signupMode == "closed" ) ]
|
||||
]
|
||||
[ span []
|
||||
[ text "No account?"
|
||||
]
|
||||
, a
|
||||
[ Page.href RegisterPage
|
||||
, class ("ml-2" ++ S.link)
|
||||
]
|
||||
[ i [ class "fa fa-user-plus mr-1" ] []
|
||||
, text "Sign up"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
resultMessage : Model -> Html Msg
|
||||
resultMessage model =
|
||||
case model.result of
|
||||
Just r ->
|
||||
if r.success then
|
||||
div [ class ("my-2" ++ S.successMessage) ]
|
||||
[ text "Login successful."
|
||||
]
|
||||
|
||||
else
|
||||
div [ class ("my-2" ++ S.errorMessage) ]
|
||||
[ text r.message
|
||||
]
|
||||
|
||||
Nothing ->
|
||||
span [ class "hidden" ] []
|
@ -26,16 +26,20 @@ type alias Model =
|
||||
|
||||
|
||||
init : Flags -> ( Model, Cmd Msg )
|
||||
init _ =
|
||||
( { currentTab = Nothing
|
||||
, tagManageModel = Comp.TagManage.emptyModel
|
||||
init flags =
|
||||
let
|
||||
( m2, c2 ) =
|
||||
Comp.TagManage.update flags Comp.TagManage.LoadTags Comp.TagManage.emptyModel
|
||||
in
|
||||
( { currentTab = Just TagTab
|
||||
, tagManageModel = m2
|
||||
, equipManageModel = Comp.EquipmentManage.emptyModel
|
||||
, orgManageModel = Comp.OrgManage.emptyModel
|
||||
, personManageModel = Comp.PersonManage.emptyModel
|
||||
, folderManageModel = Comp.FolderManage.empty
|
||||
, fieldManageModel = Comp.CustomFieldManage.empty
|
||||
}
|
||||
, Cmd.none
|
||||
, Cmd.map TagManageMsg c2
|
||||
)
|
||||
|
||||
|
||||
|
245
modules/webapp/src/main/elm/Page/ManageData/View2.elm
Normal file
245
modules/webapp/src/main/elm/Page/ManageData/View2.elm
Normal file
@ -0,0 +1,245 @@
|
||||
module Page.ManageData.View2 exposing (viewContent, viewSidebar)
|
||||
|
||||
import Comp.CustomFieldManage
|
||||
import Comp.EquipmentManage
|
||||
import Comp.FolderManage
|
||||
import Comp.OrgManage
|
||||
import Comp.PersonManage
|
||||
import Comp.TagManage
|
||||
import Data.Fields
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.Icons as Icons
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
import Page.ManageData.Data exposing (..)
|
||||
import Styles as S
|
||||
|
||||
|
||||
viewSidebar : Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewSidebar visible _ settings model =
|
||||
div
|
||||
[ id "sidebar"
|
||||
, class S.sidebar
|
||||
, class S.sidebarBg
|
||||
, classList [ ( "hidden", not visible ) ]
|
||||
]
|
||||
[ div [ class "" ]
|
||||
[ h1 [ class S.header1 ]
|
||||
[ text "Manage Data"
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-2" ]
|
||||
[ a
|
||||
[ href "#"
|
||||
, onClick (SetTab TagTab)
|
||||
, class S.sidebarLink
|
||||
, menuEntryActive model TagTab
|
||||
]
|
||||
[ Icons.tagIcon2 ""
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Tags" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab EquipTab)
|
||||
, menuEntryActive model EquipTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ Icons.equipmentIcon2 ""
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Equipment" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab OrgTab)
|
||||
, menuEntryActive model OrgTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ Icons.organizationIcon2 ""
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Organization" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab PersonTab)
|
||||
, menuEntryActive model PersonTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ Icons.personIcon2 ""
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Person" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, classList
|
||||
[ ( "hidden"
|
||||
, Data.UiSettings.fieldHidden settings Data.Fields.Folder
|
||||
)
|
||||
]
|
||||
, onClick (SetTab FolderTab)
|
||||
, menuEntryActive model FolderTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ Icons.folderIcon2 ""
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Folder" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, classList
|
||||
[ ( "invisible hidden"
|
||||
, Data.UiSettings.fieldHidden settings Data.Fields.CustomFields
|
||||
)
|
||||
]
|
||||
, onClick (SetTab CustomFieldTab)
|
||||
, menuEntryActive model CustomFieldTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ Icons.customFieldIcon2 ""
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Custom Fields" ]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
viewContent : Flags -> UiSettings -> Model -> Html Msg
|
||||
viewContent flags settings model =
|
||||
div
|
||||
[ id "content"
|
||||
, class S.content
|
||||
]
|
||||
(case model.currentTab of
|
||||
Just TagTab ->
|
||||
viewTags model
|
||||
|
||||
Just EquipTab ->
|
||||
viewEquip model
|
||||
|
||||
Just OrgTab ->
|
||||
viewOrg settings model
|
||||
|
||||
Just PersonTab ->
|
||||
viewPerson settings model
|
||||
|
||||
Just FolderTab ->
|
||||
viewFolder flags settings model
|
||||
|
||||
Just CustomFieldTab ->
|
||||
viewCustomFields flags settings model
|
||||
|
||||
Nothing ->
|
||||
[]
|
||||
)
|
||||
|
||||
|
||||
menuEntryActive : Model -> Tab -> Attribute msg
|
||||
menuEntryActive model tab =
|
||||
if model.currentTab == Just tab then
|
||||
class S.sidebarMenuItemActive
|
||||
|
||||
else
|
||||
class ""
|
||||
|
||||
|
||||
viewTags : Model -> List (Html Msg)
|
||||
viewTags model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ Icons.tagIcon2 ""
|
||||
, div [ class "ml-2" ]
|
||||
[ text "Tags"
|
||||
]
|
||||
]
|
||||
, Html.map TagManageMsg (Comp.TagManage.view2 model.tagManageModel)
|
||||
]
|
||||
|
||||
|
||||
viewEquip : Model -> List (Html Msg)
|
||||
viewEquip model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ Icons.equipmentIcon2 ""
|
||||
, div [ class "ml-2" ]
|
||||
[ text "Equipment"
|
||||
]
|
||||
]
|
||||
, Html.map EquipManageMsg (Comp.EquipmentManage.view2 model.equipManageModel)
|
||||
]
|
||||
|
||||
|
||||
viewOrg : UiSettings -> Model -> List (Html Msg)
|
||||
viewOrg settings model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ Icons.organizationIcon2 ""
|
||||
, div [ class "ml-2" ]
|
||||
[ text "Organizations"
|
||||
]
|
||||
]
|
||||
, Html.map OrgManageMsg (Comp.OrgManage.view2 settings model.orgManageModel)
|
||||
]
|
||||
|
||||
|
||||
viewPerson : UiSettings -> Model -> List (Html Msg)
|
||||
viewPerson settings model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ Icons.personIcon2 ""
|
||||
, div [ class "ml-2" ]
|
||||
[ text "Person"
|
||||
]
|
||||
]
|
||||
, Html.map PersonManageMsg
|
||||
(Comp.PersonManage.view2 settings model.personManageModel)
|
||||
]
|
||||
|
||||
|
||||
viewFolder : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
viewFolder flags _ model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ Icons.folderIcon2 ""
|
||||
, div
|
||||
[ class "ml-2"
|
||||
]
|
||||
[ text "Folder"
|
||||
]
|
||||
]
|
||||
, Html.map FolderMsg
|
||||
(Comp.FolderManage.view2 flags model.folderManageModel)
|
||||
]
|
||||
|
||||
|
||||
viewCustomFields : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
viewCustomFields flags _ model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ Icons.customFieldIcon2 ""
|
||||
, div [ class "ml-2" ]
|
||||
[ text "Custom Fields"
|
||||
]
|
||||
]
|
||||
, Html.map CustomFieldMsg
|
||||
(Comp.CustomFieldManage.view2 flags model.fieldManageModel)
|
||||
]
|
137
modules/webapp/src/main/elm/Page/NewInvite/View2.elm
Normal file
137
modules/webapp/src/main/elm/Page/NewInvite/View2.elm
Normal file
@ -0,0 +1,137 @@
|
||||
module Page.NewInvite.View2 exposing (viewContent, viewSidebar)
|
||||
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick, onInput, onSubmit)
|
||||
import Page.NewInvite.Data exposing (..)
|
||||
import Styles as S
|
||||
|
||||
|
||||
viewSidebar : Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewSidebar _ _ _ _ =
|
||||
div
|
||||
[ id "sidebar"
|
||||
, class "hidden"
|
||||
]
|
||||
[]
|
||||
|
||||
|
||||
viewContent : Flags -> UiSettings -> Model -> Html Msg
|
||||
viewContent flags _ model =
|
||||
div
|
||||
[ id "content"
|
||||
, class "flex flex-col md:w-3/5 px-2"
|
||||
, class S.content
|
||||
]
|
||||
[ h1 [ class S.header1 ] [ text "Create new invitations" ]
|
||||
, inviteMessage flags
|
||||
, div [ class " py-2 mt-6 rounded" ]
|
||||
[ Html.form
|
||||
[ action "#"
|
||||
, onSubmit GenerateInvite
|
||||
, autocomplete False
|
||||
]
|
||||
[ div [ class "flex flex-col" ]
|
||||
[ label
|
||||
[ for "invitekey"
|
||||
, class "mb-1 text-xs sm:text-sm tracking-wide "
|
||||
]
|
||||
[ text "Invitation key"
|
||||
]
|
||||
, div [ class "relative" ]
|
||||
[ div [ class "inline-flex items-center justify-center absolute left-0 top-0 h-full w-10 text-gray-400 dark:text-bluegray-400 " ]
|
||||
[ i [ class "fa fa-key" ] []
|
||||
]
|
||||
, input
|
||||
[ id "email"
|
||||
, type_ "password"
|
||||
, name "invitekey"
|
||||
, autocomplete False
|
||||
, onInput SetPassword
|
||||
, value model.password
|
||||
, autofocus True
|
||||
, class ("pl-10 pr-4 py-2 rounded-lg" ++ S.textInput)
|
||||
, placeholder "Password"
|
||||
]
|
||||
[]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-3" ]
|
||||
[ div [ class "flex flex-row space-x-2" ]
|
||||
[ button
|
||||
[ type_ "submit"
|
||||
, class (S.primaryButton ++ "inline-flex")
|
||||
]
|
||||
[ text "Submit"
|
||||
]
|
||||
, a
|
||||
[ class S.secondaryButton
|
||||
, href "#"
|
||||
, onClick Reset
|
||||
]
|
||||
[ text "Reset"
|
||||
]
|
||||
]
|
||||
]
|
||||
, resultMessage model
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
resultMessage : Model -> Html Msg
|
||||
resultMessage model =
|
||||
div
|
||||
[ classList
|
||||
[ ( S.errorMessage, isFailed model.result )
|
||||
, ( S.successMessage, isSuccess model.result )
|
||||
, ( "hidden", model.result == Empty )
|
||||
]
|
||||
]
|
||||
[ case model.result of
|
||||
Failed m ->
|
||||
p [] [ text m ]
|
||||
|
||||
Success r ->
|
||||
div [ class "" ]
|
||||
[ p []
|
||||
[ text r.message
|
||||
, text " Invitation Key:"
|
||||
]
|
||||
, pre [ class "text-center font-mono mt-4" ]
|
||||
[ Maybe.withDefault "" r.key |> text
|
||||
]
|
||||
]
|
||||
|
||||
Empty ->
|
||||
span [ class "hidden" ] []
|
||||
]
|
||||
|
||||
|
||||
inviteMessage : Flags -> Html Msg
|
||||
inviteMessage flags =
|
||||
div
|
||||
[ class (S.message ++ "text-sm")
|
||||
, classList
|
||||
[ ( "hidden", flags.config.signupMode /= "invite" )
|
||||
]
|
||||
]
|
||||
[ p []
|
||||
[ text
|
||||
"""Docspell requires an invite when signing up. You can
|
||||
create these invites here and send them to friends so
|
||||
they can signup with docspell."""
|
||||
]
|
||||
, p []
|
||||
[ text
|
||||
"""Each invite can only be used once. You'll need to
|
||||
create one key for each person you want to invite."""
|
||||
]
|
||||
, p []
|
||||
[ text
|
||||
"""Creating an invite requires providing the password
|
||||
from the configuration."""
|
||||
]
|
||||
]
|
@ -1,6 +1,7 @@
|
||||
module Page.Queue.Data exposing
|
||||
( Model
|
||||
, Msg(..)
|
||||
, QueueView(..)
|
||||
, emptyModel
|
||||
, getDuration
|
||||
, getRunningTime
|
||||
@ -27,9 +28,18 @@ type alias Model =
|
||||
, showLog : Maybe JobDetail
|
||||
, deleteConfirm : Comp.YesNoDimmer.Model
|
||||
, cancelJobRequest : Maybe String
|
||||
, queueView : QueueView
|
||||
}
|
||||
|
||||
|
||||
type QueueView
|
||||
= CurrentJobs
|
||||
| QueueAll
|
||||
| QueueWaiting
|
||||
| QueueError
|
||||
| QueueSuccess
|
||||
|
||||
|
||||
emptyModel : Model
|
||||
emptyModel =
|
||||
{ state = Api.Model.JobQueueState.empty
|
||||
@ -41,6 +51,7 @@ emptyModel =
|
||||
, showLog = Nothing
|
||||
, deleteConfirm = Comp.YesNoDimmer.emptyModel
|
||||
, cancelJobRequest = Nothing
|
||||
, queueView = CurrentJobs
|
||||
}
|
||||
|
||||
|
||||
@ -55,6 +66,7 @@ type Msg
|
||||
| DimmerMsg JobDetail Comp.YesNoDimmer.Msg
|
||||
| CancelResp (Result Http.Error BasicResult)
|
||||
| ChangePrio String Priority
|
||||
| SetQueueView QueueView
|
||||
|
||||
|
||||
getRunningTime : Model -> JobDetail -> Maybe String
|
||||
|
@ -86,6 +86,9 @@ update flags msg model =
|
||||
ChangePrio id prio ->
|
||||
( model, Api.setJobPrio flags id prio CancelResp )
|
||||
|
||||
SetQueueView v ->
|
||||
( { model | queueView = v }, Cmd.none )
|
||||
|
||||
|
||||
getNewTime : Cmd Msg
|
||||
getNewTime =
|
||||
|
440
modules/webapp/src/main/elm/Page/Queue/View2.elm
Normal file
440
modules/webapp/src/main/elm/Page/Queue/View2.elm
Normal file
@ -0,0 +1,440 @@
|
||||
module Page.Queue.View2 exposing (viewContent, viewSidebar)
|
||||
|
||||
import Api.Model.JobDetail exposing (JobDetail)
|
||||
import Api.Model.JobLogEvent exposing (JobLogEvent)
|
||||
import Comp.Progress
|
||||
import Comp.YesNoDimmer
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.Priority
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
import Page.Queue.Data exposing (..)
|
||||
import Styles as S
|
||||
import Util.Time exposing (formatDateTime, formatIsoDateTime)
|
||||
|
||||
|
||||
viewSidebar : Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewSidebar visible _ _ model =
|
||||
let
|
||||
tabLink cls v icon label =
|
||||
a
|
||||
[ href "#"
|
||||
, class S.sidebarLink
|
||||
, class cls
|
||||
, classList [ ( "bg-blue-100 dark:bg-bluegray-600", model.queueView == v ) ]
|
||||
, onClick (SetQueueView v)
|
||||
]
|
||||
[ i [ class icon ]
|
||||
[]
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text label ]
|
||||
]
|
||||
in
|
||||
div
|
||||
[ id "sidebar"
|
||||
, class S.sidebar
|
||||
, class S.sidebarBg
|
||||
, classList [ ( "hidden", not visible ) ]
|
||||
]
|
||||
[ div [ class "" ]
|
||||
[ h1 [ class S.header1 ]
|
||||
[ text "Processing Queue"
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-2" ]
|
||||
[ tabLink "" CurrentJobs "fa fa-play-circle" "Currently Running"
|
||||
, tabLink "" QueueAll "fa fa-hourglass-half" "Queue"
|
||||
, tabLink "ml-8" QueueWaiting "fa fa-clock" "Waiting"
|
||||
, tabLink "ml-8" QueueError "fa fa-bolt" "Errored"
|
||||
, tabLink "ml-8" QueueSuccess "fa fa-check" "Success"
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
viewContent : Flags -> UiSettings -> Model -> Html Msg
|
||||
viewContent _ _ model =
|
||||
let
|
||||
gridStyle =
|
||||
"grid gap-4 grid-cols-1 md:grid-cols-2"
|
||||
|
||||
isState state job =
|
||||
state == job.state
|
||||
|
||||
message str =
|
||||
div [ class "h-28 flex flex-col items-center justify-center w-full" ]
|
||||
[ div [ class S.header2 ]
|
||||
[ text str
|
||||
]
|
||||
]
|
||||
in
|
||||
div
|
||||
[ class "py-2"
|
||||
, class S.content
|
||||
]
|
||||
[ case model.showLog of
|
||||
Just job ->
|
||||
renderJobLog job
|
||||
|
||||
Nothing ->
|
||||
span [ class "hidden" ] []
|
||||
, case model.queueView of
|
||||
CurrentJobs ->
|
||||
if List.isEmpty model.state.progress then
|
||||
message "No jobs currently running."
|
||||
|
||||
else
|
||||
div [ class "flex flex-col space-y-2" ]
|
||||
(List.map (renderProgressCard model) model.state.progress)
|
||||
|
||||
QueueAll ->
|
||||
if List.isEmpty model.state.completed && List.isEmpty model.state.completed then
|
||||
message "No jobs to display."
|
||||
|
||||
else
|
||||
div [ class gridStyle ]
|
||||
(List.map (renderInfoCard model)
|
||||
(model.state.queued ++ model.state.completed)
|
||||
)
|
||||
|
||||
QueueWaiting ->
|
||||
if List.isEmpty model.state.queued then
|
||||
message "No waiting jobs."
|
||||
|
||||
else
|
||||
div [ class gridStyle ]
|
||||
(List.map (renderInfoCard model) model.state.queued)
|
||||
|
||||
QueueError ->
|
||||
let
|
||||
items =
|
||||
List.filter (isState "failed") model.state.completed
|
||||
in
|
||||
if List.isEmpty items then
|
||||
message "No failed jobs to display."
|
||||
|
||||
else
|
||||
div [ class gridStyle ]
|
||||
(List.map (renderInfoCard model) items)
|
||||
|
||||
QueueSuccess ->
|
||||
let
|
||||
items =
|
||||
List.filter (isState "success") model.state.completed
|
||||
in
|
||||
if List.isEmpty items then
|
||||
message "No succesfull jobs to display."
|
||||
|
||||
else
|
||||
div [ class gridStyle ]
|
||||
(List.map (renderInfoCard model) items)
|
||||
]
|
||||
|
||||
|
||||
renderJobLog : JobDetail -> Html Msg
|
||||
renderJobLog job =
|
||||
div
|
||||
[ class " absolute top-12 left-0 w-full h-full-12 z-40 flex flex-col items-center px-4 py-2 "
|
||||
, class "bg-white bg-opacity-80 dark:bg-black dark:bg-bluegray-900 dark:bg-opacity-90"
|
||||
]
|
||||
[ div [ class (S.box ++ "py-2 px-2 flex flex-col w-full") ]
|
||||
[ div [ class "flex flex-row mb-4 px-2" ]
|
||||
[ span [ class "font-semibold" ]
|
||||
[ text job.name
|
||||
]
|
||||
, div [ class "flex-grow flex flex-row justify-end" ]
|
||||
[ a
|
||||
[ href "#"
|
||||
, class S.link
|
||||
, onClick QuitShowLog
|
||||
]
|
||||
[ i [ class "fa fa-times" ] []
|
||||
]
|
||||
]
|
||||
]
|
||||
, div [ class styleJobLog ]
|
||||
(List.map renderLogLine job.logs)
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
renderProgressCard : Model -> JobDetail -> Html Msg
|
||||
renderProgressCard model job =
|
||||
div [ class (S.box ++ "px-2 flex flex-col") ]
|
||||
[ Comp.Progress.topAttachedIndicating job.progress
|
||||
, Html.map (DimmerMsg job)
|
||||
(Comp.YesNoDimmer.viewN
|
||||
(model.cancelJobRequest == Just job.id)
|
||||
dimmerSettings
|
||||
model.deleteConfirm
|
||||
)
|
||||
, div [ class "py-2 flex flex-row x-space-2 items-center" ]
|
||||
[ div [ class "flex flex-row items-center py-0.5" ]
|
||||
[ i [ class "fa fa-circle-notch animate-spin" ] []
|
||||
, span [ class "ml-2" ]
|
||||
[ text job.name
|
||||
]
|
||||
]
|
||||
, div [ class "flex-grow flex flex-row items-center justify-end" ]
|
||||
[ div [ class S.basicLabel ]
|
||||
[ text job.state
|
||||
, div [ class "ml-3" ]
|
||||
[ Maybe.withDefault "" job.worker |> text
|
||||
]
|
||||
]
|
||||
, div [ class (S.basicLabel ++ "ml-2") ]
|
||||
[ i [ class "fa fa-clock" ] []
|
||||
, div [ class "ml-3" ]
|
||||
[ getDuration model job |> Maybe.withDefault "-:-" |> text
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
, div [ class "py-2", id "joblog" ]
|
||||
[ div [ class styleJobLog ]
|
||||
(List.map renderLogLine job.logs)
|
||||
]
|
||||
, div [ class "py-2 flex flex-row justify-end" ]
|
||||
[ button [ class S.secondaryButton, onClick (RequestCancelJob job) ]
|
||||
[ text "Cancel"
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
styleJobLog : String
|
||||
styleJobLog =
|
||||
"bg-gray-900 text-xs leading-5 px-2 py-1 font-mono text-gray-100 overflow-auto max-h-96 rounded"
|
||||
|
||||
|
||||
renderLogLine : JobLogEvent -> Html Msg
|
||||
renderLogLine log =
|
||||
let
|
||||
lineStyle =
|
||||
case String.toLower log.level of
|
||||
"info" ->
|
||||
""
|
||||
|
||||
"debug" ->
|
||||
"opacity-50"
|
||||
|
||||
"warn" ->
|
||||
"text-yellow-400"
|
||||
|
||||
"error" ->
|
||||
"text-red-400"
|
||||
|
||||
_ ->
|
||||
""
|
||||
in
|
||||
span [ class lineStyle ]
|
||||
[ formatIsoDateTime log.time |> text
|
||||
, text ": "
|
||||
, text log.message
|
||||
, br [] []
|
||||
]
|
||||
|
||||
|
||||
isFinal : JobDetail -> Bool
|
||||
isFinal job =
|
||||
case job.state of
|
||||
"failed" ->
|
||||
True
|
||||
|
||||
"success" ->
|
||||
True
|
||||
|
||||
"cancelled" ->
|
||||
True
|
||||
|
||||
_ ->
|
||||
False
|
||||
|
||||
|
||||
dimmerSettings : Comp.YesNoDimmer.Settings
|
||||
dimmerSettings =
|
||||
let
|
||||
defaults =
|
||||
Comp.YesNoDimmer.defaultSettings
|
||||
in
|
||||
{ defaults
|
||||
| headerClass = "text-lg text-white"
|
||||
, headerIcon = ""
|
||||
, extraClass = "rounded"
|
||||
, message = "Cancel/Delete this job?"
|
||||
}
|
||||
|
||||
|
||||
renderInfoCard : Model -> JobDetail -> Html Msg
|
||||
renderInfoCard model job =
|
||||
let
|
||||
prio =
|
||||
Data.Priority.fromString job.priority
|
||||
|> Maybe.withDefault Data.Priority.Low
|
||||
|
||||
color solid =
|
||||
jobStateColor job solid
|
||||
|
||||
labelStyle solid =
|
||||
" label min-h-6 inline text-xs font-semibold " ++ color solid ++ " "
|
||||
in
|
||||
div
|
||||
[ class (S.box ++ "px-4 py-4 flex flex-col rounded relative")
|
||||
]
|
||||
[ Html.map (DimmerMsg job)
|
||||
(Comp.YesNoDimmer.viewN
|
||||
(model.cancelJobRequest == Just job.id)
|
||||
dimmerSettings
|
||||
model.deleteConfirm
|
||||
)
|
||||
, div [ class "flex flex-row" ]
|
||||
[ div [ class "flex-grow items-center" ]
|
||||
[ i
|
||||
[ classList
|
||||
[ ( "fa fa-check", job.state == "success" )
|
||||
, ( "fa fa-redo", job.state == "stuck" )
|
||||
, ( "fa fa-bolt", job.state == "failed" )
|
||||
, ( "fa fa-meh-outline", job.state == "canceled" )
|
||||
, ( "fa fa-cog", not (isFinal job) && job.state /= "stuck" )
|
||||
]
|
||||
, class "justify-center"
|
||||
]
|
||||
[]
|
||||
, div [ class (labelStyle True ++ " ml-2") ]
|
||||
[ text job.state
|
||||
]
|
||||
, div [ class "ml-2 break-all hidden sm:inline-block" ]
|
||||
[ text job.name
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-row space-x-2" ]
|
||||
[ a
|
||||
[ onClick (ShowLog job)
|
||||
, href "#"
|
||||
, class S.link
|
||||
, classList [ ( "hidden", not (isFinal job || job.state == "stuck") ) ]
|
||||
]
|
||||
[ i [ class "fa fa-file", title "Show log" ] []
|
||||
]
|
||||
, a
|
||||
[ title "Remove"
|
||||
, href "#"
|
||||
, class S.link
|
||||
, onClick (RequestCancelJob job)
|
||||
]
|
||||
[ i
|
||||
[ class "fa fa-times"
|
||||
]
|
||||
[]
|
||||
]
|
||||
, div
|
||||
[ classList [ ( "hidden", isFinal job ) ]
|
||||
]
|
||||
[ div [ class "font-mono" ]
|
||||
[ getDuration model job |> Maybe.withDefault "3:12" |> text
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
, div [ class "sm:hidden mt-1 break-all" ]
|
||||
[ text job.name
|
||||
]
|
||||
, div [ class "my-2" ]
|
||||
[ hr [ class S.border ] []
|
||||
]
|
||||
, div [ class "flex flex-row space-x-2 items-center flex-wrap" ]
|
||||
[ div [ class "flex flex-row justify-start " ]
|
||||
[ div [ class "text-xs font-semibold" ]
|
||||
[ Util.Time.formatDateTime job.submitted |> text
|
||||
]
|
||||
]
|
||||
, div [ class "flex-grow flex flex-row justify-end space-x-2 flex-wrap" ]
|
||||
[ div
|
||||
[ class (labelStyle False)
|
||||
, classList [ ( "hidden", not (isFinal job) ) ]
|
||||
]
|
||||
[ i [ class "fa fa-clock mr-3" ] []
|
||||
, span []
|
||||
[ getDuration model job |> Maybe.withDefault "-:-" |> text
|
||||
]
|
||||
]
|
||||
, div [ class (labelStyle False) ]
|
||||
[ span [ class "mr-3" ]
|
||||
[ text "Retries"
|
||||
]
|
||||
, span []
|
||||
[ job.retries |> String.fromInt |> text
|
||||
]
|
||||
]
|
||||
, case job.state of
|
||||
"waiting" ->
|
||||
a
|
||||
[ class (labelStyle False)
|
||||
, onClick (ChangePrio job.id (Data.Priority.next prio))
|
||||
, href "#"
|
||||
, title "Change priority of this job"
|
||||
]
|
||||
[ i [ class "sort numeric up icon" ] []
|
||||
, text "Prio"
|
||||
, div [ class "detail" ]
|
||||
[ code []
|
||||
[ Data.Priority.fromString job.priority
|
||||
|> Maybe.map Data.Priority.toName
|
||||
|> Maybe.withDefault job.priority
|
||||
|> text
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
_ ->
|
||||
div
|
||||
[ class (labelStyle False)
|
||||
]
|
||||
[ span [ class "mr-3" ]
|
||||
[ text "Prio"
|
||||
]
|
||||
, code [ class "font-mono" ]
|
||||
[ Data.Priority.fromString job.priority
|
||||
|> Maybe.map Data.Priority.toName
|
||||
|> Maybe.withDefault job.priority
|
||||
|> text
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
jobStateColor : JobDetail -> Bool -> String
|
||||
jobStateColor job solid =
|
||||
case job.state of
|
||||
"success" ->
|
||||
if solid then
|
||||
S.greenSolidLabel
|
||||
|
||||
else
|
||||
S.greenBasicLabel
|
||||
|
||||
"failed" ->
|
||||
if solid then
|
||||
S.redSolidLabel
|
||||
|
||||
else
|
||||
S.redBasicLabel
|
||||
|
||||
"canceled" ->
|
||||
"text-orange-500 border-orange-500"
|
||||
|
||||
"stuck" ->
|
||||
"text-purple-500 border-purple-500"
|
||||
|
||||
"scheduled" ->
|
||||
"text-blue-500 border-blue-500"
|
||||
|
||||
"waiting" ->
|
||||
"text-gray-500 border-gray-500"
|
||||
|
||||
_ ->
|
||||
""
|
259
modules/webapp/src/main/elm/Page/Register/View2.elm
Normal file
259
modules/webapp/src/main/elm/Page/Register/View2.elm
Normal file
@ -0,0 +1,259 @@
|
||||
module Page.Register.View2 exposing (viewContent, viewSidebar)
|
||||
|
||||
import Comp.Basic as B
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick, onInput, onSubmit)
|
||||
import Page exposing (Page(..))
|
||||
import Page.Register.Data exposing (..)
|
||||
import Styles as S
|
||||
|
||||
|
||||
viewSidebar : Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewSidebar _ _ _ _ =
|
||||
div
|
||||
[ id "sidebar"
|
||||
, class "hidden"
|
||||
]
|
||||
[]
|
||||
|
||||
|
||||
viewContent : Flags -> UiSettings -> Model -> Html Msg
|
||||
viewContent flags _ model =
|
||||
div
|
||||
[ id "content"
|
||||
, class "h-full flex flex-col items-center justify-center w-full"
|
||||
, class S.content
|
||||
]
|
||||
[ div [ class ("flex flex-col px-4 sm:px-6 md:px-8 lg:px-10 lg:w-2/5 py-8 rounded-md " ++ S.box) ]
|
||||
[ div [ class "self-center" ]
|
||||
[ img
|
||||
[ class "w-16 py-2"
|
||||
, src (flags.config.docspellAssetPath ++ "/img/logo-96.png")
|
||||
]
|
||||
[]
|
||||
]
|
||||
, div [ class "font-medium self-center text-xl sm:text-2xl" ]
|
||||
[ text "Signup to Docspell"
|
||||
]
|
||||
, Html.form
|
||||
[ action "#"
|
||||
, onSubmit RegisterSubmit
|
||||
, autocomplete False
|
||||
]
|
||||
[ div [ class "flex flex-col mt-6" ]
|
||||
[ label
|
||||
[ for "username"
|
||||
, class S.inputLabel
|
||||
]
|
||||
[ text "Collective ID"
|
||||
, B.inputRequired
|
||||
]
|
||||
, div [ class "relative" ]
|
||||
[ div [ class S.inputIcon ]
|
||||
[ i [ class "fa fa-users" ] []
|
||||
]
|
||||
, input
|
||||
[ type_ "text"
|
||||
, name "collective"
|
||||
, autocomplete False
|
||||
, onInput SetCollId
|
||||
, value model.collId
|
||||
, autofocus True
|
||||
, class ("pl-10 pr-4 py-2 rounded-lg" ++ S.textInput)
|
||||
, placeholder "Collective"
|
||||
]
|
||||
[]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-3" ]
|
||||
[ label
|
||||
[ for "user"
|
||||
, class S.inputLabel
|
||||
]
|
||||
[ text "User Login"
|
||||
, B.inputRequired
|
||||
]
|
||||
, div [ class "relative" ]
|
||||
[ div [ class S.inputIcon ]
|
||||
[ i [ class "fa fa-user" ] []
|
||||
]
|
||||
, input
|
||||
[ type_ "text"
|
||||
, name "user"
|
||||
, autocomplete False
|
||||
, onInput SetLogin
|
||||
, value model.login
|
||||
, class ("pl-10 pr-4 py-2 rounded-lg" ++ S.textInput)
|
||||
, placeholder "Username"
|
||||
]
|
||||
[]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-3" ]
|
||||
[ label
|
||||
[ for "passw1"
|
||||
, class S.inputLabel
|
||||
]
|
||||
[ text "Password"
|
||||
, B.inputRequired
|
||||
]
|
||||
, div [ class "relative" ]
|
||||
[ div [ class S.inputIcon ]
|
||||
[ i
|
||||
[ class "fa"
|
||||
, if model.showPass1 then
|
||||
class "fa-lock-open"
|
||||
|
||||
else
|
||||
class "fa-lock"
|
||||
]
|
||||
[]
|
||||
]
|
||||
, input
|
||||
[ type_ <|
|
||||
if model.showPass1 then
|
||||
"text"
|
||||
|
||||
else
|
||||
"password"
|
||||
, name "passw1"
|
||||
, autocomplete False
|
||||
, onInput SetPass1
|
||||
, value model.pass1
|
||||
, class ("pl-10 pr-10 py-2 rounded-lg" ++ S.textInput)
|
||||
, placeholder "Password"
|
||||
]
|
||||
[]
|
||||
, a
|
||||
[ class S.inputLeftIconLink
|
||||
, onClick ToggleShowPass1
|
||||
, href "#"
|
||||
]
|
||||
[ i [ class "fa fa-eye" ] []
|
||||
]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-3" ]
|
||||
[ label
|
||||
[ for "passw2"
|
||||
, class S.inputLabel
|
||||
]
|
||||
[ text "Password (repeat)"
|
||||
, B.inputRequired
|
||||
]
|
||||
, div [ class "relative" ]
|
||||
[ div [ class S.inputIcon ]
|
||||
[ i
|
||||
[ class "fa"
|
||||
, if model.showPass2 then
|
||||
class "fa-lock-open"
|
||||
|
||||
else
|
||||
class "fa-lock"
|
||||
]
|
||||
[]
|
||||
]
|
||||
, input
|
||||
[ type_ <|
|
||||
if model.showPass2 then
|
||||
"text"
|
||||
|
||||
else
|
||||
"password"
|
||||
, name "passw2"
|
||||
, autocomplete False
|
||||
, onInput SetPass2
|
||||
, value model.pass2
|
||||
, class ("pl-10 pr-10 py-2 rounded-lg" ++ S.textInput)
|
||||
, placeholder "Password (repeat)"
|
||||
]
|
||||
[]
|
||||
, a
|
||||
[ class S.inputLeftIconLink
|
||||
, onClick ToggleShowPass2
|
||||
, href "#"
|
||||
]
|
||||
[ i [ class "fa fa-eye" ] []
|
||||
]
|
||||
]
|
||||
]
|
||||
, div
|
||||
[ class "flex flex-col my-3"
|
||||
, classList [ ( "hidden", flags.config.signupMode /= "invite" ) ]
|
||||
]
|
||||
[ label
|
||||
[ for "invitekey"
|
||||
, class S.inputLabel
|
||||
]
|
||||
[ text "Invitation Key"
|
||||
, B.inputRequired
|
||||
]
|
||||
, div [ class "relative" ]
|
||||
[ div [ class S.inputIcon ]
|
||||
[ i [ class "fa fa-key" ] []
|
||||
]
|
||||
, input
|
||||
[ type_ "text"
|
||||
, name "invitekey"
|
||||
, autocomplete False
|
||||
, onInput SetInvite
|
||||
, model.invite |> Maybe.withDefault "" |> value
|
||||
, class ("pl-10 pr-4 py-2 rounded-lg" ++ S.textInput)
|
||||
, placeholder "Invitation Key"
|
||||
]
|
||||
[]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-3" ]
|
||||
[ button
|
||||
[ type_ "submit"
|
||||
, class S.primaryButton
|
||||
]
|
||||
[ text "Submit"
|
||||
]
|
||||
]
|
||||
, resultMessage model
|
||||
, div
|
||||
[ class "flex justify-end text-sm pt-4"
|
||||
, classList [ ( "hidden", flags.config.signupMode == "closed" ) ]
|
||||
]
|
||||
[ span []
|
||||
[ text "Already signed up?"
|
||||
]
|
||||
, a
|
||||
[ Page.href (LoginPage Nothing)
|
||||
, class ("ml-2" ++ S.link)
|
||||
]
|
||||
[ i [ class "fa fa-user-plus mr-1" ] []
|
||||
, text "Sign in"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
resultMessage : Model -> Html Msg
|
||||
resultMessage model =
|
||||
case model.result of
|
||||
Just r ->
|
||||
if r.success then
|
||||
div [ class S.successMessage ]
|
||||
[ text "Registration successful."
|
||||
]
|
||||
|
||||
else
|
||||
div [ class S.errorMessage ]
|
||||
[ text r.message
|
||||
]
|
||||
|
||||
Nothing ->
|
||||
if List.isEmpty model.errorMsg then
|
||||
span [ class "hidden" ] []
|
||||
|
||||
else
|
||||
div [ class S.errorMessage ]
|
||||
(List.map (\s -> div [] [ text s ]) model.errorMsg)
|
@ -38,22 +38,6 @@ type alias Model =
|
||||
}
|
||||
|
||||
|
||||
dropzoneSettings : Comp.Dropzone.Settings
|
||||
dropzoneSettings =
|
||||
let
|
||||
ds =
|
||||
Comp.Dropzone.defaultSettings
|
||||
in
|
||||
{ ds
|
||||
| classList =
|
||||
\m ->
|
||||
[ ( "ui attached blue placeholder segment dropzone", True )
|
||||
, ( "dragging", m.hover )
|
||||
, ( "disabled", not m.active )
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
mkLanguageItem : Language -> Comp.FixedDropdown.Item Language
|
||||
mkLanguageItem lang =
|
||||
Comp.FixedDropdown.Item lang (Data.Language.toName lang)
|
||||
@ -67,7 +51,7 @@ emptyModel =
|
||||
, completed = Set.empty
|
||||
, errored = Set.empty
|
||||
, loading = Dict.empty
|
||||
, dropzone = Comp.Dropzone.init dropzoneSettings
|
||||
, dropzone = Comp.Dropzone.init []
|
||||
, skipDuplicates = True
|
||||
, languageModel =
|
||||
Comp.FixedDropdown.init
|
||||
|
@ -23,7 +23,7 @@ view mid model =
|
||||
[ div [ class "ui top attached segment" ]
|
||||
[ renderForm model
|
||||
]
|
||||
, Html.map DropzoneMsg (Comp.Dropzone.view model.dropzone)
|
||||
, Html.map DropzoneMsg (Comp.Dropzone.view dropzoneSettings model.dropzone)
|
||||
, div [ class "ui bottom attached segment" ]
|
||||
[ a [ class "ui primary button", href "#", onClick SubmitUpload ]
|
||||
[ text "Submit"
|
||||
@ -50,6 +50,22 @@ view mid model =
|
||||
]
|
||||
|
||||
|
||||
dropzoneSettings : Comp.Dropzone.Settings
|
||||
dropzoneSettings =
|
||||
let
|
||||
ds =
|
||||
Comp.Dropzone.defaultSettings
|
||||
in
|
||||
{ ds
|
||||
| classList =
|
||||
\m ->
|
||||
[ ( "ui attached blue placeholder segment dropzone", True )
|
||||
, ( "dragging", m.hover )
|
||||
, ( "disabled", not m.active )
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
renderErrorMsg : Model -> Html Msg
|
||||
renderErrorMsg _ =
|
||||
div [ class "row" ]
|
||||
|
280
modules/webapp/src/main/elm/Page/Upload/View2.elm
Normal file
280
modules/webapp/src/main/elm/Page/Upload/View2.elm
Normal file
@ -0,0 +1,280 @@
|
||||
module Page.Upload.View2 exposing (viewContent, viewSidebar)
|
||||
|
||||
import Comp.Dropzone
|
||||
import Comp.FixedDropdown
|
||||
import Comp.Progress
|
||||
import Data.DropdownStyle as DS
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Dict
|
||||
import File exposing (File)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onCheck, onClick)
|
||||
import Page exposing (Page(..))
|
||||
import Page.Upload.Data exposing (..)
|
||||
import Styles as S
|
||||
import Util.File exposing (makeFileId)
|
||||
import Util.Maybe
|
||||
import Util.Size
|
||||
|
||||
|
||||
viewSidebar : Maybe String -> Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewSidebar _ _ _ _ _ =
|
||||
div
|
||||
[ id "sidebar"
|
||||
, class "hidden"
|
||||
]
|
||||
[]
|
||||
|
||||
|
||||
viewContent : Maybe String -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewContent mid _ _ model =
|
||||
div
|
||||
[ id "content"
|
||||
, class S.content
|
||||
]
|
||||
[ div [ class "px-0 flex flex-col" ]
|
||||
[ div [ class "py-4" ]
|
||||
[ renderForm model
|
||||
]
|
||||
, div [ class "py-0" ]
|
||||
[ Html.map DropzoneMsg
|
||||
(Comp.Dropzone.view2 model.dropzone)
|
||||
]
|
||||
, div [ class "py-4" ]
|
||||
[ a
|
||||
[ class S.primaryButton
|
||||
, href "#"
|
||||
, onClick SubmitUpload
|
||||
]
|
||||
[ text "Submit"
|
||||
]
|
||||
, a
|
||||
[ class S.secondaryButton
|
||||
, class "ml-2"
|
||||
, href "#"
|
||||
, onClick Clear
|
||||
]
|
||||
[ text "Reset"
|
||||
]
|
||||
]
|
||||
]
|
||||
, renderErrorMsg model
|
||||
, renderSuccessMsg (Util.Maybe.nonEmpty mid) model
|
||||
, renderUploads model
|
||||
]
|
||||
|
||||
|
||||
renderForm : Model -> Html Msg
|
||||
renderForm model =
|
||||
div [ class "row" ]
|
||||
[ Html.form [ action "#" ]
|
||||
[ div [ class "flex flex-col mb-3" ]
|
||||
[ label [ class "inline-flex items-center" ]
|
||||
[ input
|
||||
[ type_ "radio"
|
||||
, checked model.incoming
|
||||
, onCheck (\_ -> ToggleIncoming)
|
||||
, class S.radioInput
|
||||
]
|
||||
[]
|
||||
, span [ class "ml-2" ] [ text "Incoming" ]
|
||||
]
|
||||
, label [ class "inline-flex items-center" ]
|
||||
[ input
|
||||
[ type_ "radio"
|
||||
, checked (not model.incoming)
|
||||
, onCheck (\_ -> ToggleIncoming)
|
||||
, class S.radioInput
|
||||
]
|
||||
[]
|
||||
, span [ class "ml-2" ] [ text "Outgoing" ]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col mb-3" ]
|
||||
[ label [ class "inline-flex items-center" ]
|
||||
[ input
|
||||
[ type_ "checkbox"
|
||||
, checked model.singleItem
|
||||
, onCheck (\_ -> ToggleSingleItem)
|
||||
, class S.checkboxInput
|
||||
]
|
||||
[]
|
||||
, span [ class "ml-2" ]
|
||||
[ text "All files are one single item"
|
||||
]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col mb-3" ]
|
||||
[ label [ class "inline-flex items-center" ]
|
||||
[ input
|
||||
[ type_ "checkbox"
|
||||
, checked model.skipDuplicates
|
||||
, onCheck (\_ -> ToggleSkipDuplicates)
|
||||
, class S.checkboxInput
|
||||
]
|
||||
[]
|
||||
, span [ class "ml-2" ]
|
||||
[ text "Skip files already present in docspell"
|
||||
]
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col mb-3" ]
|
||||
[ label [ class "inline-flex items-center mb-2" ]
|
||||
[ span [ class "mr-2" ] [ text "Language:" ]
|
||||
, Html.map LanguageMsg
|
||||
(Comp.FixedDropdown.viewStyled2
|
||||
(DS.mainStyleWith "w-40")
|
||||
False
|
||||
(Maybe.map mkLanguageItem model.language)
|
||||
model.languageModel
|
||||
)
|
||||
]
|
||||
, div [ class "text-gray-400 text-xs" ]
|
||||
[ text "Used for text extraction and analysis. The collective's "
|
||||
, text "default language is used if not specified here."
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
renderErrorMsg : Model -> Html Msg
|
||||
renderErrorMsg model =
|
||||
div
|
||||
[ class "row"
|
||||
, classList [ ( "hidden", not (isDone model && hasErrors model) ) ]
|
||||
]
|
||||
[ div [ class "mt-4" ]
|
||||
[ div [ class S.errorMessage ]
|
||||
[ text "There were errors uploading some files."
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
renderSuccessMsg : Bool -> Model -> Html Msg
|
||||
renderSuccessMsg public model =
|
||||
div
|
||||
[ class "row"
|
||||
, classList [ ( "hidden", List.isEmpty model.files || not (isSuccessAll model) ) ]
|
||||
]
|
||||
[ div [ class "mt-4" ]
|
||||
[ div [ class S.successMessage ]
|
||||
[ h3 [ class S.header2, class "text-green-800 dark:text-lime-800" ]
|
||||
[ i [ class "fa fa-smile font-thin" ] []
|
||||
, span [ class "ml-2" ]
|
||||
[ text "All files uploaded"
|
||||
]
|
||||
]
|
||||
, p
|
||||
[ classList [ ( "hidden", public ) ]
|
||||
]
|
||||
[ text "Your files have been successfully uploaded. "
|
||||
, text "They are now being processed. Check the "
|
||||
, a
|
||||
[ class S.successMessageLink
|
||||
, Page.href HomePage
|
||||
]
|
||||
[ text "Items page"
|
||||
]
|
||||
, text " later where the files will arrive eventually. Or go to the "
|
||||
, a
|
||||
[ class S.successMessageLink
|
||||
, Page.href QueuePage
|
||||
]
|
||||
[ text "Processing Page"
|
||||
]
|
||||
, text " to view the current processing state."
|
||||
]
|
||||
, p []
|
||||
[ text "Click "
|
||||
, a
|
||||
[ class S.successMessageLink
|
||||
, href "#"
|
||||
, onClick Clear
|
||||
]
|
||||
[ text "Reset"
|
||||
]
|
||||
, text " to upload more files."
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
renderUploads : Model -> Html Msg
|
||||
renderUploads model =
|
||||
div
|
||||
[ class "mt-4"
|
||||
, classList [ ( "hidden", List.isEmpty model.files || isSuccessAll model ) ]
|
||||
]
|
||||
[ div [ class "sixteen wide column" ]
|
||||
[ div [ class "ui basic segment" ]
|
||||
[ h2 [ class S.header2 ]
|
||||
[ text "Selected Files"
|
||||
]
|
||||
, div [ class "ui items" ] <|
|
||||
if model.singleItem then
|
||||
List.map (renderFileItem model (Just uploadAllTracker)) model.files
|
||||
|
||||
else
|
||||
List.map (renderFileItem model Nothing) model.files
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
getProgress : Model -> File -> Int
|
||||
getProgress model file =
|
||||
let
|
||||
key =
|
||||
if model.singleItem then
|
||||
uploadAllTracker
|
||||
|
||||
else
|
||||
makeFileId file
|
||||
in
|
||||
Dict.get key model.loading
|
||||
|> Maybe.withDefault 0
|
||||
|
||||
|
||||
renderFileItem : Model -> Maybe String -> File -> Html Msg
|
||||
renderFileItem model _ file =
|
||||
let
|
||||
name =
|
||||
File.name file
|
||||
|
||||
size =
|
||||
File.size file
|
||||
|> toFloat
|
||||
|> Util.Size.bytesReadable Util.Size.B
|
||||
in
|
||||
div [ class "flex flex-col w-full mb-4" ]
|
||||
[ div [ class "flex flex-row items-center" ]
|
||||
[ div [ class "inline-flex items-center" ]
|
||||
[ i
|
||||
[ classList
|
||||
[ ( "mr-2 text-lg", True )
|
||||
, ( "fa fa-file font-thin", isIdle model file )
|
||||
, ( "fa fa-spinner animate-spin ", isLoading model file )
|
||||
, ( "fa fa-check ", isCompleted model file )
|
||||
, ( "fa fa-bolt", isError model file )
|
||||
]
|
||||
]
|
||||
[]
|
||||
, div [ class "middle aligned content" ]
|
||||
[ div [ class "header" ]
|
||||
[ text name
|
||||
]
|
||||
]
|
||||
]
|
||||
, div [ class "flex-grow inline-flex justify-end" ]
|
||||
[ text size
|
||||
]
|
||||
]
|
||||
, div [ class "h-4" ]
|
||||
[ Comp.Progress.progress2 (getProgress model file)
|
||||
]
|
||||
]
|
@ -32,7 +32,7 @@ init flags settings =
|
||||
( um, uc ) =
|
||||
Comp.UiSettingsManage.init flags settings
|
||||
in
|
||||
( { currentTab = Nothing
|
||||
( { currentTab = Just UiSettingsTab
|
||||
, changePassModel = Comp.ChangePasswordForm.emptyModel
|
||||
, emailSettingsModel = Comp.EmailSettingsManage.emptyModel
|
||||
, imapSettingsModel = Comp.ImapSettingsManage.emptyModel
|
||||
|
282
modules/webapp/src/main/elm/Page/UserSettings/View2.elm
Normal file
282
modules/webapp/src/main/elm/Page/UserSettings/View2.elm
Normal file
@ -0,0 +1,282 @@
|
||||
module Page.UserSettings.View2 exposing (viewContent, viewSidebar)
|
||||
|
||||
import Comp.ChangePasswordForm
|
||||
import Comp.EmailSettingsManage
|
||||
import Comp.ImapSettingsManage
|
||||
import Comp.NotificationManage
|
||||
import Comp.ScanMailboxManage
|
||||
import Comp.UiSettingsManage
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
import Page.UserSettings.Data exposing (..)
|
||||
import Styles as S
|
||||
|
||||
|
||||
viewSidebar : Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||
viewSidebar visible _ _ model =
|
||||
div
|
||||
[ id "sidebar"
|
||||
, class S.sidebar
|
||||
, class S.sidebarBg
|
||||
, classList [ ( "hidden", not visible ) ]
|
||||
]
|
||||
[ div [ class "" ]
|
||||
[ h1 [ class S.header1 ]
|
||||
[ text "User Settings"
|
||||
]
|
||||
]
|
||||
, div [ class "flex flex-col my-2" ]
|
||||
[ a
|
||||
[ href "#"
|
||||
, onClick (SetTab UiSettingsTab)
|
||||
, menuEntryActive model UiSettingsTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ i [ class "fa fa-cog" ] []
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "UI Settings" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab NotificationTab)
|
||||
, menuEntryActive model NotificationTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ i [ class "fa fa-bullhorn" ] []
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Notifications" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab ScanMailboxTab)
|
||||
, menuEntryActive model ScanMailboxTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ i [ class "fa fa-envelope-open font-thin" ] []
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "Scan Mailbox" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab EmailSettingsTab)
|
||||
, class S.sidebarLink
|
||||
, menuEntryActive model EmailSettingsTab
|
||||
]
|
||||
[ i [ class "fa fa-envelope" ] []
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "E-Mail Settings (SMTP)" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab ImapSettingsTab)
|
||||
, menuEntryActive model ImapSettingsTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ i [ class "fa fa-envelope" ] []
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "E-Mail Settings (IMAP)" ]
|
||||
]
|
||||
, a
|
||||
[ href "#"
|
||||
, onClick (SetTab ChangePassTab)
|
||||
, menuEntryActive model ChangePassTab
|
||||
, class S.sidebarLink
|
||||
]
|
||||
[ i [ class "fa fa-user-secret" ] []
|
||||
, span
|
||||
[ class "ml-3" ]
|
||||
[ text "CHange Password" ]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
viewContent : Flags -> UiSettings -> Model -> Html Msg
|
||||
viewContent flags settings model =
|
||||
div
|
||||
[ id "content"
|
||||
, class S.content
|
||||
]
|
||||
(case model.currentTab of
|
||||
Just ChangePassTab ->
|
||||
viewChangePassword model
|
||||
|
||||
Just EmailSettingsTab ->
|
||||
viewEmailSettings settings model
|
||||
|
||||
Just NotificationTab ->
|
||||
viewNotificationManage settings model
|
||||
|
||||
Just ImapSettingsTab ->
|
||||
viewImapSettings settings model
|
||||
|
||||
Just ScanMailboxTab ->
|
||||
viewScanMailboxManage settings model
|
||||
|
||||
Just UiSettingsTab ->
|
||||
viewUiSettings flags settings model
|
||||
|
||||
Nothing ->
|
||||
[]
|
||||
)
|
||||
|
||||
|
||||
|
||||
--- Helper
|
||||
|
||||
|
||||
menuEntryActive : Model -> Tab -> Attribute msg
|
||||
menuEntryActive model tab =
|
||||
if model.currentTab == Just tab then
|
||||
class S.sidebarMenuItemActive
|
||||
|
||||
else
|
||||
class ""
|
||||
|
||||
|
||||
viewChangePassword : Model -> List (Html Msg)
|
||||
viewChangePassword model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ i [ class "fa fa-user-secret" ] []
|
||||
, div [ class "ml-3" ]
|
||||
[ text "Change Password"
|
||||
]
|
||||
]
|
||||
, Html.map ChangePassMsg (Comp.ChangePasswordForm.view2 model.changePassModel)
|
||||
]
|
||||
|
||||
|
||||
viewUiSettings : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
viewUiSettings flags settings model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ i [ class "fa fa-cog" ] []
|
||||
, span [ class "ml-3" ]
|
||||
[ text "UI Settings"
|
||||
]
|
||||
]
|
||||
, p [ class "opacity-75 text-lg mb-4" ]
|
||||
[ text "These settings only affect the web ui. They are stored in the browser, "
|
||||
, text "so they are separated between browsers and devices."
|
||||
]
|
||||
, Html.map UiSettingsMsg
|
||||
(Comp.UiSettingsManage.view2
|
||||
flags
|
||||
settings
|
||||
""
|
||||
model.uiSettingsModel
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
viewEmailSettings : UiSettings -> Model -> List (Html Msg)
|
||||
viewEmailSettings settings model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ i [ class "fa fa-envelope" ] []
|
||||
, div [ class "ml-3" ]
|
||||
[ text "E-Mail Settings (Smtp)"
|
||||
]
|
||||
]
|
||||
, Html.map EmailSettingsMsg
|
||||
(Comp.EmailSettingsManage.view2
|
||||
settings
|
||||
model.emailSettingsModel
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
viewImapSettings : UiSettings -> Model -> List (Html Msg)
|
||||
viewImapSettings settings model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ i [ class "fa fa-envelope" ] []
|
||||
, div [ class "ml-3" ]
|
||||
[ text "E-Mail Settings (Imap)"
|
||||
]
|
||||
]
|
||||
, Html.map ImapSettingsMsg
|
||||
(Comp.ImapSettingsManage.view2
|
||||
settings
|
||||
model.imapSettingsModel
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
viewNotificationManage : UiSettings -> Model -> List (Html Msg)
|
||||
viewNotificationManage settings model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ i [ class "fa fa-bullhorn" ] []
|
||||
, div [ class "ml-3" ]
|
||||
[ text "Notification"
|
||||
]
|
||||
]
|
||||
, p [ class "opacity-80 text-lg mb-3" ]
|
||||
[ text """
|
||||
Docspell can notify you once the due dates of your items
|
||||
come closer. Notification is done via e-mail. You need to
|
||||
provide a connection in your e-mail settings."""
|
||||
]
|
||||
, p [ class "opacity-80 text-lg mb-3" ]
|
||||
[ text "Docspell finds all items that are due in "
|
||||
, em [ class "font-italic" ] [ text "Remind Days" ]
|
||||
, text " days and sends this list via e-mail."
|
||||
]
|
||||
, Html.map NotificationMsg
|
||||
(Comp.NotificationManage.view2 settings model.notificationModel)
|
||||
]
|
||||
|
||||
|
||||
viewScanMailboxManage : UiSettings -> Model -> List (Html Msg)
|
||||
viewScanMailboxManage settings model =
|
||||
[ h2
|
||||
[ class S.header1
|
||||
, class "inline-flex items-center"
|
||||
]
|
||||
[ i [ class "fa fa-envelope-open font-thin" ] []
|
||||
, div [ class "ml-3" ]
|
||||
[ text "Scan Mailbox"
|
||||
]
|
||||
]
|
||||
, p [ class "opacity-80 text-lg mb-3" ]
|
||||
[ text "Docspell can scan folders of your mailbox to import your mails. "
|
||||
, text "You need to provide a connection in "
|
||||
, text "your e-mail (imap) settings."
|
||||
]
|
||||
, p [ class "opacity-80 text-lg mb-3 hidden" ]
|
||||
[ text """
|
||||
Docspell goes through all configured folders and imports
|
||||
mails matching the search criteria. Mails are skipped if
|
||||
they were imported in a previous run and the corresponding
|
||||
items still exist. After submitting a mail into docspell,
|
||||
you can choose to move it to another folder, to delete it
|
||||
or to just leave it there. In the latter case you should
|
||||
adjust the schedule to avoid reading over the same mails
|
||||
again."""
|
||||
]
|
||||
, Html.map ScanMailboxMsg
|
||||
(Comp.ScanMailboxManage.view2
|
||||
settings
|
||||
model.scanMailboxModel
|
||||
)
|
||||
]
|
Reference in New Issue
Block a user