mirror of
				https://github.com/TheAnachronism/docspell.git
				synced 2025-10-31 09:30:12 +00:00 
			
		
		
		
	Basic ui for addons
This commit is contained in:
		| @@ -18,6 +18,14 @@ module Api exposing | ||||
|     , addShare | ||||
|     , addTag | ||||
|     , addTagsMultiple | ||||
|     , addonRunConfigDelete | ||||
|     , addonRunConfigGet | ||||
|     , addonRunConfigSet | ||||
|     , addonRunExistingItem | ||||
|     , addonsDelete | ||||
|     , addonsGetAll | ||||
|     , addonsInstall | ||||
|     , addonsUpdate | ||||
|     , attachmentPreviewURL | ||||
|     , bookmarkNameExists | ||||
|     , cancelJob | ||||
| @@ -211,6 +219,11 @@ module Api exposing | ||||
|     , versionInfo | ||||
|     ) | ||||
|  | ||||
| import Api.Model.AddonList exposing (AddonList) | ||||
| import Api.Model.AddonRegister exposing (AddonRegister) | ||||
| import Api.Model.AddonRunConfig exposing (AddonRunConfig) | ||||
| import Api.Model.AddonRunConfigList exposing (AddonRunConfigList) | ||||
| import Api.Model.AddonRunExistingItem exposing (AddonRunExistingItem) | ||||
| import Api.Model.AttachmentMeta exposing (AttachmentMeta) | ||||
| import Api.Model.AuthResult exposing (AuthResult) | ||||
| import Api.Model.BasicResult exposing (BasicResult) | ||||
| @@ -3156,6 +3169,99 @@ shareDownloadAllLink flags id = | ||||
|  | ||||
|  | ||||
|  | ||||
| --- Addons | ||||
|  | ||||
|  | ||||
| addonsGetAll : Flags -> (Result Http.Error AddonList -> msg) -> Cmd msg | ||||
| addonsGetAll flags receive = | ||||
|     Http2.authGet | ||||
|         { url = flags.config.baseUrl ++ "/api/v1/sec/addon/archive" | ||||
|         , account = getAccount flags | ||||
|         , expect = Http.expectJson receive Api.Model.AddonList.decoder | ||||
|         } | ||||
|  | ||||
|  | ||||
| addonsDelete : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg | ||||
| addonsDelete flags addonId receive = | ||||
|     Http2.authDelete | ||||
|         { url = flags.config.baseUrl ++ "/api/v1/sec/addon/archive/" ++ addonId | ||||
|         , account = getAccount flags | ||||
|         , expect = Http.expectJson receive Api.Model.BasicResult.decoder | ||||
|         } | ||||
|  | ||||
|  | ||||
| addonsInstall : Flags -> AddonRegister -> (Result Http.Error BasicResult -> msg) -> Cmd msg | ||||
| addonsInstall flags addon receive = | ||||
|     Http2.authPost | ||||
|         { url = flags.config.baseUrl ++ "/api/v1/sec/addon/archive" | ||||
|         , account = getAccount flags | ||||
|         , body = Http.jsonBody (Api.Model.AddonRegister.encode addon) | ||||
|         , expect = Http.expectJson receive Api.Model.BasicResult.decoder | ||||
|         } | ||||
|  | ||||
|  | ||||
| addonsUpdate : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg | ||||
| addonsUpdate flags addonId receive = | ||||
|     Http2.authPut | ||||
|         { url = flags.config.baseUrl ++ "/api/v1/sec/addon/archive/" ++ addonId | ||||
|         , account = getAccount flags | ||||
|         , body = Http.emptyBody | ||||
|         , expect = Http.expectJson receive Api.Model.BasicResult.decoder | ||||
|         } | ||||
|  | ||||
|  | ||||
| addonRunConfigGet : Flags -> (Result Http.Error AddonRunConfigList -> msg) -> Cmd msg | ||||
| addonRunConfigGet flags receive = | ||||
|     Http2.authGet | ||||
|         { url = flags.config.baseUrl ++ "/api/v1/sec/addon/run-config" | ||||
|         , account = getAccount flags | ||||
|         , expect = Http.expectJson receive Api.Model.AddonRunConfigList.decoder | ||||
|         } | ||||
|  | ||||
|  | ||||
| addonRunConfigSet : | ||||
|     Flags | ||||
|     -> AddonRunConfig | ||||
|     -> (Result Http.Error BasicResult -> msg) | ||||
|     -> Cmd msg | ||||
| addonRunConfigSet flags cfg receive = | ||||
|     if cfg.id == "" then | ||||
|         Http2.authPost | ||||
|             { url = flags.config.baseUrl ++ "/api/v1/sec/addon/run-config" | ||||
|             , account = getAccount flags | ||||
|             , body = Http.jsonBody (Api.Model.AddonRunConfig.encode cfg) | ||||
|             , expect = Http.expectJson receive Api.Model.BasicResult.decoder | ||||
|             } | ||||
|  | ||||
|     else | ||||
|         Http2.authPut | ||||
|             { url = flags.config.baseUrl ++ "/api/v1/sec/addon/run-config/" ++ cfg.id | ||||
|             , account = getAccount flags | ||||
|             , body = Http.jsonBody (Api.Model.AddonRunConfig.encode cfg) | ||||
|             , expect = Http.expectJson receive Api.Model.BasicResult.decoder | ||||
|             } | ||||
|  | ||||
|  | ||||
| addonRunConfigDelete : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg | ||||
| addonRunConfigDelete flags id receive = | ||||
|     Http2.authDelete | ||||
|         { url = flags.config.baseUrl ++ "/api/v1/sec/addon/run-config/" ++ id | ||||
|         , account = getAccount flags | ||||
|         , expect = Http.expectJson receive Api.Model.BasicResult.decoder | ||||
|         } | ||||
|  | ||||
|  | ||||
| addonRunExistingItem : Flags -> AddonRunExistingItem -> (Result Http.Error BasicResult -> msg) -> Cmd msg | ||||
| addonRunExistingItem flags input receive = | ||||
|     Http2.authPost | ||||
|         { url = flags.config.baseUrl ++ "/api/v1/sec/addon/run/existingitem" | ||||
|         , account = getAccount flags | ||||
|         , body = Http.jsonBody (Api.Model.AddonRunExistingItem.encode input) | ||||
|         , expect = Http.expectJson receive Api.Model.BasicResult.decoder | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
| --- Helper | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import Api | ||||
| import App.Data exposing (..) | ||||
| import Browser exposing (UrlRequest(..)) | ||||
| import Browser.Navigation as Nav | ||||
| import Comp.AddonArchiveManage | ||||
| import Comp.DownloadAll | ||||
| import Data.AppEvent exposing (AppEvent(..)) | ||||
| import Data.Environment as Env | ||||
| @@ -345,6 +346,9 @@ updateWithSub msg model = | ||||
|                 Ok (JobsWaiting n) -> | ||||
|                     ( { model | jobsWaiting = max 0 n }, Cmd.none, Sub.none ) | ||||
|  | ||||
|                 Ok (AddonInstalled info) -> | ||||
|                     updateManageData (Page.ManageData.Data.AddonArchiveMsg <| Comp.AddonArchiveManage.addonInstallResult info) model | ||||
|  | ||||
|                 Err _ -> | ||||
|                     ( model, Cmd.none, Sub.none ) | ||||
|  | ||||
| @@ -640,7 +644,7 @@ updateManageData : Page.ManageData.Data.Msg -> Model -> ( Model, Cmd Msg, Sub Ms | ||||
| updateManageData lmsg model = | ||||
|     let | ||||
|         ( lm, lc, ls ) = | ||||
|             Page.ManageData.Update.update model.flags lmsg model.manageDataModel | ||||
|             Page.ManageData.Update.update model.flags model.uiSettings lmsg model.manageDataModel | ||||
|     in | ||||
|     ( { model | manageDataModel = lm } | ||||
|     , Cmd.map ManageDataMsg lc | ||||
|   | ||||
							
								
								
									
										106
									
								
								modules/webapp/src/main/elm/Comp/AddonArchiveForm.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								modules/webapp/src/main/elm/Comp/AddonArchiveForm.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Comp.AddonArchiveForm exposing (Model, Msg, get, init, initWith, update, view) | ||||
|  | ||||
| import Api.Model.Addon exposing (Addon) | ||||
| import Comp.Basic as B | ||||
| import Data.Flags exposing (Flags) | ||||
| import Html exposing (..) | ||||
| import Html.Attributes exposing (..) | ||||
| import Html.Events exposing (onInput) | ||||
| import Messages.Comp.AddonArchiveForm exposing (Texts) | ||||
| import Styles as S | ||||
| import Util.Maybe | ||||
|  | ||||
|  | ||||
| type alias Model = | ||||
|     { addon : Addon | ||||
|     , url : Maybe String | ||||
|     } | ||||
|  | ||||
|  | ||||
| init : ( Model, Cmd Msg ) | ||||
| init = | ||||
|     ( { addon = Api.Model.Addon.empty | ||||
|       , url = Nothing | ||||
|       } | ||||
|     , Cmd.none | ||||
|     ) | ||||
|  | ||||
|  | ||||
| initWith : Addon -> ( Model, Cmd Msg ) | ||||
| initWith a = | ||||
|     ( { addon = a | ||||
|       , url = a.url | ||||
|       } | ||||
|     , Cmd.none | ||||
|     ) | ||||
|  | ||||
|  | ||||
| isValid : Model -> Bool | ||||
| isValid model = | ||||
|     model.url /= Nothing | ||||
|  | ||||
|  | ||||
| get : Model -> Maybe Addon | ||||
| get model = | ||||
|     let | ||||
|         a = | ||||
|             model.addon | ||||
|     in | ||||
|     if isValid model then | ||||
|         Just | ||||
|             { a | ||||
|                 | url = model.url | ||||
|             } | ||||
|  | ||||
|     else | ||||
|         Nothing | ||||
|  | ||||
|  | ||||
| type Msg | ||||
|     = SetUrl String | ||||
|  | ||||
|  | ||||
| update : Flags -> Msg -> Model -> ( Model, Cmd Msg ) | ||||
| update _ msg model = | ||||
|     case msg of | ||||
|         SetUrl url -> | ||||
|             ( { model | url = Util.Maybe.fromString url }, Cmd.none ) | ||||
|  | ||||
|  | ||||
|  | ||||
| --- View | ||||
|  | ||||
|  | ||||
| view : Texts -> Model -> Html Msg | ||||
| view texts model = | ||||
|     div | ||||
|         [ class "flex flex-col" ] | ||||
|         [ div [ class "mb-4" ] | ||||
|             [ label | ||||
|                 [ class S.inputLabel | ||||
|                 ] | ||||
|                 [ text texts.addonUrl | ||||
|                 , B.inputRequired | ||||
|                 ] | ||||
|             , input | ||||
|                 [ type_ "text" | ||||
|                 , placeholder texts.addonUrlPlaceholder | ||||
|                 , class S.textInput | ||||
|                 , classList [ ( "disabled", model.addon.id /= "" ) ] | ||||
|                 , value (model.url |> Maybe.withDefault "") | ||||
|                 , onInput SetUrl | ||||
|                 , disabled (model.addon.id /= "") | ||||
|                 ] | ||||
|                 [] | ||||
|             , span [ class "text-sm opacity-75" ] | ||||
|                 [ text texts.installInfoText | ||||
|                 ] | ||||
|             ] | ||||
|         ] | ||||
							
								
								
									
										429
									
								
								modules/webapp/src/main/elm/Comp/AddonArchiveManage.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										429
									
								
								modules/webapp/src/main/elm/Comp/AddonArchiveManage.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,429 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Comp.AddonArchiveManage exposing (Model, Msg, addonInstallResult, init, loadAddons, update, view) | ||||
|  | ||||
| import Api | ||||
| import Api.Model.Addon exposing (Addon) | ||||
| import Api.Model.AddonList exposing (AddonList) | ||||
| import Api.Model.AddonRegister exposing (AddonRegister) | ||||
| import Api.Model.BasicResult exposing (BasicResult) | ||||
| import Comp.AddonArchiveForm | ||||
| import Comp.AddonArchiveTable | ||||
| import Comp.Basic as B | ||||
| import Comp.ItemDetail.Model exposing (Msg(..)) | ||||
| import Comp.MenuBar as MB | ||||
| import Data.Flags exposing (Flags) | ||||
| import Data.ServerEvent exposing (AddonInfo) | ||||
| import Data.UiSettings exposing (UiSettings) | ||||
| import Html exposing (..) | ||||
| import Html.Attributes exposing (..) | ||||
| import Html.Events exposing (onClick) | ||||
| import Http | ||||
| import Markdown | ||||
| import Messages.Comp.AddonArchiveManage exposing (Texts) | ||||
| import Page exposing (Page(..)) | ||||
| import Styles as S | ||||
|  | ||||
|  | ||||
| type FormError | ||||
|     = FormErrorNone | ||||
|     | FormErrorHttp Http.Error | ||||
|     | FormErrorInvalid | ||||
|     | FormErrorSubmit String | ||||
|  | ||||
|  | ||||
| type ViewMode | ||||
|     = Table | ||||
|     | Form | ||||
|  | ||||
|  | ||||
| type DeleteConfirm | ||||
|     = DeleteConfirmOff | ||||
|     | DeleteConfirmOn | ||||
|  | ||||
|  | ||||
| type alias Model = | ||||
|     { viewMode : ViewMode | ||||
|     , addons : List Addon | ||||
|     , formModel : Comp.AddonArchiveForm.Model | ||||
|     , loading : Bool | ||||
|     , formError : FormError | ||||
|     , deleteConfirm : DeleteConfirm | ||||
|     } | ||||
|  | ||||
|  | ||||
| init : Flags -> ( Model, Cmd Msg ) | ||||
| init flags = | ||||
|     let | ||||
|         ( fm, fc ) = | ||||
|             Comp.AddonArchiveForm.init | ||||
|     in | ||||
|     ( { viewMode = Table | ||||
|       , addons = [] | ||||
|       , formModel = fm | ||||
|       , loading = False | ||||
|       , formError = FormErrorNone | ||||
|       , deleteConfirm = DeleteConfirmOff | ||||
|       } | ||||
|     , Cmd.batch | ||||
|         [ Cmd.map FormMsg fc | ||||
|         , Api.addonsGetAll flags LoadAddonsResp | ||||
|         ] | ||||
|     ) | ||||
|  | ||||
|  | ||||
| type Msg | ||||
|     = LoadAddons | ||||
|     | TableMsg Comp.AddonArchiveTable.Msg | ||||
|     | FormMsg Comp.AddonArchiveForm.Msg | ||||
|     | InitNewAddon | ||||
|     | SetViewMode ViewMode | ||||
|     | Submit | ||||
|     | RequestDelete | ||||
|     | CancelDelete | ||||
|     | DeleteAddonNow String | ||||
|     | LoadAddonsResp (Result Http.Error AddonList) | ||||
|     | AddAddonResp (Result Http.Error BasicResult) | ||||
|     | UpdateAddonResp (Result Http.Error BasicResult) | ||||
|     | DeleteAddonResp (Result Http.Error BasicResult) | ||||
|     | AddonInstallResp AddonInfo | ||||
|  | ||||
|  | ||||
| loadAddons : Msg | ||||
| loadAddons = | ||||
|     LoadAddons | ||||
|  | ||||
|  | ||||
| addonInstallResult : AddonInfo -> Msg | ||||
| addonInstallResult info = | ||||
|     AddonInstallResp info | ||||
|  | ||||
|  | ||||
|  | ||||
| --- update | ||||
|  | ||||
|  | ||||
| update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) | ||||
| update flags msg model = | ||||
|     case msg of | ||||
|         InitNewAddon -> | ||||
|             let | ||||
|                 ( bm, bc ) = | ||||
|                     Comp.AddonArchiveForm.init | ||||
|  | ||||
|                 nm = | ||||
|                     { model | ||||
|                         | viewMode = Form | ||||
|                         , formError = FormErrorNone | ||||
|                         , formModel = bm | ||||
|                     } | ||||
|             in | ||||
|             ( nm, Cmd.map FormMsg bc, Sub.none ) | ||||
|  | ||||
|         SetViewMode vm -> | ||||
|             ( { model | viewMode = vm, formError = FormErrorNone } | ||||
|             , if vm == Table then | ||||
|                 Api.addonsGetAll flags LoadAddonsResp | ||||
|  | ||||
|               else | ||||
|                 Cmd.none | ||||
|             , Sub.none | ||||
|             ) | ||||
|  | ||||
|         FormMsg lm -> | ||||
|             let | ||||
|                 ( fm, fc ) = | ||||
|                     Comp.AddonArchiveForm.update flags lm model.formModel | ||||
|             in | ||||
|             ( { model | formModel = fm, formError = FormErrorNone } | ||||
|             , Cmd.map FormMsg fc | ||||
|             , Sub.none | ||||
|             ) | ||||
|  | ||||
|         TableMsg lm -> | ||||
|             let | ||||
|                 action = | ||||
|                     Comp.AddonArchiveTable.update lm | ||||
|             in | ||||
|             case action of | ||||
|                 Comp.AddonArchiveTable.Selected addon -> | ||||
|                     let | ||||
|                         ( bm, bc ) = | ||||
|                             Comp.AddonArchiveForm.initWith addon | ||||
|                     in | ||||
|                     ( { model | ||||
|                         | viewMode = Form | ||||
|                         , formError = FormErrorNone | ||||
|                         , formModel = bm | ||||
|                       } | ||||
|                     , Cmd.map FormMsg bc | ||||
|                     , Sub.none | ||||
|                     ) | ||||
|  | ||||
|         RequestDelete -> | ||||
|             ( { model | deleteConfirm = DeleteConfirmOn }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         CancelDelete -> | ||||
|             ( { model | deleteConfirm = DeleteConfirmOff }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         DeleteAddonNow id -> | ||||
|             ( { model | deleteConfirm = DeleteConfirmOff, loading = True } | ||||
|             , Api.addonsDelete flags id DeleteAddonResp | ||||
|             , Sub.none | ||||
|             ) | ||||
|  | ||||
|         LoadAddons -> | ||||
|             ( { model | loading = True } | ||||
|             , Api.addonsGetAll flags LoadAddonsResp | ||||
|             , Sub.none | ||||
|             ) | ||||
|  | ||||
|         LoadAddonsResp (Ok list) -> | ||||
|             ( { model | loading = False, addons = list.items, formError = FormErrorNone } | ||||
|             , Cmd.none | ||||
|             , Sub.none | ||||
|             ) | ||||
|  | ||||
|         LoadAddonsResp (Err err) -> | ||||
|             ( { model | loading = False, formError = FormErrorHttp err }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         AddonInstallResp info -> | ||||
|             if info.success then | ||||
|                 ( { model | loading = False, viewMode = Table }, Api.addonsGetAll flags LoadAddonsResp, Sub.none ) | ||||
|  | ||||
|             else | ||||
|                 ( { model | loading = False, formError = FormErrorSubmit info.message }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         Submit -> | ||||
|             case Comp.AddonArchiveForm.get model.formModel of | ||||
|                 Just data -> | ||||
|                     if data.id /= "" then | ||||
|                         ( { model | loading = True } | ||||
|                         , Api.addonsUpdate flags data.id UpdateAddonResp | ||||
|                         , Sub.none | ||||
|                         ) | ||||
|  | ||||
|                     else | ||||
|                         ( { model | loading = True } | ||||
|                         , Api.addonsInstall | ||||
|                             flags | ||||
|                             (AddonRegister <| Maybe.withDefault "" data.url) | ||||
|                             AddAddonResp | ||||
|                         , Sub.none | ||||
|                         ) | ||||
|  | ||||
|                 Nothing -> | ||||
|                     ( { model | formError = FormErrorInvalid }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         AddAddonResp (Ok res) -> | ||||
|             if res.success then | ||||
|                 ( model, Cmd.none, Sub.none ) | ||||
|  | ||||
|             else | ||||
|                 ( { model | loading = False, formError = FormErrorSubmit res.message }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         AddAddonResp (Err err) -> | ||||
|             ( { model | loading = False, formError = FormErrorHttp err }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         UpdateAddonResp (Ok res) -> | ||||
|             if res.success then | ||||
|                 ( model, Cmd.none, Sub.none ) | ||||
|  | ||||
|             else | ||||
|                 ( { model | loading = False, formError = FormErrorSubmit res.message }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         UpdateAddonResp (Err err) -> | ||||
|             ( { model | loading = False, formError = FormErrorHttp err }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         DeleteAddonResp (Ok res) -> | ||||
|             if res.success then | ||||
|                 update flags (SetViewMode Table) { model | loading = False } | ||||
|  | ||||
|             else | ||||
|                 ( { model | formError = FormErrorSubmit res.message, loading = False }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         DeleteAddonResp (Err err) -> | ||||
|             ( { model | formError = FormErrorHttp err, loading = False }, Cmd.none, Sub.none ) | ||||
|  | ||||
|  | ||||
|  | ||||
| --- view | ||||
|  | ||||
|  | ||||
| view : Texts -> UiSettings -> Flags -> Model -> Html Msg | ||||
| view texts settings flags model = | ||||
|     if model.viewMode == Table then | ||||
|         viewTable texts model | ||||
|  | ||||
|     else | ||||
|         viewForm texts settings flags model | ||||
|  | ||||
|  | ||||
| viewTable : Texts -> Model -> Html Msg | ||||
| viewTable texts model = | ||||
|     div [ class "flex flex-col" ] | ||||
|         [ MB.view | ||||
|             { start = | ||||
|                 [] | ||||
|             , end = | ||||
|                 [ MB.PrimaryButton | ||||
|                     { tagger = InitNewAddon | ||||
|                     , title = texts.createNewAddonArchive | ||||
|                     , icon = Just "fa fa-plus" | ||||
|                     , label = texts.newAddonArchive | ||||
|                     } | ||||
|                 ] | ||||
|             , rootClasses = "mb-4" | ||||
|             , sticky = True | ||||
|             } | ||||
|         , div | ||||
|             [ class "flex flex-col" | ||||
|             ] | ||||
|             [ Html.map TableMsg | ||||
|                 (Comp.AddonArchiveTable.view texts.addonArchiveTable model.addons) | ||||
|             ] | ||||
|         , B.loadingDimmer | ||||
|             { label = "" | ||||
|             , active = model.loading | ||||
|             } | ||||
|         ] | ||||
|  | ||||
|  | ||||
| viewForm : Texts -> UiSettings -> Flags -> Model -> Html Msg | ||||
| viewForm texts _ _ model = | ||||
|     let | ||||
|         newAddon = | ||||
|             model.formModel.addon.id == "" | ||||
|  | ||||
|         isValid = | ||||
|             Comp.AddonArchiveForm.get model.formModel /= Nothing | ||||
|     in | ||||
|     div [ class "relative" ] | ||||
|         [ Html.form [] | ||||
|             [ if newAddon then | ||||
|                 h1 [ class S.header2 ] | ||||
|                     [ text texts.createNewAddonArchive | ||||
|                     ] | ||||
|  | ||||
|               else | ||||
|                 h1 [ class S.header2 ] | ||||
|                     [ text (Comp.AddonArchiveForm.get model.formModel |> Maybe.map .name |> Maybe.withDefault "Update") | ||||
|                     ] | ||||
|             , MB.view | ||||
|                 { start = | ||||
|                     [ MB.SecondaryButton | ||||
|                         { tagger = SetViewMode Table | ||||
|                         , title = texts.basics.backToList | ||||
|                         , icon = Just "fa fa-arrow-left" | ||||
|                         , label = texts.basics.back | ||||
|                         } | ||||
|                     ] | ||||
|                 , end = | ||||
|                     if not newAddon then | ||||
|                         [ MB.DeleteButton | ||||
|                             { tagger = RequestDelete | ||||
|                             , title = texts.deleteThisAddonArchive | ||||
|                             , icon = Just "fa fa-trash" | ||||
|                             , label = texts.basics.delete | ||||
|                             } | ||||
|                         ] | ||||
|  | ||||
|                     else | ||||
|                         [] | ||||
|                 , rootClasses = "mb-4" | ||||
|                 , sticky = True | ||||
|                 } | ||||
|             , div | ||||
|                 [ classList | ||||
|                     [ ( "hidden", model.formError == FormErrorNone ) | ||||
|                     ] | ||||
|                 , class "my-2" | ||||
|                 , class S.errorMessage | ||||
|                 ] | ||||
|                 [ case model.formError of | ||||
|                     FormErrorNone -> | ||||
|                         text "" | ||||
|  | ||||
|                     FormErrorHttp err -> | ||||
|                         text (texts.httpError err) | ||||
|  | ||||
|                     FormErrorInvalid -> | ||||
|                         text texts.correctFormErrors | ||||
|  | ||||
|                     FormErrorSubmit m -> | ||||
|                         text m | ||||
|                 ] | ||||
|             , div [] | ||||
|                 [ Html.map FormMsg (Comp.AddonArchiveForm.view texts.addonArchiveForm model.formModel) | ||||
|                 ] | ||||
|             , MB.view | ||||
|                 { start = | ||||
|                     [ MB.PrimaryButton | ||||
|                         { tagger = Submit | ||||
|                         , title = texts.installNow | ||||
|                         , icon = | ||||
|                             if newAddon then | ||||
|                                 Just "fa fa-save" | ||||
|  | ||||
|                             else | ||||
|                                 Just "fa fa-arrows-rotate" | ||||
|                         , label = | ||||
|                             if newAddon then | ||||
|                                 texts.installNow | ||||
|  | ||||
|                             else | ||||
|                                 texts.updateNow | ||||
|                         } | ||||
|                     ] | ||||
|                 , end = [] | ||||
|                 , rootClasses = "mb-4" | ||||
|                 , sticky = False | ||||
|                 } | ||||
|             , div | ||||
|                 [ class "mb-4" | ||||
|                 , classList [ ( "hidden", newAddon ) ] | ||||
|                 ] | ||||
|                 [ label [ class S.inputLabel ] [ text texts.description ] | ||||
|                 , case model.formModel.addon.description of | ||||
|                     Just desc -> | ||||
|                         Markdown.toHtml [ class "markdown-preview" ] desc | ||||
|  | ||||
|                     Nothing -> | ||||
|                         div [ class "italic" ] [ text "-" ] | ||||
|                 ] | ||||
|             , B.loadingDimmer | ||||
|                 { active = model.loading | ||||
|                 , label = texts.basics.loading | ||||
|                 } | ||||
|             , B.contentDimmer | ||||
|                 (model.deleteConfirm == DeleteConfirmOn) | ||||
|                 (div [ class "flex flex-col" ] | ||||
|                     [ div [ class "text-lg" ] | ||||
|                         [ i [ class "fa fa-info-circle mr-2" ] [] | ||||
|                         , text texts.reallyDeleteAddonArchive | ||||
|                         ] | ||||
|                     , div [ class "mt-4 flex flex-row items-center" ] | ||||
|                         [ B.deleteButton | ||||
|                             { label = texts.basics.yes | ||||
|                             , icon = "fa fa-check" | ||||
|                             , disabled = False | ||||
|                             , handler = onClick (DeleteAddonNow model.formModel.addon.id) | ||||
|                             , attrs = [ href "#" ] | ||||
|                             } | ||||
|                         , B.secondaryButton | ||||
|                             { label = texts.basics.no | ||||
|                             , icon = "fa fa-times" | ||||
|                             , disabled = False | ||||
|                             , handler = onClick CancelDelete | ||||
|                             , attrs = [ href "#", class "ml-2" ] | ||||
|                             } | ||||
|                         ] | ||||
|                     ] | ||||
|                 ) | ||||
|             ] | ||||
|         ] | ||||
							
								
								
									
										72
									
								
								modules/webapp/src/main/elm/Comp/AddonArchiveTable.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								modules/webapp/src/main/elm/Comp/AddonArchiveTable.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Comp.AddonArchiveTable exposing (..) | ||||
|  | ||||
| import Api.Model.Addon exposing (Addon) | ||||
| import Comp.Basic as B | ||||
| import Html exposing (Html, div, table, tbody, td, text, th, thead, tr) | ||||
| import Html.Attributes exposing (class) | ||||
| import Messages.Comp.AddonArchiveTable exposing (Texts) | ||||
| import Styles as S | ||||
|  | ||||
|  | ||||
| type Msg | ||||
|     = SelectAddon Addon | ||||
|  | ||||
|  | ||||
| type TableAction | ||||
|     = Selected Addon | ||||
|  | ||||
|  | ||||
|  | ||||
| --- Update | ||||
|  | ||||
|  | ||||
| update : Msg -> TableAction | ||||
| update msg = | ||||
|     case msg of | ||||
|         SelectAddon addon -> | ||||
|             Selected addon | ||||
|  | ||||
|  | ||||
|  | ||||
| --- View | ||||
|  | ||||
|  | ||||
| view : Texts -> List Addon -> Html Msg | ||||
| view texts addons = | ||||
|     table [ class S.tableMain ] | ||||
|         [ thead [] | ||||
|             [ tr [] | ||||
|                 [ th [ class "" ] [] | ||||
|                 , th [ class "text-left" ] | ||||
|                     [ text texts.basics.name | ||||
|                     ] | ||||
|                 , th [ class "text-left" ] | ||||
|                     [ text texts.version | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|         , tbody [] | ||||
|             (List.map (renderAddonLine texts) addons) | ||||
|         ] | ||||
|  | ||||
|  | ||||
| renderAddonLine : Texts -> Addon -> Html Msg | ||||
| renderAddonLine texts addon = | ||||
|     tr | ||||
|         [ class S.tableRow | ||||
|         ] | ||||
|         [ B.editLinkTableCell texts.basics.edit (SelectAddon addon) | ||||
|         , td [ class "text-left py-4 md:py-2" ] | ||||
|             [ text addon.name | ||||
|             ] | ||||
|         , td [ class "text-left" ] | ||||
|             [ text addon.version | ||||
|             ] | ||||
|         ] | ||||
							
								
								
									
										709
									
								
								modules/webapp/src/main/elm/Comp/AddonRunConfigForm.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										709
									
								
								modules/webapp/src/main/elm/Comp/AddonRunConfigForm.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,709 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Comp.AddonRunConfigForm exposing (Model, Msg, get, init, initWith, update, view) | ||||
|  | ||||
| import Api | ||||
| import Api.Model.Addon exposing (Addon) | ||||
| import Api.Model.AddonList exposing (AddonList) | ||||
| import Api.Model.AddonRef exposing (AddonRef) | ||||
| import Api.Model.AddonRunConfig exposing (AddonRunConfig) | ||||
| import Api.Model.User exposing (User) | ||||
| import Api.Model.UserList exposing (UserList) | ||||
| import Comp.Basic as B | ||||
| import Comp.CalEventInput | ||||
| import Comp.Dropdown | ||||
| import Comp.MenuBar as MB | ||||
| import Data.AddonTrigger exposing (AddonTrigger) | ||||
| import Data.CalEvent exposing (CalEvent) | ||||
| import Data.DropdownStyle as DS | ||||
| import Data.Flags exposing (Flags) | ||||
| import Data.TimeZone exposing (TimeZone) | ||||
| import Data.UiSettings exposing (UiSettings) | ||||
| import Html exposing (..) | ||||
| import Html.Attributes exposing (..) | ||||
| import Html.Events exposing (onClick, onInput) | ||||
| import Http | ||||
| import Markdown | ||||
| import Messages.Comp.AddonRunConfigForm exposing (Texts) | ||||
| import Process | ||||
| import Styles as S | ||||
| import Task | ||||
| import Util.List | ||||
| import Util.String | ||||
|  | ||||
|  | ||||
| type alias Model = | ||||
|     { runConfig : AddonRunConfig | ||||
|     , name : String | ||||
|     , enabled : Bool | ||||
|     , userDropdown : Comp.Dropdown.Model User | ||||
|     , userId : Maybe String | ||||
|     , userList : List User | ||||
|     , scheduleModel : Maybe Comp.CalEventInput.Model | ||||
|     , schedule : Maybe CalEvent | ||||
|     , triggerDropdown : Comp.Dropdown.Model AddonTrigger | ||||
|     , addons : List AddonRef | ||||
|     , selectedAddon : Maybe AddonConfigModel | ||||
|     , existingAddonDropdown : Comp.Dropdown.Model Addon | ||||
|     , existingAddons : List Addon | ||||
|     , configApplied : Bool | ||||
|     } | ||||
|  | ||||
|  | ||||
| type alias AddonConfigModel = | ||||
|     { ref : AddonRef | ||||
|     , position : Int | ||||
|     , args : String | ||||
|     , readMore : Bool | ||||
|     } | ||||
|  | ||||
|  | ||||
| getRef : AddonConfigModel -> AddonRef | ||||
| getRef cfg = | ||||
|     let | ||||
|         a = | ||||
|             cfg.ref | ||||
|     in | ||||
|     { a | args = cfg.args } | ||||
|  | ||||
|  | ||||
| emptyModel : Model | ||||
| emptyModel = | ||||
|     { runConfig = Api.Model.AddonRunConfig.empty | ||||
|     , name = "" | ||||
|     , enabled = True | ||||
|     , userDropdown = Comp.Dropdown.makeSingle | ||||
|     , userId = Nothing | ||||
|     , userList = [] | ||||
|     , scheduleModel = Nothing | ||||
|     , schedule = Nothing | ||||
|     , triggerDropdown = | ||||
|         Comp.Dropdown.makeMultipleList | ||||
|             { options = Data.AddonTrigger.all, selected = [] } | ||||
|     , addons = [] | ||||
|     , selectedAddon = Nothing | ||||
|     , existingAddonDropdown = Comp.Dropdown.makeSingle | ||||
|     , existingAddons = [] | ||||
|     , configApplied = False | ||||
|     } | ||||
|  | ||||
|  | ||||
| init : Flags -> ( Model, Cmd Msg ) | ||||
| init flags = | ||||
|     ( emptyModel | ||||
|     , Cmd.batch | ||||
|         [ Api.getUsers flags UserListResp | ||||
|         , Api.addonsGetAll flags AddonListResp | ||||
|         ] | ||||
|     ) | ||||
|  | ||||
|  | ||||
| initWith : Flags -> AddonRunConfig -> ( Model, Cmd Msg ) | ||||
| initWith flags a = | ||||
|     let | ||||
|         ce = | ||||
|             Maybe.andThen Data.CalEvent.fromEvent a.schedule | ||||
|  | ||||
|         ceInit = | ||||
|             Maybe.map (Comp.CalEventInput.init flags) ce | ||||
|  | ||||
|         triggerModel = | ||||
|             Comp.Dropdown.makeMultipleList | ||||
|                 { options = Data.AddonTrigger.all | ||||
|                 , selected = Data.AddonTrigger.fromList a.trigger | ||||
|                 } | ||||
|     in | ||||
|     ( { emptyModel | ||||
|         | runConfig = a | ||||
|         , name = a.name | ||||
|         , enabled = a.enabled | ||||
|         , scheduleModel = Maybe.map Tuple.first ceInit | ||||
|         , schedule = ce | ||||
|         , triggerDropdown = triggerModel | ||||
|         , userId = a.userId | ||||
|         , addons = a.addons | ||||
|       } | ||||
|     , Cmd.batch | ||||
|         [ Api.getUsers flags UserListResp | ||||
|         , Api.addonsGetAll flags AddonListResp | ||||
|         , Maybe.map Tuple.second ceInit | ||||
|             |> Maybe.map (Cmd.map ScheduleMsg) | ||||
|             |> Maybe.withDefault Cmd.none | ||||
|         ] | ||||
|     ) | ||||
|  | ||||
|  | ||||
| isValid : Model -> Bool | ||||
| isValid model = | ||||
|     model.name | ||||
|         /= "" | ||||
|         && (Comp.Dropdown.getSelected model.triggerDropdown | ||||
|                 |> List.isEmpty | ||||
|                 |> not | ||||
|            ) | ||||
|         && (List.isEmpty model.addons | ||||
|                 |> not | ||||
|            ) | ||||
|  | ||||
|  | ||||
| get : Model -> Maybe AddonRunConfig | ||||
| get model = | ||||
|     let | ||||
|         a = | ||||
|             model.runConfig | ||||
|     in | ||||
|     if isValid model then | ||||
|         Just | ||||
|             { a | ||||
|                 | name = model.name | ||||
|                 , enabled = model.enabled | ||||
|                 , schedule = Maybe.map Data.CalEvent.makeEvent model.schedule | ||||
|                 , trigger = | ||||
|                     Comp.Dropdown.getSelected model.triggerDropdown | ||||
|                         |> List.map Data.AddonTrigger.asString | ||||
|                 , userId = model.userId | ||||
|                 , addons = model.addons | ||||
|             } | ||||
|  | ||||
|     else | ||||
|         Nothing | ||||
|  | ||||
|  | ||||
| type Msg | ||||
|     = SetName String | ||||
|     | UserListResp (Result Http.Error UserList) | ||||
|     | AddonListResp (Result Http.Error AddonList) | ||||
|     | ScheduleMsg Comp.CalEventInput.Msg | ||||
|     | UserDropdownMsg (Comp.Dropdown.Msg User) | ||||
|     | TriggerDropdownMsg (Comp.Dropdown.Msg AddonTrigger) | ||||
|     | AddonDropdownMsg (Comp.Dropdown.Msg Addon) | ||||
|     | Configure Int AddonRef | ||||
|     | Up Int | ||||
|     | Down Int | ||||
|     | Remove Int | ||||
|     | ToggleEnabled | ||||
|     | ConfigSetArgs String | ||||
|     | ConfigApply | ||||
|     | ConfigCancel | ||||
|     | AddSelectedAddon | ||||
|     | ConfigToggleReadMore | ||||
|     | ConfigArgsUpdated Bool | ||||
|  | ||||
|  | ||||
|  | ||||
| --- Update | ||||
|  | ||||
|  | ||||
| update : Flags -> TimeZone -> Msg -> Model -> ( Model, Cmd Msg ) | ||||
| update flags tz msg model = | ||||
|     case msg of | ||||
|         UserListResp (Ok list) -> | ||||
|             let | ||||
|                 um = | ||||
|                     Comp.Dropdown.makeSingleList | ||||
|                         { options = list.items | ||||
|                         , selected = Nothing | ||||
|                         } | ||||
|             in | ||||
|             ( { model | userDropdown = um, userList = list.items }, Cmd.none ) | ||||
|  | ||||
|         UserListResp (Err err) -> | ||||
|             ( model, Cmd.none ) | ||||
|  | ||||
|         AddonListResp (Ok list) -> | ||||
|             let | ||||
|                 am = | ||||
|                     Comp.Dropdown.makeSingleList | ||||
|                         { options = list.items | ||||
|                         , selected = Nothing | ||||
|                         } | ||||
|             in | ||||
|             ( { model | existingAddonDropdown = am, existingAddons = list.items }, Cmd.none ) | ||||
|  | ||||
|         AddonListResp (Err err) -> | ||||
|             ( model, Cmd.none ) | ||||
|  | ||||
|         UserDropdownMsg lm -> | ||||
|             let | ||||
|                 ( um, cmd ) = | ||||
|                     Comp.Dropdown.update lm model.userDropdown | ||||
|  | ||||
|                 sel = | ||||
|                     Comp.Dropdown.getSelected um |> List.head | ||||
|             in | ||||
|             ( { model | userDropdown = um, userId = Maybe.map .id sel }, Cmd.map UserDropdownMsg cmd ) | ||||
|  | ||||
|         TriggerDropdownMsg lm -> | ||||
|             let | ||||
|                 ( tm, tc ) = | ||||
|                     Comp.Dropdown.update lm model.triggerDropdown | ||||
|  | ||||
|                 ( nm, nc ) = | ||||
|                     initScheduleIfNeeded flags { model | triggerDropdown = tm } tz | ||||
|             in | ||||
|             ( nm, Cmd.batch [ Cmd.map TriggerDropdownMsg tc, nc ] ) | ||||
|  | ||||
|         ScheduleMsg lm -> | ||||
|             case model.scheduleModel of | ||||
|                 Just m -> | ||||
|                     let | ||||
|                         ( cm, cc, ce ) = | ||||
|                             Comp.CalEventInput.update flags tz model.schedule lm m | ||||
|                     in | ||||
|                     ( { model | scheduleModel = Just cm, schedule = ce }, Cmd.map ScheduleMsg cc ) | ||||
|  | ||||
|                 Nothing -> | ||||
|                     ( model, Cmd.none ) | ||||
|  | ||||
|         ToggleEnabled -> | ||||
|             ( { model | enabled = not model.enabled }, Cmd.none ) | ||||
|  | ||||
|         AddonDropdownMsg lm -> | ||||
|             let | ||||
|                 ( am, ac ) = | ||||
|                     Comp.Dropdown.update lm model.existingAddonDropdown | ||||
|             in | ||||
|             ( { model | existingAddonDropdown = am }, Cmd.map AddonDropdownMsg ac ) | ||||
|  | ||||
|         Configure index ref -> | ||||
|             let | ||||
|                 cfg = | ||||
|                     { ref = ref | ||||
|                     , position = index + 1 | ||||
|                     , args = ref.args | ||||
|                     , readMore = False | ||||
|                     } | ||||
|             in | ||||
|             ( { model | selectedAddon = Just cfg }, Cmd.none ) | ||||
|  | ||||
|         ConfigCancel -> | ||||
|             ( { model | selectedAddon = Nothing }, Cmd.none ) | ||||
|  | ||||
|         ConfigToggleReadMore -> | ||||
|             case model.selectedAddon of | ||||
|                 Just cfg -> | ||||
|                     ( { model | selectedAddon = Just { cfg | readMore = not cfg.readMore } }, Cmd.none ) | ||||
|  | ||||
|                 Nothing -> | ||||
|                     ( model, Cmd.none ) | ||||
|  | ||||
|         ConfigArgsUpdated flag -> | ||||
|             ( { model | configApplied = flag }, Cmd.none ) | ||||
|  | ||||
|         ConfigSetArgs str -> | ||||
|             case model.selectedAddon of | ||||
|                 Just cfg -> | ||||
|                     ( { model | selectedAddon = Just { cfg | args = str } } | ||||
|                     , Cmd.none | ||||
|                     ) | ||||
|  | ||||
|                 Nothing -> | ||||
|                     ( model, Cmd.none ) | ||||
|  | ||||
|         ConfigApply -> | ||||
|             case model.selectedAddon of | ||||
|                 Just cfg -> | ||||
|                     let | ||||
|                         na = | ||||
|                             getRef cfg | ||||
|  | ||||
|                         addons = | ||||
|                             Util.List.replaceByIndex (cfg.position - 1) na model.addons | ||||
|                     in | ||||
|                     ( { model | addons = addons, configApplied = True } | ||||
|                     , Process.sleep 1200 |> Task.perform (\_ -> ConfigArgsUpdated False) | ||||
|                     ) | ||||
|  | ||||
|                 Nothing -> | ||||
|                     ( model, Cmd.none ) | ||||
|  | ||||
|         AddSelectedAddon -> | ||||
|             let | ||||
|                 sel = | ||||
|                     Comp.Dropdown.getSelected model.existingAddonDropdown |> List.head | ||||
|  | ||||
|                 ( dm, _ ) = | ||||
|                     Comp.Dropdown.update (Comp.Dropdown.SetSelection []) model.existingAddonDropdown | ||||
|  | ||||
|                 addon = | ||||
|                     Maybe.map | ||||
|                         (\a -> | ||||
|                             { addonId = a.id | ||||
|                             , name = a.name | ||||
|                             , version = a.version | ||||
|                             , description = a.description | ||||
|                             , args = "" | ||||
|                             } | ||||
|                         ) | ||||
|                         sel | ||||
|  | ||||
|                 newAddons = | ||||
|                     Maybe.map (\e -> e :: model.addons) addon | ||||
|                         |> Maybe.withDefault model.addons | ||||
|             in | ||||
|             ( { model | addons = newAddons, existingAddonDropdown = dm, selectedAddon = Nothing }, Cmd.none ) | ||||
|  | ||||
|         Up curIndex -> | ||||
|             let | ||||
|                 newAddons = | ||||
|                     Util.List.changePosition curIndex (curIndex - 1) model.addons | ||||
|             in | ||||
|             ( { model | addons = newAddons, selectedAddon = Nothing }, Cmd.none ) | ||||
|  | ||||
|         Down curIndex -> | ||||
|             let | ||||
|                 newAddons = | ||||
|                     Util.List.changePosition (curIndex + 1) curIndex model.addons | ||||
|             in | ||||
|             ( { model | addons = newAddons, selectedAddon = Nothing }, Cmd.none ) | ||||
|  | ||||
|         SetName str -> | ||||
|             ( { model | name = str }, Cmd.none ) | ||||
|  | ||||
|         Remove index -> | ||||
|             ( { model | addons = Util.List.removeByIndex index model.addons, selectedAddon = Nothing }, Cmd.none ) | ||||
|  | ||||
|  | ||||
| initScheduleIfNeeded : Flags -> Model -> TimeZone -> ( Model, Cmd Msg ) | ||||
| initScheduleIfNeeded flags model tz = | ||||
|     let | ||||
|         hasTrigger = | ||||
|             Comp.Dropdown.getSelected model.triggerDropdown | ||||
|                 |> List.any ((==) Data.AddonTrigger.Scheduled) | ||||
|  | ||||
|         noModel = | ||||
|             model.scheduleModel == Nothing | ||||
|  | ||||
|         hasModel = | ||||
|             not noModel | ||||
|  | ||||
|         ce = | ||||
|             Data.CalEvent.everyMonthTz tz | ||||
|  | ||||
|         ( cm, cc ) = | ||||
|             Comp.CalEventInput.init flags ce | ||||
|     in | ||||
|     if hasTrigger && noModel then | ||||
|         ( { model | scheduleModel = Just cm, schedule = Just ce }, Cmd.map ScheduleMsg cc ) | ||||
|  | ||||
|     else if not hasTrigger && hasModel then | ||||
|         ( { model | scheduleModel = Nothing, schedule = Nothing }, Cmd.none ) | ||||
|  | ||||
|     else | ||||
|         ( model, Cmd.none ) | ||||
|  | ||||
|  | ||||
|  | ||||
| --- View | ||||
|  | ||||
|  | ||||
| view : Texts -> UiSettings -> Model -> Html Msg | ||||
| view texts settings model = | ||||
|     let | ||||
|         userDs = | ||||
|             { makeOption = \user -> { text = user.login, additional = "" } | ||||
|             , placeholder = texts.basics.selectPlaceholder | ||||
|             , labelColor = \_ -> \_ -> "" | ||||
|             , style = DS.mainStyle | ||||
|             } | ||||
|  | ||||
|         triggerDs = | ||||
|             { makeOption = \trigger -> { text = Data.AddonTrigger.asString trigger, additional = "" } | ||||
|             , placeholder = texts.basics.selectPlaceholder | ||||
|             , labelColor = \_ -> \_ -> "" | ||||
|             , style = DS.mainStyle | ||||
|             } | ||||
|     in | ||||
|     div | ||||
|         [ class "flex flex-col" ] | ||||
|         [ div [ class "mb-4" ] | ||||
|             [ div [ class "mb-4" ] | ||||
|                 [ label | ||||
|                     [ class S.inputLabel | ||||
|                     ] | ||||
|                     [ text texts.basics.name | ||||
|                     , B.inputRequired | ||||
|                     ] | ||||
|                 , input | ||||
|                     [ type_ "text" | ||||
|                     , placeholder texts.chooseName | ||||
|                     , value model.name | ||||
|                     , onInput SetName | ||||
|                     , class S.textInput | ||||
|                     , classList [ ( S.inputErrorBorder, model.name == "" ) ] | ||||
|                     ] | ||||
|                     [] | ||||
|                 ] | ||||
|             , div [ class "mb-4" ] | ||||
|                 [ MB.viewItem <| | ||||
|                     MB.Checkbox | ||||
|                         { tagger = \_ -> ToggleEnabled | ||||
|                         , label = texts.enableDisable | ||||
|                         , value = model.enabled | ||||
|                         , id = "addon-run-config-enabled" | ||||
|                         } | ||||
|                 ] | ||||
|             , div [ class "mb-4" ] | ||||
|                 [ label | ||||
|                     [ class S.inputLabel | ||||
|                     ] | ||||
|                     [ text texts.impersonateUser | ||||
|                     ] | ||||
|                 , Html.map UserDropdownMsg | ||||
|                     (Comp.Dropdown.view2 userDs settings model.userDropdown) | ||||
|                 ] | ||||
|             , div [ class "mb-4" ] | ||||
|                 [ label | ||||
|                     [ class S.inputLabel | ||||
|                     ] | ||||
|                     [ text texts.triggerRun | ||||
|                     , B.inputRequired | ||||
|                     ] | ||||
|                 , Html.map TriggerDropdownMsg | ||||
|                     (Comp.Dropdown.view2 triggerDs settings model.triggerDropdown) | ||||
|                 ] | ||||
|             , case model.scheduleModel of | ||||
|                 Nothing -> | ||||
|                     span [ class "hidden" ] [] | ||||
|  | ||||
|                 Just m -> | ||||
|                     div [ class "mb-4" ] | ||||
|                         [ label | ||||
|                             [ class S.inputLabel ] | ||||
|                             [ text texts.schedule | ||||
|                             ] | ||||
|                         , Html.map ScheduleMsg (Comp.CalEventInput.view2 texts.calEventInput "" model.schedule m) | ||||
|                         ] | ||||
|             ] | ||||
|         , div [ class "mb-4" ] | ||||
|             [ h2 [ class S.header2 ] | ||||
|                 [ text texts.addons ] | ||||
|             , addonRef texts model | ||||
|             , div [ class "mb-4" ] | ||||
|                 [ label [ class S.inputLabel ] | ||||
|                     [ text texts.includedAddons | ||||
|                     , B.inputRequired | ||||
|                     ] | ||||
|                 , newAddon texts settings model | ||||
|                 , div [ class "mb-4" ] | ||||
|                     [ div [ class "flex flex-col mb-4" ] | ||||
|                         (List.indexedMap (addonLine texts model) model.addons) | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|         ] | ||||
|  | ||||
|  | ||||
| newAddon : Texts -> UiSettings -> Model -> Html Msg | ||||
| newAddon texts uiSettings model = | ||||
|     let | ||||
|         addonDs = | ||||
|             { makeOption = \addon -> { text = addon.name ++ " / " ++ addon.version, additional = "" } | ||||
|             , placeholder = texts.basics.selectPlaceholder | ||||
|             , labelColor = \_ -> \_ -> "" | ||||
|             , style = DS.mainStyle | ||||
|             } | ||||
|     in | ||||
|     div [ class "mb-4" ] | ||||
|         [ div [ class "flex flex-row" ] | ||||
|             [ div [ class "flex-grow mr-2" ] | ||||
|                 [ Html.map AddonDropdownMsg | ||||
|                     (Comp.Dropdown.view2 addonDs uiSettings model.existingAddonDropdown) | ||||
|                 ] | ||||
|             , B.primaryBasicButton | ||||
|                 { label = texts.add | ||||
|                 , icon = "fa fa-plus" | ||||
|                 , disabled = List.isEmpty (Comp.Dropdown.getSelected model.existingAddonDropdown) | ||||
|                 , handler = onClick AddSelectedAddon | ||||
|                 , attrs = [ href "#" ] | ||||
|                 } | ||||
|             ] | ||||
|         ] | ||||
|  | ||||
|  | ||||
| addonRef : Texts -> Model -> Html Msg | ||||
| addonRef texts model = | ||||
|     let | ||||
|         maybeRef = | ||||
|             Maybe.map .ref model.selectedAddon | ||||
|  | ||||
|         refInfo = | ||||
|             case model.selectedAddon of | ||||
|                 Nothing -> | ||||
|                     div [ class "mb-4" ] | ||||
|                         [ text "[ -- ]" | ||||
|                         ] | ||||
|  | ||||
|                 Just cfg -> | ||||
|                     let | ||||
|                         ( descr, requireFolding ) = | ||||
|                             case cfg.ref.description of | ||||
|                                 Just d -> | ||||
|                                     let | ||||
|                                         part = | ||||
|                                             Util.String.firstSentenceOrMax 120 d | ||||
|  | ||||
|                                         text = | ||||
|                                             if cfg.readMore then | ||||
|                                                 d | ||||
|  | ||||
|                                             else | ||||
|                                                 Maybe.withDefault d part | ||||
|                                     in | ||||
|                                     ( Markdown.toHtml [ class "markdown-preview" ] text, part /= Nothing ) | ||||
|  | ||||
|                                 Nothing -> | ||||
|                                     ( span [ class "italic" ] [ text "No description." ], False ) | ||||
|                     in | ||||
|                     div [ class "flex flex-col mb-4" ] | ||||
|                         [ div [ class "mt-2" ] | ||||
|                             [ label [ class " font-semibold py-0.5 " ] | ||||
|                                 [ text cfg.ref.name | ||||
|                                 , text " " | ||||
|                                 , text cfg.ref.version | ||||
|                                 , text " (pos. " | ||||
|                                 , text <| String.fromInt cfg.position | ||||
|                                 , text ")" | ||||
|                                 , span | ||||
|                                     [ classList [ ( "hidden", not requireFolding ) ] | ||||
|                                     , class "ml-2" | ||||
|                                     ] | ||||
|                                     [ a | ||||
|                                         [ class "px-4" | ||||
|                                         , class S.link | ||||
|                                         , href "#" | ||||
|                                         , onClick ConfigToggleReadMore | ||||
|                                         ] | ||||
|                                         [ if cfg.readMore then | ||||
|                                             text texts.readLess | ||||
|  | ||||
|                                           else | ||||
|                                             text texts.readMore | ||||
|                                         ] | ||||
|                                     ] | ||||
|                                 ] | ||||
|                             , div [ class "px-3 py-1 border-l dark:border-slate-600" ] | ||||
|                                 [ descr | ||||
|                                 ] | ||||
|                             ] | ||||
|                         ] | ||||
|     in | ||||
|     div | ||||
|         [ class "flex flex-col mb-3" | ||||
|         , classList [ ( "disabled", maybeRef == Nothing ) ] | ||||
|         ] | ||||
|         [ refInfo | ||||
|         , div [ class "mb-2" ] | ||||
|             [ label [ class S.inputLabel ] [ text texts.arguments ] | ||||
|             , textarea | ||||
|                 [ Maybe.map .args model.selectedAddon |> Maybe.withDefault "" |> value | ||||
|                 , class S.textAreaInput | ||||
|                 , class "font-mono" | ||||
|                 , rows 8 | ||||
|                 , onInput ConfigSetArgs | ||||
|                 ] | ||||
|                 [] | ||||
|             ] | ||||
|         , MB.view | ||||
|             { start = | ||||
|                 [ MB.PrimaryButton | ||||
|                     { tagger = ConfigApply | ||||
|                     , title = "" | ||||
|                     , icon = Just "fa fa-save" | ||||
|                     , label = texts.update | ||||
|                     } | ||||
|                 , MB.SecondaryButton | ||||
|                     { tagger = ConfigCancel | ||||
|                     , title = texts.basics.cancel | ||||
|                     , icon = Just "fa fa-times" | ||||
|                     , label = texts.basics.cancel | ||||
|                     } | ||||
|                 , MB.CustomElement <| | ||||
|                     div | ||||
|                         [ classList [ ( "hidden", not model.configApplied ) ] | ||||
|                         , class S.successText | ||||
|                         , class "inline-block min-w-fit font-semibold text-normal min-w-fit" | ||||
|                         ] | ||||
|                         [ text texts.argumentsUpdated | ||||
|                         , i [ class "fa fa-thumbs-up ml-2" ] [] | ||||
|                         ] | ||||
|                 ] | ||||
|             , end = [] | ||||
|             , rootClasses = "mb-4 text-sm" | ||||
|             , sticky = False | ||||
|             } | ||||
|         ] | ||||
|  | ||||
|  | ||||
| addonLine : Texts -> Model -> Int -> AddonRef -> Html Msg | ||||
| addonLine texts model index ref = | ||||
|     let | ||||
|         isSelected = | ||||
|             case model.selectedAddon of | ||||
|                 Just cfg -> | ||||
|                     cfg.position - 1 == index | ||||
|  | ||||
|                 Nothing -> | ||||
|                     False | ||||
|     in | ||||
|     div | ||||
|         [ class "flex flex-row items-center px-4 py-4 rounded shadow dark:border dark:border-slate-600 mb-2" | ||||
|         , classList [ ( "ring-2", isSelected ) ] | ||||
|         ] | ||||
|         [ div [ class "px-2 hidden sm:block" ] | ||||
|             [ span [ class "label rounded-full opacity-75" ] | ||||
|                 [ text <| String.fromInt (index + 1) | ||||
|                 ] | ||||
|             ] | ||||
|         , div [ class "px-4 font-semibold" ] | ||||
|             [ text ref.name | ||||
|             , text " v" | ||||
|             , text ref.version | ||||
|             ] | ||||
|         , div [ class "flex-grow" ] | ||||
|             [] | ||||
|         , div [ class "px-2" ] | ||||
|             [ MB.view | ||||
|                 { start = [] | ||||
|                 , end = | ||||
|                     [ MB.PrimaryButton | ||||
|                         { tagger = Configure index ref | ||||
|                         , title = texts.configureTitle | ||||
|                         , icon = Just "fa fa-cog" | ||||
|                         , label = texts.configureLabel | ||||
|                         } | ||||
|                     , MB.CustomElement <| | ||||
|                         B.secondaryButton | ||||
|                             { handler = onClick (Up index) | ||||
|                             , attrs = [ title "Move up", href "#" ] | ||||
|                             , icon = "fa fa-arrow-up" | ||||
|                             , label = "" | ||||
|                             , disabled = index == 0 | ||||
|                             } | ||||
|                     , MB.CustomElement <| | ||||
|                         B.secondaryButton | ||||
|                             { handler = onClick (Down index) | ||||
|                             , attrs = [ title "Move down", href "#" ] | ||||
|                             , icon = "fa fa-arrow-down" | ||||
|                             , label = "" | ||||
|                             , disabled = index + 1 == List.length model.addons | ||||
|                             } | ||||
|                     , MB.CustomElement <| | ||||
|                         B.deleteButton | ||||
|                             { label = "" | ||||
|                             , icon = "fa fa-trash" | ||||
|                             , disabled = False | ||||
|                             , handler = onClick (Remove index) | ||||
|                             , attrs = [ href "#" ] | ||||
|                             } | ||||
|                     ] | ||||
|                 , rootClasses = "text-sm" | ||||
|                 , sticky = False | ||||
|                 } | ||||
|             ] | ||||
|         ] | ||||
							
								
								
									
										364
									
								
								modules/webapp/src/main/elm/Comp/AddonRunConfigManage.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										364
									
								
								modules/webapp/src/main/elm/Comp/AddonRunConfigManage.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,364 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Comp.AddonRunConfigManage exposing (Model, Msg, init, loadConfigs, update, view) | ||||
|  | ||||
| import Api | ||||
| import Api.Model.AddonRunConfig exposing (AddonRunConfig) | ||||
| import Api.Model.AddonRunConfigList exposing (AddonRunConfigList) | ||||
| import Api.Model.BasicResult exposing (BasicResult) | ||||
| import Comp.AddonRunConfigForm | ||||
| import Comp.AddonRunConfigTable | ||||
| import Comp.Basic as B | ||||
| import Comp.ItemDetail.Model exposing (Msg(..)) | ||||
| import Comp.MenuBar as MB | ||||
| import Data.Flags exposing (Flags) | ||||
| import Data.TimeZone exposing (TimeZone) | ||||
| import Data.UiSettings exposing (UiSettings) | ||||
| import Html exposing (..) | ||||
| import Html.Attributes exposing (..) | ||||
| import Html.Events exposing (onClick) | ||||
| import Http | ||||
| import Messages.Comp.AddonRunConfigManage exposing (Texts) | ||||
| import Page exposing (Page(..)) | ||||
| import Styles as S | ||||
|  | ||||
|  | ||||
| type FormError | ||||
|     = FormErrorNone | ||||
|     | FormErrorHttp Http.Error | ||||
|     | FormErrorInvalid | ||||
|     | FormErrorSubmit String | ||||
|  | ||||
|  | ||||
| type ViewMode | ||||
|     = Table | ||||
|     | Form | ||||
|  | ||||
|  | ||||
| type DeleteConfirm | ||||
|     = DeleteConfirmOff | ||||
|     | DeleteConfirmOn | ||||
|  | ||||
|  | ||||
| type alias Model = | ||||
|     { viewMode : ViewMode | ||||
|     , runConfigs : List AddonRunConfig | ||||
|     , formModel : Comp.AddonRunConfigForm.Model | ||||
|     , loading : Bool | ||||
|     , formError : FormError | ||||
|     , deleteConfirm : DeleteConfirm | ||||
|     } | ||||
|  | ||||
|  | ||||
| init : Flags -> ( Model, Cmd Msg ) | ||||
| init flags = | ||||
|     let | ||||
|         ( fm, fc ) = | ||||
|             Comp.AddonRunConfigForm.init flags | ||||
|     in | ||||
|     ( { viewMode = Table | ||||
|       , runConfigs = [] | ||||
|       , formModel = fm | ||||
|       , loading = False | ||||
|       , formError = FormErrorNone | ||||
|       , deleteConfirm = DeleteConfirmOff | ||||
|       } | ||||
|     , Cmd.batch | ||||
|         [ Cmd.map FormMsg fc | ||||
|         , Api.addonRunConfigGet flags LoadConfigsResp | ||||
|         ] | ||||
|     ) | ||||
|  | ||||
|  | ||||
| type Msg | ||||
|     = LoadRunConfigs | ||||
|     | TableMsg Comp.AddonRunConfigTable.Msg | ||||
|     | FormMsg Comp.AddonRunConfigForm.Msg | ||||
|     | InitNewConfig | ||||
|     | SetViewMode ViewMode | ||||
|     | Submit | ||||
|     | RequestDelete | ||||
|     | CancelDelete | ||||
|     | DeleteConfigNow String | ||||
|     | LoadConfigsResp (Result Http.Error AddonRunConfigList) | ||||
|     | AddConfigResp (Result Http.Error BasicResult) | ||||
|     | DeleteConfigResp (Result Http.Error BasicResult) | ||||
|  | ||||
|  | ||||
| loadConfigs : Msg | ||||
| loadConfigs = | ||||
|     LoadRunConfigs | ||||
|  | ||||
|  | ||||
|  | ||||
| --- update | ||||
|  | ||||
|  | ||||
| update : Flags -> TimeZone -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) | ||||
| update flags tz msg model = | ||||
|     case msg of | ||||
|         InitNewConfig -> | ||||
|             let | ||||
|                 ( bm, bc ) = | ||||
|                     Comp.AddonRunConfigForm.init flags | ||||
|  | ||||
|                 nm = | ||||
|                     { model | ||||
|                         | viewMode = Form | ||||
|                         , formError = FormErrorNone | ||||
|                         , formModel = bm | ||||
|                     } | ||||
|             in | ||||
|             ( nm, Cmd.map FormMsg bc, Sub.none ) | ||||
|  | ||||
|         SetViewMode vm -> | ||||
|             ( { model | viewMode = vm, formError = FormErrorNone } | ||||
|             , if vm == Table then | ||||
|                 Api.addonRunConfigGet flags LoadConfigsResp | ||||
|  | ||||
|               else | ||||
|                 Cmd.none | ||||
|             , Sub.none | ||||
|             ) | ||||
|  | ||||
|         FormMsg lm -> | ||||
|             let | ||||
|                 ( fm, fc ) = | ||||
|                     Comp.AddonRunConfigForm.update flags tz lm model.formModel | ||||
|             in | ||||
|             ( { model | formModel = fm, formError = FormErrorNone } | ||||
|             , Cmd.map FormMsg fc | ||||
|             , Sub.none | ||||
|             ) | ||||
|  | ||||
|         TableMsg lm -> | ||||
|             let | ||||
|                 action = | ||||
|                     Comp.AddonRunConfigTable.update lm | ||||
|             in | ||||
|             case action of | ||||
|                 Comp.AddonRunConfigTable.Selected addon -> | ||||
|                     let | ||||
|                         ( bm, bc ) = | ||||
|                             Comp.AddonRunConfigForm.initWith flags addon | ||||
|                     in | ||||
|                     ( { model | ||||
|                         | viewMode = Form | ||||
|                         , formError = FormErrorNone | ||||
|                         , formModel = bm | ||||
|                       } | ||||
|                     , Cmd.map FormMsg bc | ||||
|                     , Sub.none | ||||
|                     ) | ||||
|  | ||||
|         RequestDelete -> | ||||
|             ( { model | deleteConfirm = DeleteConfirmOn }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         CancelDelete -> | ||||
|             ( { model | deleteConfirm = DeleteConfirmOff }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         DeleteConfigNow id -> | ||||
|             ( { model | deleteConfirm = DeleteConfirmOff, loading = True } | ||||
|             , Api.addonRunConfigDelete flags id DeleteConfigResp | ||||
|             , Sub.none | ||||
|             ) | ||||
|  | ||||
|         LoadRunConfigs -> | ||||
|             ( { model | loading = True } | ||||
|             , Api.addonRunConfigGet flags LoadConfigsResp | ||||
|             , Sub.none | ||||
|             ) | ||||
|  | ||||
|         LoadConfigsResp (Ok list) -> | ||||
|             ( { model | loading = False, runConfigs = list.items, formError = FormErrorNone } | ||||
|             , Cmd.none | ||||
|             , Sub.none | ||||
|             ) | ||||
|  | ||||
|         LoadConfigsResp (Err err) -> | ||||
|             ( { model | loading = False, formError = FormErrorHttp err }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         Submit -> | ||||
|             case Comp.AddonRunConfigForm.get model.formModel of | ||||
|                 Just data -> | ||||
|                     ( { model | loading = True }, Api.addonRunConfigSet flags data AddConfigResp, Sub.none ) | ||||
|  | ||||
|                 Nothing -> | ||||
|                     ( { model | formError = FormErrorInvalid }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         AddConfigResp (Ok res) -> | ||||
|             if res.success then | ||||
|                 ( { model | loading = False }, Cmd.none, Sub.none ) | ||||
|  | ||||
|             else | ||||
|                 ( { model | loading = False, formError = FormErrorSubmit res.message }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         AddConfigResp (Err err) -> | ||||
|             ( { model | loading = False, formError = FormErrorHttp err }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         DeleteConfigResp (Ok res) -> | ||||
|             if res.success then | ||||
|                 update flags tz (SetViewMode Table) { model | loading = False } | ||||
|  | ||||
|             else | ||||
|                 ( { model | formError = FormErrorSubmit res.message, loading = False }, Cmd.none, Sub.none ) | ||||
|  | ||||
|         DeleteConfigResp (Err err) -> | ||||
|             ( { model | formError = FormErrorHttp err, loading = False }, Cmd.none, Sub.none ) | ||||
|  | ||||
|  | ||||
|  | ||||
| --- view | ||||
|  | ||||
|  | ||||
| view : Texts -> UiSettings -> Flags -> Model -> Html Msg | ||||
| view texts settings flags model = | ||||
|     if model.viewMode == Table then | ||||
|         viewTable texts model | ||||
|  | ||||
|     else | ||||
|         viewForm texts settings flags model | ||||
|  | ||||
|  | ||||
| viewTable : Texts -> Model -> Html Msg | ||||
| viewTable texts model = | ||||
|     div [ class "flex flex-col" ] | ||||
|         [ MB.view | ||||
|             { start = | ||||
|                 [] | ||||
|             , end = | ||||
|                 [ MB.PrimaryButton | ||||
|                     { tagger = InitNewConfig | ||||
|                     , title = texts.createNewAddonRunConfig | ||||
|                     , icon = Just "fa fa-plus" | ||||
|                     , label = texts.newAddonRunConfig | ||||
|                     } | ||||
|                 ] | ||||
|             , rootClasses = "mb-4" | ||||
|             , sticky = True | ||||
|             } | ||||
|         , div | ||||
|             [ class "flex flex-col" | ||||
|             ] | ||||
|             [ Html.map TableMsg | ||||
|                 (Comp.AddonRunConfigTable.view texts.addonArchiveTable model.runConfigs) | ||||
|             ] | ||||
|         , B.loadingDimmer | ||||
|             { label = "" | ||||
|             , active = model.loading | ||||
|             } | ||||
|         ] | ||||
|  | ||||
|  | ||||
| viewForm : Texts -> UiSettings -> Flags -> Model -> Html Msg | ||||
| viewForm texts uiSettings _ model = | ||||
|     let | ||||
|         newConfig = | ||||
|             model.formModel.runConfig.id == "" | ||||
|  | ||||
|         isValid = | ||||
|             Comp.AddonRunConfigForm.get model.formModel /= Nothing | ||||
|     in | ||||
|     div [] | ||||
|         [ Html.form [] | ||||
|             [ if newConfig then | ||||
|                 h1 [ class S.header2 ] | ||||
|                     [ text texts.createNewAddonRunConfig | ||||
|                     ] | ||||
|  | ||||
|               else | ||||
|                 h1 [ class S.header2 ] | ||||
|                     [ text (Comp.AddonRunConfigForm.get model.formModel |> Maybe.map .name |> Maybe.withDefault "Update") | ||||
|                     ] | ||||
|             , MB.view | ||||
|                 { start = | ||||
|                     [ MB.CustomElement <| | ||||
|                         B.primaryButton | ||||
|                             { handler = onClick Submit | ||||
|                             , title = texts.basics.submitThisForm | ||||
|                             , icon = "fa fa-save" | ||||
|                             , label = texts.basics.submit | ||||
|                             , disabled = not isValid | ||||
|                             , attrs = [ href "#" ] | ||||
|                             } | ||||
|                     , MB.SecondaryButton | ||||
|                         { tagger = SetViewMode Table | ||||
|                         , title = texts.basics.backToList | ||||
|                         , icon = Just "fa fa-arrow-left" | ||||
|                         , label = texts.basics.back | ||||
|                         } | ||||
|                     ] | ||||
|                 , end = | ||||
|                     if not newConfig then | ||||
|                         [ MB.DeleteButton | ||||
|                             { tagger = RequestDelete | ||||
|                             , title = texts.deleteThisAddonRunConfig | ||||
|                             , icon = Just "fa fa-trash" | ||||
|                             , label = texts.basics.delete | ||||
|                             } | ||||
|                         ] | ||||
|  | ||||
|                     else | ||||
|                         [] | ||||
|                 , rootClasses = "mb-4" | ||||
|                 , sticky = True | ||||
|                 } | ||||
|             , div | ||||
|                 [ classList | ||||
|                     [ ( "hidden", model.formError == FormErrorNone ) | ||||
|                     ] | ||||
|                 , class "my-2" | ||||
|                 , class S.errorMessage | ||||
|                 ] | ||||
|                 [ case model.formError of | ||||
|                     FormErrorNone -> | ||||
|                         text "" | ||||
|  | ||||
|                     FormErrorHttp err -> | ||||
|                         text (texts.httpError err) | ||||
|  | ||||
|                     FormErrorInvalid -> | ||||
|                         text texts.correctFormErrors | ||||
|  | ||||
|                     FormErrorSubmit m -> | ||||
|                         text m | ||||
|                 ] | ||||
|             , div [] | ||||
|                 [ Html.map FormMsg (Comp.AddonRunConfigForm.view texts.addonArchiveForm uiSettings model.formModel) | ||||
|                 ] | ||||
|             , B.loadingDimmer | ||||
|                 { active = model.loading | ||||
|                 , label = texts.basics.loading | ||||
|                 } | ||||
|             , B.contentDimmer | ||||
|                 (model.deleteConfirm == DeleteConfirmOn) | ||||
|                 (div [ class "flex flex-col" ] | ||||
|                     [ div [ class "text-lg" ] | ||||
|                         [ i [ class "fa fa-info-circle mr-2" ] [] | ||||
|                         , text texts.reallyDeleteAddonRunConfig | ||||
|                         ] | ||||
|                     , div [ class "mt-4 flex flex-row items-center" ] | ||||
|                         [ B.deleteButton | ||||
|                             { label = texts.basics.yes | ||||
|                             , icon = "fa fa-check" | ||||
|                             , disabled = False | ||||
|                             , handler = onClick (DeleteConfigNow model.formModel.runConfig.id) | ||||
|                             , attrs = [ href "#" ] | ||||
|                             } | ||||
|                         , B.secondaryButton | ||||
|                             { label = texts.basics.no | ||||
|                             , icon = "fa fa-times" | ||||
|                             , disabled = False | ||||
|                             , handler = onClick CancelDelete | ||||
|                             , attrs = [ href "#", class "ml-2" ] | ||||
|                             } | ||||
|                         ] | ||||
|                     ] | ||||
|                 ) | ||||
|             ] | ||||
|         ] | ||||
							
								
								
									
										79
									
								
								modules/webapp/src/main/elm/Comp/AddonRunConfigTable.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								modules/webapp/src/main/elm/Comp/AddonRunConfigTable.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Comp.AddonRunConfigTable exposing (..) | ||||
|  | ||||
| import Api.Model.AddonRunConfig exposing (AddonRunConfig) | ||||
| import Comp.Basic as B | ||||
| import Html exposing (Html, div, table, tbody, td, text, th, thead, tr) | ||||
| import Html.Attributes exposing (class) | ||||
| import Messages.Comp.AddonRunConfigTable exposing (Texts) | ||||
| import Styles as S | ||||
| import Util.Html | ||||
|  | ||||
|  | ||||
| type Msg | ||||
|     = SelectRunConfig AddonRunConfig | ||||
|  | ||||
|  | ||||
| type TableAction | ||||
|     = Selected AddonRunConfig | ||||
|  | ||||
|  | ||||
|  | ||||
| --- Update | ||||
|  | ||||
|  | ||||
| update : Msg -> TableAction | ||||
| update msg = | ||||
|     case msg of | ||||
|         SelectRunConfig cfg -> | ||||
|             Selected cfg | ||||
|  | ||||
|  | ||||
|  | ||||
| --- View | ||||
|  | ||||
|  | ||||
| view : Texts -> List AddonRunConfig -> Html Msg | ||||
| view texts addons = | ||||
|     table [ class S.tableMain ] | ||||
|         [ thead [] | ||||
|             [ tr [] | ||||
|                 [ th [ class "" ] [] | ||||
|                 , th [ class "text-left" ] | ||||
|                     [ text texts.basics.name | ||||
|                     ] | ||||
|                 , th [ class "px-2 text-center" ] [ text texts.enabled ] | ||||
|                 , th [ class "px-2 text-left" ] [ text texts.trigger ] | ||||
|                 , th [ class "px-2 text-center" ] [ text "# Addons" ] | ||||
|                 ] | ||||
|             ] | ||||
|         , tbody [] | ||||
|             (List.map (renderRunConfigLine texts) addons) | ||||
|         ] | ||||
|  | ||||
|  | ||||
| renderRunConfigLine : Texts -> AddonRunConfig -> Html Msg | ||||
| renderRunConfigLine texts cfg = | ||||
|     tr | ||||
|         [ class S.tableRow | ||||
|         ] | ||||
|         [ B.editLinkTableCell texts.basics.edit (SelectRunConfig cfg) | ||||
|         , td [ class "text-left py-4 md:py-2" ] | ||||
|             [ text cfg.name | ||||
|             ] | ||||
|         , td [ class "w-px whitespace-nowrap px-2 text-center" ] | ||||
|             [ Util.Html.checkbox2 cfg.enabled | ||||
|             ] | ||||
|         , td [ class "px-2 text-left" ] | ||||
|             [ text (String.join ", " cfg.trigger) | ||||
|             ] | ||||
|         , td [ class "px-2 text-center" ] | ||||
|             [ text (String.fromInt <| List.length cfg.addons) | ||||
|             ] | ||||
|         ] | ||||
| @@ -28,6 +28,8 @@ module Comp.ItemDetail.Model exposing | ||||
|     , resultModelCmdSub | ||||
|     ) | ||||
|  | ||||
| import Api.Model.AddonRunConfig exposing (AddonRunConfig) | ||||
| import Api.Model.AddonRunConfigList exposing (AddonRunConfigList) | ||||
| import Api.Model.BasicResult exposing (BasicResult) | ||||
| import Api.Model.CustomField exposing (CustomField) | ||||
| import Api.Model.EquipmentList exposing (EquipmentList) | ||||
| @@ -72,6 +74,7 @@ import Set exposing (Set) | ||||
|  | ||||
| type alias Model = | ||||
|     { item : ItemDetail | ||||
|     , runConfigs : List AddonRunConfig | ||||
|     , visibleAttach : Int | ||||
|     , attachMenuOpen : Bool | ||||
|     , menuOpen : Bool | ||||
| @@ -123,6 +126,9 @@ type alias Model = | ||||
|     , viewMode : ViewMode | ||||
|     , showQrModel : ShowQrModel | ||||
|     , itemLinkModel : Comp.ItemLinkForm.Model | ||||
|     , showRunAddon : Bool | ||||
|     , addonRunConfigDropdown : Comp.Dropdown.Model AddonRunConfig | ||||
|     , addonRunSubmitted : Bool | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -204,6 +210,7 @@ isEditNotes field = | ||||
| emptyModel : Model | ||||
| emptyModel = | ||||
|     { item = Api.Model.ItemDetail.empty | ||||
|     , runConfigs = [] | ||||
|     , visibleAttach = 0 | ||||
|     , attachMenuOpen = False | ||||
|     , menuOpen = False | ||||
| @@ -259,6 +266,9 @@ emptyModel = | ||||
|     , viewMode = SimpleView | ||||
|     , showQrModel = initShowQrModel | ||||
|     , itemLinkModel = Comp.ItemLinkForm.emptyModel | ||||
|     , showRunAddon = False | ||||
|     , addonRunConfigDropdown = Comp.Dropdown.makeSingle | ||||
|     , addonRunSubmitted = False | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -373,6 +383,12 @@ type Msg | ||||
|     | SetNameMsg Comp.SimpleTextInput.Msg | ||||
|     | ToggleSelectItem | ||||
|     | ItemLinkFormMsg Comp.ItemLinkForm.Msg | ||||
|     | ToggleShowRunAddon | ||||
|     | LoadRunConfigResp (Result Http.Error AddonRunConfigList) | ||||
|     | RunAddonMsg (Comp.Dropdown.Msg AddonRunConfig) | ||||
|     | RunSelectedAddon | ||||
|     | RunAddonResp (Result Http.Error BasicResult) | ||||
|     | SetAddonRunSubmitted Bool | ||||
|  | ||||
|  | ||||
| type SaveNameState | ||||
|   | ||||
							
								
								
									
										78
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/RunAddonForm.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/RunAddonForm.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Comp.ItemDetail.RunAddonForm exposing (..) | ||||
|  | ||||
| import Comp.Basic as B | ||||
| import Comp.Dropdown | ||||
| import Comp.ItemDetail.Model exposing (..) | ||||
| import Comp.MenuBar as MB | ||||
| import Data.DropdownStyle as DS | ||||
| import Data.UiSettings exposing (UiSettings) | ||||
| import Html exposing (Html, div, h3, label, text) | ||||
| import Html.Attributes exposing (class, classList, title) | ||||
| import Html.Events exposing (onClick) | ||||
| import Messages.Comp.ItemDetail.RunAddonForm exposing (Texts) | ||||
| import Styles as S | ||||
|  | ||||
|  | ||||
| view : Texts -> UiSettings -> Model -> Html Msg | ||||
| view texts uiSettings model = | ||||
|     let | ||||
|         viewSettings = | ||||
|             { makeOption = \cfg -> { text = cfg.name, additional = "" } | ||||
|             , placeholder = texts.basics.selectPlaceholder | ||||
|             , labelColor = \_ -> \_ -> "" | ||||
|             , style = DS.mainStyle | ||||
|             } | ||||
|  | ||||
|         runDisabled = | ||||
|             Comp.Dropdown.getSelected model.addonRunConfigDropdown | ||||
|                 |> List.isEmpty | ||||
|     in | ||||
|     div | ||||
|         [ classList [ ( "hidden", not model.showRunAddon ) ] | ||||
|         , class "mb-4" | ||||
|         ] | ||||
|         [ h3 [ class S.header3 ] [ text texts.runAddon ] | ||||
|         , div [ class "my-2" ] | ||||
|             [ label [ class S.inputLabel ] | ||||
|                 [ text texts.addonRunConfig | ||||
|                 ] | ||||
|             , Html.map RunAddonMsg (Comp.Dropdown.view2 viewSettings uiSettings model.addonRunConfigDropdown) | ||||
|             ] | ||||
|         , div [ class "my-2" ] | ||||
|             [ MB.view | ||||
|                 { start = | ||||
|                     [ MB.CustomElement <| | ||||
|                         B.primaryButton | ||||
|                             { label = "Run" | ||||
|                             , icon = | ||||
|                                 if model.addonRunSubmitted then | ||||
|                                     "fa fa-check" | ||||
|  | ||||
|                                 else | ||||
|                                     "fa fa-play" | ||||
|                             , disabled = runDisabled | ||||
|                             , handler = onClick RunSelectedAddon | ||||
|                             , attrs = | ||||
|                                 [ title texts.runAddonTitle | ||||
|                                 ] | ||||
|                             } | ||||
|                     , MB.SecondaryButton | ||||
|                         { label = texts.basics.cancel | ||||
|                         , icon = Just "fa fa-times" | ||||
|                         , tagger = ToggleShowRunAddon | ||||
|                         , title = "" | ||||
|                         } | ||||
|                     ] | ||||
|                 , end = [] | ||||
|                 , rootClasses = "text-sm mt-1" | ||||
|                 , sticky = False | ||||
|                 } | ||||
|             ] | ||||
|         ] | ||||
| @@ -56,6 +56,7 @@ import Comp.PersonForm | ||||
| import Comp.SentMails | ||||
| import Comp.SimpleTextInput | ||||
| import Comp.TagDropdown | ||||
| import Data.AddonTrigger | ||||
| import Data.CustomFieldChange exposing (CustomFieldChange(..)) | ||||
| import Data.Direction | ||||
| import Data.Environment as Env | ||||
| @@ -75,7 +76,9 @@ import Html5.DragDrop as DD | ||||
| import Http | ||||
| import Page exposing (Page(..)) | ||||
| import Ports | ||||
| import Process | ||||
| import Set exposing (Set) | ||||
| import Task | ||||
| import Util.File exposing (makeFileId) | ||||
| import Util.List | ||||
| import Util.Maybe | ||||
| @@ -121,9 +124,77 @@ update inav env msg model = | ||||
|                     , Cmd.map ItemMailMsg ic | ||||
|                     , Cmd.map CustomFieldMsg cc | ||||
|                     , Api.getSentMails env.flags model.item.id SentMailsResp | ||||
|                     , Api.addonRunConfigGet env.flags LoadRunConfigResp | ||||
|                     ] | ||||
|                 ) | ||||
|  | ||||
|         LoadRunConfigResp (Ok list) -> | ||||
|             let | ||||
|                 existingItem cfg = | ||||
|                     cfg.enabled | ||||
|                         && (Data.AddonTrigger.fromList cfg.trigger | ||||
|                                 |> List.any ((==) Data.AddonTrigger.ExistingItem) | ||||
|                            ) | ||||
|  | ||||
|                 configs = | ||||
|                     List.filter existingItem list.items | ||||
|  | ||||
|                 dropdown = | ||||
|                     Comp.Dropdown.makeSingleList { options = configs, selected = Nothing } | ||||
|             in | ||||
|             resultModel { model | runConfigs = configs, addonRunConfigDropdown = dropdown } | ||||
|  | ||||
|         RunAddonMsg lm -> | ||||
|             let | ||||
|                 ( dd, dc ) = | ||||
|                     Comp.Dropdown.update lm model.addonRunConfigDropdown | ||||
|             in | ||||
|             resultModelCmd ( { model | addonRunConfigDropdown = dd }, Cmd.map RunAddonMsg dc ) | ||||
|  | ||||
|         RunSelectedAddon -> | ||||
|             let | ||||
|                 configs = | ||||
|                     Comp.Dropdown.getSelected model.addonRunConfigDropdown | ||||
|                         |> List.map .id | ||||
|  | ||||
|                 payload = | ||||
|                     { itemId = model.item.id | ||||
|                     , additionalItems = [] | ||||
|                     , addonRunConfigIds = configs | ||||
|                     } | ||||
|  | ||||
|                 ( dd, _ ) = | ||||
|                     Comp.Dropdown.update (Comp.Dropdown.SetSelection []) model.addonRunConfigDropdown | ||||
|             in | ||||
|             case configs of | ||||
|                 [] -> | ||||
|                     resultModel model | ||||
|  | ||||
|                 _ -> | ||||
|                     resultModelCmd | ||||
|                         ( { model | addonRunConfigDropdown = dd } | ||||
|                         , Api.addonRunExistingItem env.flags payload RunAddonResp | ||||
|                         ) | ||||
|  | ||||
|         LoadRunConfigResp (Err _) -> | ||||
|             resultModel model | ||||
|  | ||||
|         RunAddonResp (Ok res) -> | ||||
|             if res.success then | ||||
|                 resultModelCmd | ||||
|                     ( { model | addonRunSubmitted = True } | ||||
|                     , Process.sleep 1200 |> Task.perform (\_ -> SetAddonRunSubmitted False) | ||||
|                     ) | ||||
|  | ||||
|             else | ||||
|                 resultModel model | ||||
|  | ||||
|         RunAddonResp (Err _) -> | ||||
|             resultModel model | ||||
|  | ||||
|         SetAddonRunSubmitted flag -> | ||||
|             resultModel { model | addonRunSubmitted = flag } | ||||
|  | ||||
|         SetItem item -> | ||||
|             let | ||||
|                 res1 = | ||||
| @@ -1638,6 +1709,9 @@ update inav env msg model = | ||||
|                 , Sub.map ItemLinkFormMsg ils | ||||
|                 ) | ||||
|  | ||||
|         ToggleShowRunAddon -> | ||||
|             resultModel { model | showRunAddon = not model.showRunAddon, mobileItemMenuOpen = False } | ||||
|  | ||||
|  | ||||
|  | ||||
| --- Helper | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import Comp.ItemDetail.Model | ||||
|         , isShowQrItem | ||||
|         ) | ||||
| import Comp.ItemDetail.Notes | ||||
| import Comp.ItemDetail.RunAddonForm | ||||
| import Comp.ItemDetail.ShowQrCode | ||||
| import Comp.ItemDetail.SingleAttachment | ||||
| import Comp.ItemLinkForm | ||||
| @@ -177,6 +178,24 @@ menuBar texts inav env model = | ||||
|                     ] | ||||
|                     [ Icons.addFilesIcon2 "" | ||||
|                     ] | ||||
|             , MB.CustomElement <| | ||||
|                 a | ||||
|                     [ classList | ||||
|                         [ ( "bg-gray-200 dark:bg-slate-600", model.showRunAddon ) | ||||
|                         , ( "hidden", not env.flags.config.addonsEnabled || List.isEmpty model.runConfigs ) | ||||
|                         , ( "hidden md:block", env.flags.config.addonsEnabled && not (List.isEmpty model.runConfigs) ) | ||||
|                         ] | ||||
|                     , if model.showRunAddon then | ||||
|                         title texts.close | ||||
|  | ||||
|                       else | ||||
|                         title texts.runAddonTitle | ||||
|                     , onClick ToggleShowRunAddon | ||||
|                     , class S.secondaryBasicButton | ||||
|                     , href "#" | ||||
|                     ] | ||||
|                     [ Icons.addonIcon "" | ||||
|                     ] | ||||
|             , MB.CustomElement <| | ||||
|                 a | ||||
|                     [ classList | ||||
| @@ -248,6 +267,15 @@ menuBar texts inav env model = | ||||
|                             , onClick AddFilesToggle | ||||
|                             ] | ||||
|                       } | ||||
|                     , { icon = Icons.addonIcon "" | ||||
|                       , label = texts.runAddonLabel | ||||
|                       , disabled = False | ||||
|                       , attrs = | ||||
|                             [ href "#" | ||||
|                             , onClick ToggleShowRunAddon | ||||
|                             , classList [ ( "hidden", not env.flags.config.addonsEnabled ) ] | ||||
|                             ] | ||||
|                       } | ||||
|                     , { icon = Icons.showQrIcon "" | ||||
|                       , label = texts.showQrCode | ||||
|                       , disabled = False | ||||
| @@ -402,6 +430,11 @@ itemActions texts flags settings model classes = | ||||
|             (S.border ++ " mb-4") | ||||
|             model | ||||
|             (Comp.ItemDetail.ShowQrCode.Item model.item.id) | ||||
|         , if flags.config.addonsEnabled then | ||||
|             Comp.ItemDetail.RunAddonForm.view texts.runAddonForm settings model | ||||
|  | ||||
|           else | ||||
|             span [ class "hidden" ] [] | ||||
|         ] | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										59
									
								
								modules/webapp/src/main/elm/Data/AddonTrigger.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								modules/webapp/src/main/elm/Data/AddonTrigger.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Data.AddonTrigger exposing (..) | ||||
|  | ||||
| -- A copy of docspell.addons.AddonTrigger.scala | ||||
|  | ||||
|  | ||||
| type AddonTrigger | ||||
|     = FinalProcessItem | ||||
|     | FinalReprocessItem | ||||
|     | Scheduled | ||||
|     | ExistingItem | ||||
|  | ||||
|  | ||||
| all : List AddonTrigger | ||||
| all = | ||||
|     [ FinalProcessItem | ||||
|     , FinalReprocessItem | ||||
|     , Scheduled | ||||
|     , ExistingItem | ||||
|     ] | ||||
|  | ||||
|  | ||||
| asString : AddonTrigger -> String | ||||
| asString t = | ||||
|     case t of | ||||
|         FinalProcessItem -> | ||||
|             "final-process-item" | ||||
|  | ||||
|         FinalReprocessItem -> | ||||
|             "final-reprocess-item" | ||||
|  | ||||
|         Scheduled -> | ||||
|             "scheduled" | ||||
|  | ||||
|         ExistingItem -> | ||||
|             "existing-item" | ||||
|  | ||||
|  | ||||
| fromString : String -> Maybe AddonTrigger | ||||
| fromString s = | ||||
|     let | ||||
|         name = | ||||
|             String.toLower s | ||||
|  | ||||
|         x = | ||||
|             List.filter (\e -> asString e == name) all | ||||
|     in | ||||
|     List.head x | ||||
|  | ||||
|  | ||||
| fromList : List String -> List AddonTrigger | ||||
| fromList list = | ||||
|     List.filterMap fromString list | ||||
| @@ -8,6 +8,7 @@ | ||||
| module Data.CalEvent exposing | ||||
|     ( CalEvent | ||||
|     , everyMonth | ||||
|     , everyMonthTz | ||||
|     , fromEvent | ||||
|     , makeEvent | ||||
|     ) | ||||
| @@ -29,7 +30,12 @@ type alias CalEvent = | ||||
|  | ||||
| everyMonth : CalEvent | ||||
| everyMonth = | ||||
|     CalEvent Nothing "*" "*" "01" "00" "00" Data.TimeZone.utc | ||||
|     everyMonthTz Data.TimeZone.utc | ||||
|  | ||||
|  | ||||
| everyMonthTz : TimeZone -> CalEvent | ||||
| everyMonthTz tz = | ||||
|     CalEvent Nothing "*" "*" "01" "00" "00" tz | ||||
|  | ||||
|  | ||||
| makeEvent : CalEvent -> String | ||||
|   | ||||
| @@ -38,6 +38,7 @@ type alias Config = | ||||
|     , downloadAllMaxFiles : Int | ||||
|     , downloadAllMaxSize : Int | ||||
|     , openIdAuth : List OpenIdAuth | ||||
|     , addonsEnabled : Bool | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -8,6 +8,8 @@ | ||||
| module Data.Icons exposing | ||||
|     ( addFiles2 | ||||
|     , addFilesIcon2 | ||||
|     , addonIcon | ||||
|     , addonRunConfigIcon | ||||
|     , concerned | ||||
|     , concerned2 | ||||
|     , concernedIcon | ||||
| @@ -76,7 +78,7 @@ module Data.Icons exposing | ||||
|     ) | ||||
|  | ||||
| import Data.CustomFieldType exposing (CustomFieldType) | ||||
| import Html exposing (Html, i, img) | ||||
| import Html exposing (Html, div, i, img, span) | ||||
| import Html.Attributes exposing (class, src) | ||||
| import Svg | ||||
| import Svg.Attributes as SA | ||||
| @@ -265,6 +267,24 @@ customFieldIcon2 classes = | ||||
|     i [ class (customField2 ++ " " ++ classes) ] [] | ||||
|  | ||||
|  | ||||
| addon : String | ||||
| addon = | ||||
|     "fa fa-puzzle-piece" | ||||
|  | ||||
|  | ||||
| addonIcon : String -> Html msg | ||||
| addonIcon classes = | ||||
|     i [ class (addon ++ " " ++ classes) ] [] | ||||
|  | ||||
|  | ||||
| addonRunConfigIcon : String -> Html msg | ||||
| addonRunConfigIcon classes = | ||||
|     div [ class (classes ++ " inline-block relative margin-auto leading-8") ] | ||||
|         [ i [ class "fa fa-puzzle-piece" ] [] | ||||
|         , i [ class "fa fa-play font-bold absolute text-xs -right-2 top-0" ] [] | ||||
|         ] | ||||
|  | ||||
|  | ||||
| search : String | ||||
| search = | ||||
|     "fa fa-search" | ||||
|   | ||||
| @@ -5,15 +5,34 @@ | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Data.ServerEvent exposing (ServerEvent(..), decode) | ||||
| module Data.ServerEvent exposing (AddonInfo, ServerEvent(..), decode) | ||||
|  | ||||
| import Json.Decode as D | ||||
| import Json.Decode.Pipeline as P | ||||
|  | ||||
|  | ||||
| type ServerEvent | ||||
|     = JobSubmitted String | ||||
|     | JobDone String | ||||
|     | JobsWaiting Int | ||||
|     | AddonInstalled AddonInfo | ||||
|  | ||||
|  | ||||
| type alias AddonInfo = | ||||
|     { success : Bool | ||||
|     , addonId : Maybe String | ||||
|     , addonUrl : Maybe String | ||||
|     , message : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| addonInfoDecoder : D.Decoder AddonInfo | ||||
| addonInfoDecoder = | ||||
|     D.succeed AddonInfo | ||||
|         |> P.required "success" D.bool | ||||
|         |> P.optional "addonId" (D.maybe D.string) Nothing | ||||
|         |> P.optional "addonUrl" (D.maybe D.string) Nothing | ||||
|         |> P.required "message" D.string | ||||
|  | ||||
|  | ||||
| decoder : D.Decoder ServerEvent | ||||
| @@ -43,5 +62,9 @@ decodeTag tag = | ||||
|             D.field "content" D.int | ||||
|                 |> D.map JobsWaiting | ||||
|  | ||||
|         "addon-installed" -> | ||||
|             D.field "content" addonInfoDecoder | ||||
|                 |> D.map AddonInstalled | ||||
|  | ||||
|         _ -> | ||||
|             D.fail ("Unknown tag: " ++ tag) | ||||
|   | ||||
| @@ -0,0 +1,54 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Messages.Comp.AddonArchiveForm exposing | ||||
|     ( Texts | ||||
|     , de | ||||
|     , fr | ||||
|     , gb | ||||
|     ) | ||||
|  | ||||
| import Messages.Basics | ||||
|  | ||||
|  | ||||
| type alias Texts = | ||||
|     { basics : Messages.Basics.Texts | ||||
|     , addonUrl : String | ||||
|     , addonUrlPlaceholder : String | ||||
|     , installInfoText : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| gb : Texts | ||||
| gb = | ||||
|     { basics = Messages.Basics.gb | ||||
|     , addonUrl = "Addon URL" | ||||
|     , addonUrlPlaceholder = "e.g. https://github.com/some-user/project/refs/tags/1.0.zip" | ||||
|     , installInfoText = "Only urls to remote addon zip files are supported." | ||||
|     } | ||||
|  | ||||
|  | ||||
| de : Texts | ||||
| de = | ||||
|     { basics = Messages.Basics.de | ||||
|     , addonUrl = "Addon URL" | ||||
|     , addonUrlPlaceholder = "z.B. https://github.com/some-user/project/refs/tags/1.0.zip" | ||||
|     , installInfoText = "Nur URLs to externen zip Dateien werden unterstützt." | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| -- TODO: translate-fr | ||||
|  | ||||
|  | ||||
| fr : Texts | ||||
| fr = | ||||
|     { basics = Messages.Basics.fr | ||||
|     , addonUrl = "Addon URL" | ||||
|     , addonUrlPlaceholder = "p.e. https://github.com/some-user/project/refs/tags/1.0.zip" | ||||
|     , installInfoText = "Only urls to remote addon zip files are supported." | ||||
|     } | ||||
| @@ -0,0 +1,86 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Messages.Comp.AddonArchiveManage exposing | ||||
|     ( Texts | ||||
|     , de | ||||
|     , fr | ||||
|     , gb | ||||
|     ) | ||||
|  | ||||
| import Http | ||||
| import Messages.Basics | ||||
| import Messages.Comp.AddonArchiveForm | ||||
| import Messages.Comp.AddonArchiveTable | ||||
| import Messages.Comp.HttpError | ||||
|  | ||||
|  | ||||
| type alias Texts = | ||||
|     { basics : Messages.Basics.Texts | ||||
|     , addonArchiveTable : Messages.Comp.AddonArchiveTable.Texts | ||||
|     , addonArchiveForm : Messages.Comp.AddonArchiveForm.Texts | ||||
|     , httpError : Http.Error -> String | ||||
|     , newAddonArchive : String | ||||
|     , reallyDeleteAddonArchive : String | ||||
|     , createNewAddonArchive : String | ||||
|     , deleteThisAddonArchive : String | ||||
|     , correctFormErrors : String | ||||
|     , installNow : String | ||||
|     , updateNow : String | ||||
|     , description : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| gb : Texts | ||||
| gb = | ||||
|     { basics = Messages.Basics.gb | ||||
|     , addonArchiveTable = Messages.Comp.AddonArchiveTable.gb | ||||
|     , addonArchiveForm = Messages.Comp.AddonArchiveForm.gb | ||||
|     , httpError = Messages.Comp.HttpError.gb | ||||
|     , newAddonArchive = "New Addon" | ||||
|     , reallyDeleteAddonArchive = "Really delete this Addon?" | ||||
|     , createNewAddonArchive = "Install new Addon" | ||||
|     , deleteThisAddonArchive = "Delete this Addon" | ||||
|     , correctFormErrors = "Please correct the errors in the form." | ||||
|     , installNow = "Install Addon" | ||||
|     , updateNow = "Update Addon" | ||||
|     , description = "Description" | ||||
|     } | ||||
|  | ||||
|  | ||||
| de : Texts | ||||
| de = | ||||
|     { basics = Messages.Basics.de | ||||
|     , addonArchiveTable = Messages.Comp.AddonArchiveTable.de | ||||
|     , addonArchiveForm = Messages.Comp.AddonArchiveForm.de | ||||
|     , httpError = Messages.Comp.HttpError.de | ||||
|     , newAddonArchive = "Neues Addon" | ||||
|     , reallyDeleteAddonArchive = "Dieses Addon wirklich entfernen?" | ||||
|     , createNewAddonArchive = "Neues Addon installieren" | ||||
|     , deleteThisAddonArchive = "Addon löschen" | ||||
|     , correctFormErrors = "Bitte korrigiere die Fehler im Formular." | ||||
|     , installNow = "Addon Installieren" | ||||
|     , updateNow = "Addon aktualisieren" | ||||
|     , description = "Beschreibung" | ||||
|     } | ||||
|  | ||||
|  | ||||
| fr : Texts | ||||
| fr = | ||||
|     { basics = Messages.Basics.fr | ||||
|     , addonArchiveTable = Messages.Comp.AddonArchiveTable.fr | ||||
|     , addonArchiveForm = Messages.Comp.AddonArchiveForm.fr | ||||
|     , httpError = Messages.Comp.HttpError.fr | ||||
|     , newAddonArchive = "Nouveau favori" | ||||
|     , reallyDeleteAddonArchive = "Confirmer la suppression de ce favori ?" | ||||
|     , createNewAddonArchive = "Créer un nouveau favori" | ||||
|     , deleteThisAddonArchive = "Supprimer ce favori" | ||||
|     , correctFormErrors = "Veuillez corriger les erreurs du formulaire" | ||||
|     , installNow = "Installation de l'addon" | ||||
|     , updateNow = "Actualiser l'addon" | ||||
|     , description = "Description" | ||||
|     } | ||||
| @@ -0,0 +1,42 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Messages.Comp.AddonArchiveTable exposing | ||||
|     ( Texts | ||||
|     , de | ||||
|     , fr | ||||
|     , gb | ||||
|     ) | ||||
|  | ||||
| import Messages.Basics | ||||
|  | ||||
|  | ||||
| type alias Texts = | ||||
|     { basics : Messages.Basics.Texts | ||||
|     , version : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| gb : Texts | ||||
| gb = | ||||
|     { basics = Messages.Basics.gb | ||||
|     , version = "Version" | ||||
|     } | ||||
|  | ||||
|  | ||||
| de : Texts | ||||
| de = | ||||
|     { basics = Messages.Basics.de | ||||
|     , version = "Version" | ||||
|     } | ||||
|  | ||||
|  | ||||
| fr : Texts | ||||
| fr = | ||||
|     { basics = Messages.Basics.fr | ||||
|     , version = "Version" | ||||
|     } | ||||
							
								
								
									
										108
									
								
								modules/webapp/src/main/elm/Messages/Comp/AddonRunConfigForm.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								modules/webapp/src/main/elm/Messages/Comp/AddonRunConfigForm.elm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Messages.Comp.AddonRunConfigForm exposing | ||||
|     ( Texts | ||||
|     , de | ||||
|     , fr | ||||
|     , gb | ||||
|     ) | ||||
|  | ||||
| import Data.TimeZone exposing (TimeZone) | ||||
| import Messages.Basics | ||||
| import Messages.Comp.CalEventInput | ||||
|  | ||||
|  | ||||
| type alias Texts = | ||||
|     { basics : Messages.Basics.Texts | ||||
|     , calEventInput : Messages.Comp.CalEventInput.Texts | ||||
|     , enableDisable : String | ||||
|     , chooseName : String | ||||
|     , impersonateUser : String | ||||
|     , triggerRun : String | ||||
|     , schedule : String | ||||
|     , addons : String | ||||
|     , includedAddons : String | ||||
|     , add : String | ||||
|     , readMore : String | ||||
|     , readLess : String | ||||
|     , arguments : String | ||||
|     , update : String | ||||
|     , argumentsUpdated : String | ||||
|     , configureTitle : String | ||||
|     , configureLabel : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| gb : TimeZone -> Texts | ||||
| gb tz = | ||||
|     { basics = Messages.Basics.gb | ||||
|     , calEventInput = Messages.Comp.CalEventInput.gb tz | ||||
|     , enableDisable = "Enable or disable this run configuration." | ||||
|     , chooseName = "Choose a name…" | ||||
|     , impersonateUser = "Run on behalf of user" | ||||
|     , triggerRun = "Trigger Run" | ||||
|     , schedule = "Schedule" | ||||
|     , addons = "Addons" | ||||
|     , includedAddons = "Included addons" | ||||
|     , add = "Add" | ||||
|     , readMore = "Read more" | ||||
|     , readLess = "Read less" | ||||
|     , arguments = "Arguments" | ||||
|     , update = "Update" | ||||
|     , argumentsUpdated = "Arguments updated" | ||||
|     , configureTitle = "Configure this addon" | ||||
|     , configureLabel = "Configure" | ||||
|     } | ||||
|  | ||||
|  | ||||
| de : TimeZone -> Texts | ||||
| de tz = | ||||
|     { basics = Messages.Basics.de | ||||
|     , calEventInput = Messages.Comp.CalEventInput.de tz | ||||
|     , enableDisable = "Konfiguration aktivieren oder deaktivieren" | ||||
|     , chooseName = "Name der Konfiguration…" | ||||
|     , impersonateUser = "Als Benutzer ausführen" | ||||
|     , triggerRun = "Auslöser" | ||||
|     , schedule = "Zeitplan" | ||||
|     , addons = "Addons" | ||||
|     , includedAddons = "Gewählte Addons" | ||||
|     , add = "Hinzufügen" | ||||
|     , readMore = "Mehr" | ||||
|     , readLess = "Weniger" | ||||
|     , arguments = "Argumente" | ||||
|     , update = "Aktualisieren" | ||||
|     , argumentsUpdated = "Argumente aktualisiert" | ||||
|     , configureTitle = "Konfiguriere dieses Addon" | ||||
|     , configureLabel = "Konfigurieren" | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| -- TODO: translate-fr | ||||
|  | ||||
|  | ||||
| fr : TimeZone -> Texts | ||||
| fr tz = | ||||
|     { basics = Messages.Basics.fr | ||||
|     , calEventInput = Messages.Comp.CalEventInput.fr tz | ||||
|     , enableDisable = "Activer ou désactiver cette tâche." | ||||
|     , chooseName = "Choose a name…" | ||||
|     , impersonateUser = "Impersonate user" | ||||
|     , triggerRun = "Trigger Run" | ||||
|     , schedule = "Programmation" | ||||
|     , addons = "Addons" | ||||
|     , includedAddons = "Included addons" | ||||
|     , add = "Ajouter" | ||||
|     , readMore = "Read more" | ||||
|     , readLess = "Read less" | ||||
|     , arguments = "Arguments" | ||||
|     , update = "Update" | ||||
|     , argumentsUpdated = "Arguments updated" | ||||
|     , configureTitle = "Configure this addon" | ||||
|     , configureLabel = "Configure" | ||||
|     } | ||||
| @@ -0,0 +1,79 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Messages.Comp.AddonRunConfigManage exposing | ||||
|     ( Texts | ||||
|     , de | ||||
|     , fr | ||||
|     , gb | ||||
|     ) | ||||
|  | ||||
| import Data.TimeZone exposing (TimeZone) | ||||
| import Http | ||||
| import Messages.Basics | ||||
| import Messages.Comp.AddonRunConfigForm | ||||
| import Messages.Comp.AddonRunConfigTable | ||||
| import Messages.Comp.HttpError | ||||
|  | ||||
|  | ||||
| type alias Texts = | ||||
|     { basics : Messages.Basics.Texts | ||||
|     , addonArchiveTable : Messages.Comp.AddonRunConfigTable.Texts | ||||
|     , addonArchiveForm : Messages.Comp.AddonRunConfigForm.Texts | ||||
|     , httpError : Http.Error -> String | ||||
|     , newAddonRunConfig : String | ||||
|     , reallyDeleteAddonRunConfig : String | ||||
|     , createNewAddonRunConfig : String | ||||
|     , deleteThisAddonRunConfig : String | ||||
|     , correctFormErrors : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| gb : TimeZone -> Texts | ||||
| gb tz = | ||||
|     { basics = Messages.Basics.gb | ||||
|     , addonArchiveTable = Messages.Comp.AddonRunConfigTable.gb | ||||
|     , addonArchiveForm = Messages.Comp.AddonRunConfigForm.gb tz | ||||
|     , httpError = Messages.Comp.HttpError.gb | ||||
|     , newAddonRunConfig = "New" | ||||
|     , reallyDeleteAddonRunConfig = "Really delete this run config?" | ||||
|     , createNewAddonRunConfig = "Create a new run configuration" | ||||
|     , deleteThisAddonRunConfig = "Delete this run configuration" | ||||
|     , correctFormErrors = "Please correct the errors in the form." | ||||
|     } | ||||
|  | ||||
|  | ||||
| de : TimeZone -> Texts | ||||
| de tz = | ||||
|     { basics = Messages.Basics.de | ||||
|     , addonArchiveTable = Messages.Comp.AddonRunConfigTable.de | ||||
|     , addonArchiveForm = Messages.Comp.AddonRunConfigForm.de tz | ||||
|     , httpError = Messages.Comp.HttpError.de | ||||
|     , newAddonRunConfig = "Neu" | ||||
|     , reallyDeleteAddonRunConfig = "Dieses Konfiguration wirklich entfernen?" | ||||
|     , createNewAddonRunConfig = "Neue Run-Konfiguration erstellen" | ||||
|     , deleteThisAddonRunConfig = "Run-Konfiguration löschen" | ||||
|     , correctFormErrors = "Bitte korrigiere die Fehler im Formular." | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| --- TODO translate-fr | ||||
|  | ||||
|  | ||||
| fr : TimeZone -> Texts | ||||
| fr tz = | ||||
|     { basics = Messages.Basics.fr | ||||
|     , addonArchiveTable = Messages.Comp.AddonRunConfigTable.fr | ||||
|     , addonArchiveForm = Messages.Comp.AddonRunConfigForm.fr tz | ||||
|     , httpError = Messages.Comp.HttpError.fr | ||||
|     , newAddonRunConfig = "Nouveau favori" | ||||
|     , reallyDeleteAddonRunConfig = "Confirmer la suppression de ce favori ?" | ||||
|     , createNewAddonRunConfig = "Créer un nouveau favori" | ||||
|     , deleteThisAddonRunConfig = "Supprimer ce favori" | ||||
|     , correctFormErrors = "Veuillez corriger les erreurs du formulaire" | ||||
|     } | ||||
| @@ -0,0 +1,50 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Messages.Comp.AddonRunConfigTable exposing | ||||
|     ( Texts | ||||
|     , de | ||||
|     , fr | ||||
|     , gb | ||||
|     ) | ||||
|  | ||||
| import Messages.Basics | ||||
|  | ||||
|  | ||||
| type alias Texts = | ||||
|     { basics : Messages.Basics.Texts | ||||
|     , enabled : String | ||||
|     , trigger : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| gb : Texts | ||||
| gb = | ||||
|     { basics = Messages.Basics.gb | ||||
|     , enabled = "Enabled" | ||||
|     , trigger = "Triggered" | ||||
|     } | ||||
|  | ||||
|  | ||||
| de : Texts | ||||
| de = | ||||
|     { basics = Messages.Basics.de | ||||
|     , enabled = "Aktive" | ||||
|     , trigger = "Auslöser" | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| -- TODO translate-fr | ||||
|  | ||||
|  | ||||
| fr : Texts | ||||
| fr = | ||||
|     { basics = Messages.Basics.fr | ||||
|     , enabled = "Enabled" | ||||
|     , trigger = "Triggered" | ||||
|     } | ||||
| @@ -20,6 +20,7 @@ import Messages.Comp.ItemDetail.AddFilesForm | ||||
| import Messages.Comp.ItemDetail.ConfirmModal | ||||
| import Messages.Comp.ItemDetail.ItemInfoHeader | ||||
| import Messages.Comp.ItemDetail.Notes | ||||
| import Messages.Comp.ItemDetail.RunAddonForm | ||||
| import Messages.Comp.ItemDetail.SingleAttachment | ||||
| import Messages.Comp.ItemLinkForm | ||||
| import Messages.Comp.ItemMail | ||||
| @@ -38,6 +39,7 @@ type alias Texts = | ||||
|     , detailEdit : Messages.Comp.DetailEdit.Texts | ||||
|     , confirmModal : Messages.Comp.ItemDetail.ConfirmModal.Texts | ||||
|     , itemLinkForm : Messages.Comp.ItemLinkForm.Texts | ||||
|     , runAddonForm : Messages.Comp.ItemDetail.RunAddonForm.Texts | ||||
|     , httpError : Http.Error -> String | ||||
|     , key : String | ||||
|     , backToSearchResults : String | ||||
| @@ -64,6 +66,8 @@ type alias Texts = | ||||
|     , selectItem : String | ||||
|     , deselectItem : String | ||||
|     , relatedItems : String | ||||
|     , runAddonLabel : String | ||||
|     , runAddonTitle : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -78,6 +82,7 @@ gb tz = | ||||
|     , detailEdit = Messages.Comp.DetailEdit.gb | ||||
|     , confirmModal = Messages.Comp.ItemDetail.ConfirmModal.gb | ||||
|     , itemLinkForm = Messages.Comp.ItemLinkForm.gb tz | ||||
|     , runAddonForm = Messages.Comp.ItemDetail.RunAddonForm.gb | ||||
|     , httpError = Messages.Comp.HttpError.gb | ||||
|     , key = "Key" | ||||
|     , backToSearchResults = "Back to search results" | ||||
| @@ -104,6 +109,8 @@ gb tz = | ||||
|     , selectItem = "Select this item" | ||||
|     , deselectItem = "Deselect this item" | ||||
|     , relatedItems = "Linked items" | ||||
|     , runAddonLabel = "Run addon" | ||||
|     , runAddonTitle = "Run an addon on this item" | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -118,6 +125,7 @@ de tz = | ||||
|     , detailEdit = Messages.Comp.DetailEdit.de | ||||
|     , confirmModal = Messages.Comp.ItemDetail.ConfirmModal.de | ||||
|     , itemLinkForm = Messages.Comp.ItemLinkForm.de tz | ||||
|     , runAddonForm = Messages.Comp.ItemDetail.RunAddonForm.de | ||||
|     , httpError = Messages.Comp.HttpError.de | ||||
|     , key = "Taste" | ||||
|     , backToSearchResults = "Zurück zur Suche" | ||||
| @@ -144,6 +152,8 @@ de tz = | ||||
|     , selectItem = "Zur Auswahl hinzufügen" | ||||
|     , deselectItem = "Aus Auswahl entfernen" | ||||
|     , relatedItems = "Verknüpfte Dokumente" | ||||
|     , runAddonLabel = "Addon ausführen" | ||||
|     , runAddonTitle = "Addons für dieses Dokument ausführen" | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -158,6 +168,7 @@ fr tz = | ||||
|     , detailEdit = Messages.Comp.DetailEdit.fr | ||||
|     , confirmModal = Messages.Comp.ItemDetail.ConfirmModal.fr | ||||
|     , itemLinkForm = Messages.Comp.ItemLinkForm.fr tz | ||||
|     , runAddonForm = Messages.Comp.ItemDetail.RunAddonForm.fr | ||||
|     , httpError = Messages.Comp.HttpError.fr | ||||
|     , key = "Clé" | ||||
|     , backToSearchResults = "Retour aux résultat de recherche" | ||||
| @@ -184,4 +195,10 @@ fr tz = | ||||
|     , selectItem = "Sélectionner  ce document" | ||||
|     , deselectItem = "Désélectionner  ce document" | ||||
|     , relatedItems = "Documents associés" | ||||
|     , runAddonLabel = "Run addon" | ||||
|     , runAddonTitle = "Run an addon on this item" | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| -- TODO translate-fr | ||||
|   | ||||
| @@ -0,0 +1,49 @@ | ||||
| {- | ||||
|    Copyright 2020 Eike K. & Contributors | ||||
|  | ||||
|    SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| -} | ||||
|  | ||||
|  | ||||
| module Messages.Comp.ItemDetail.RunAddonForm exposing (Texts, de, fr, gb) | ||||
|  | ||||
| import Messages.Basics | ||||
|  | ||||
|  | ||||
| type alias Texts = | ||||
|     { basics : Messages.Basics.Texts | ||||
|     , runAddon : String | ||||
|     , addonRunConfig : String | ||||
|     , runAddonTitle : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| gb : Texts | ||||
| gb = | ||||
|     { basics = Messages.Basics.gb | ||||
|     , runAddon = "Run an addon" | ||||
|     , addonRunConfig = "Addon run configuration" | ||||
|     , runAddonTitle = "Run the selected addon on this item." | ||||
|     } | ||||
|  | ||||
|  | ||||
| de : Texts | ||||
| de = | ||||
|     { basics = Messages.Basics.de | ||||
|     , runAddon = "Addon ausführen" | ||||
|     , addonRunConfig = "Addon Konfiguration" | ||||
|     , runAddonTitle = "Run the selected addon on this item." | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| -- TODO: translate-fr | ||||
|  | ||||
|  | ||||
| fr : Texts | ||||
| fr = | ||||
|     { basics = Messages.Basics.fr | ||||
|     , runAddon = "Run an addon" | ||||
|     , addonRunConfig = "Addon run configuration" | ||||
|     , runAddonTitle = "Run the selected addon on this item." | ||||
|     } | ||||
| @@ -14,6 +14,8 @@ module Messages.Page.ManageData exposing | ||||
|  | ||||
| import Data.TimeZone exposing (TimeZone) | ||||
| import Messages.Basics | ||||
| import Messages.Comp.AddonArchiveManage | ||||
| import Messages.Comp.AddonRunConfigManage | ||||
| import Messages.Comp.BookmarkManage | ||||
| import Messages.Comp.CustomFieldManage | ||||
| import Messages.Comp.EquipmentManage | ||||
| @@ -32,8 +34,12 @@ type alias Texts = | ||||
|     , folderManage : Messages.Comp.FolderManage.Texts | ||||
|     , customFieldManage : Messages.Comp.CustomFieldManage.Texts | ||||
|     , bookmarkManage : Messages.Comp.BookmarkManage.Texts | ||||
|     , addonArchiveManage : Messages.Comp.AddonArchiveManage.Texts | ||||
|     , addonRunConfigManage : Messages.Comp.AddonRunConfigManage.Texts | ||||
|     , manageData : String | ||||
|     , bookmarks : String | ||||
|     , addonArchives : String | ||||
|     , addonRunConfigs : String | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -47,8 +53,12 @@ gb tz = | ||||
|     , folderManage = Messages.Comp.FolderManage.gb tz | ||||
|     , customFieldManage = Messages.Comp.CustomFieldManage.gb tz | ||||
|     , bookmarkManage = Messages.Comp.BookmarkManage.gb | ||||
|     , addonArchiveManage = Messages.Comp.AddonArchiveManage.gb | ||||
|     , addonRunConfigManage = Messages.Comp.AddonRunConfigManage.gb tz | ||||
|     , manageData = "Manage Data" | ||||
|     , bookmarks = "Bookmarks" | ||||
|     , addonArchives = "Addons" | ||||
|     , addonRunConfigs = "Addon Run Configurations" | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -62,8 +72,12 @@ de tz = | ||||
|     , folderManage = Messages.Comp.FolderManage.de tz | ||||
|     , customFieldManage = Messages.Comp.CustomFieldManage.de tz | ||||
|     , bookmarkManage = Messages.Comp.BookmarkManage.de | ||||
|     , addonArchiveManage = Messages.Comp.AddonArchiveManage.de | ||||
|     , addonRunConfigManage = Messages.Comp.AddonRunConfigManage.de tz | ||||
|     , manageData = "Daten verwalten" | ||||
|     , bookmarks = "Bookmarks" | ||||
|     , addonArchives = "Addons" | ||||
|     , addonRunConfigs = "Addon Run Configurations" | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -77,6 +91,10 @@ fr tz = | ||||
|     , folderManage = Messages.Comp.FolderManage.fr tz | ||||
|     , customFieldManage = Messages.Comp.CustomFieldManage.fr tz | ||||
|     , bookmarkManage = Messages.Comp.BookmarkManage.fr | ||||
|     , addonArchiveManage = Messages.Comp.AddonArchiveManage.fr | ||||
|     , addonRunConfigManage = Messages.Comp.AddonRunConfigManage.fr tz | ||||
|     , manageData = "Gestion des métadonnées" | ||||
|     , bookmarks = "Favoris" | ||||
|     , addonArchives = "Addons" | ||||
|     , addonRunConfigs = "Addon Run Configurations" | ||||
|     } | ||||
|   | ||||
| @@ -12,6 +12,8 @@ module Page.ManageData.Data exposing | ||||
|     , init | ||||
|     ) | ||||
|  | ||||
| import Comp.AddonArchiveManage | ||||
| import Comp.AddonRunConfigManage | ||||
| import Comp.BookmarkManage | ||||
| import Comp.CustomFieldManage | ||||
| import Comp.EquipmentManage | ||||
| @@ -31,6 +33,8 @@ type alias Model = | ||||
|     , folderManageModel : Comp.FolderManage.Model | ||||
|     , fieldManageModel : Comp.CustomFieldManage.Model | ||||
|     , bookmarkModel : Comp.BookmarkManage.Model | ||||
|     , addonArchiveModel : Comp.AddonArchiveManage.Model | ||||
|     , addonRunConfigModel : Comp.AddonRunConfigManage.Model | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -42,6 +46,12 @@ init flags = | ||||
|  | ||||
|         ( bm, bc ) = | ||||
|             Comp.BookmarkManage.init flags | ||||
|  | ||||
|         ( aam, aac ) = | ||||
|             Comp.AddonArchiveManage.init flags | ||||
|  | ||||
|         ( arm, arc ) = | ||||
|             Comp.AddonRunConfigManage.init flags | ||||
|     in | ||||
|     ( { currentTab = Just TagTab | ||||
|       , tagManageModel = m2 | ||||
| @@ -51,10 +61,14 @@ init flags = | ||||
|       , folderManageModel = Comp.FolderManage.empty | ||||
|       , fieldManageModel = Comp.CustomFieldManage.empty | ||||
|       , bookmarkModel = bm | ||||
|       , addonArchiveModel = aam | ||||
|       , addonRunConfigModel = arm | ||||
|       } | ||||
|     , Cmd.batch | ||||
|         [ Cmd.map TagManageMsg c2 | ||||
|         , Cmd.map BookmarkMsg bc | ||||
|         , Cmd.map AddonArchiveMsg aac | ||||
|         , Cmd.map AddonRunConfigMsg arc | ||||
|         ] | ||||
|     ) | ||||
|  | ||||
| @@ -67,6 +81,8 @@ type Tab | ||||
|     | FolderTab | ||||
|     | CustomFieldTab | ||||
|     | BookmarkTab | ||||
|     | AddonArchiveTab | ||||
|     | AddonRunConfigTab | ||||
|  | ||||
|  | ||||
| type Msg | ||||
| @@ -78,3 +94,5 @@ type Msg | ||||
|     | FolderMsg Comp.FolderManage.Msg | ||||
|     | CustomFieldMsg Comp.CustomFieldManage.Msg | ||||
|     | BookmarkMsg Comp.BookmarkManage.Msg | ||||
|     | AddonArchiveMsg Comp.AddonArchiveManage.Msg | ||||
|     | AddonRunConfigMsg Comp.AddonRunConfigManage.Msg | ||||
|   | ||||
| @@ -7,6 +7,8 @@ | ||||
|  | ||||
| module Page.ManageData.Update exposing (update) | ||||
|  | ||||
| import Comp.AddonArchiveManage | ||||
| import Comp.AddonRunConfigManage | ||||
| import Comp.BookmarkManage | ||||
| import Comp.CustomFieldManage | ||||
| import Comp.EquipmentManage | ||||
| @@ -15,11 +17,12 @@ import Comp.OrgManage | ||||
| import Comp.PersonManage | ||||
| import Comp.TagManage | ||||
| import Data.Flags exposing (Flags) | ||||
| import Data.UiSettings exposing (UiSettings) | ||||
| import Page.ManageData.Data exposing (..) | ||||
|  | ||||
|  | ||||
| update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) | ||||
| update flags msg model = | ||||
| update : Flags -> UiSettings -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) | ||||
| update flags uiSettings msg model = | ||||
|     case msg of | ||||
|         SetTab t -> | ||||
|             let | ||||
| @@ -28,16 +31,16 @@ update flags msg model = | ||||
|             in | ||||
|             case t of | ||||
|                 TagTab -> | ||||
|                     update flags (TagManageMsg Comp.TagManage.LoadTags) m | ||||
|                     update flags uiSettings (TagManageMsg Comp.TagManage.LoadTags) m | ||||
|  | ||||
|                 EquipTab -> | ||||
|                     update flags (EquipManageMsg Comp.EquipmentManage.LoadEquipments) m | ||||
|                     update flags uiSettings (EquipManageMsg Comp.EquipmentManage.LoadEquipments) m | ||||
|  | ||||
|                 OrgTab -> | ||||
|                     update flags (OrgManageMsg Comp.OrgManage.LoadOrgs) m | ||||
|                     update flags uiSettings (OrgManageMsg Comp.OrgManage.LoadOrgs) m | ||||
|  | ||||
|                 PersonTab -> | ||||
|                     update flags (PersonManageMsg Comp.PersonManage.LoadPersons) m | ||||
|                     update flags uiSettings (PersonManageMsg Comp.PersonManage.LoadPersons) m | ||||
|  | ||||
|                 FolderTab -> | ||||
|                     let | ||||
| @@ -60,6 +63,20 @@ update flags msg model = | ||||
|                     in | ||||
|                     ( { m | bookmarkModel = bm }, Cmd.map BookmarkMsg bc, Sub.none ) | ||||
|  | ||||
|                 AddonArchiveTab -> | ||||
|                     let | ||||
|                         ( aam, aac ) = | ||||
|                             Comp.AddonArchiveManage.init flags | ||||
|                     in | ||||
|                     ( { m | addonArchiveModel = aam }, Cmd.map AddonArchiveMsg aac, Sub.none ) | ||||
|  | ||||
|                 AddonRunConfigTab -> | ||||
|                     let | ||||
|                         ( arm, arc ) = | ||||
|                             Comp.AddonRunConfigManage.init flags | ||||
|                     in | ||||
|                     ( { m | addonRunConfigModel = arm }, Cmd.map AddonRunConfigMsg arc, Sub.none ) | ||||
|  | ||||
|         TagManageMsg m -> | ||||
|             let | ||||
|                 ( m2, c2 ) = | ||||
| @@ -117,3 +134,23 @@ update flags msg model = | ||||
|             , Cmd.map BookmarkMsg c2 | ||||
|             , Sub.map BookmarkMsg s2 | ||||
|             ) | ||||
|  | ||||
|         AddonArchiveMsg lm -> | ||||
|             let | ||||
|                 ( aam, aac, aas ) = | ||||
|                     Comp.AddonArchiveManage.update flags lm model.addonArchiveModel | ||||
|             in | ||||
|             ( { model | addonArchiveModel = aam } | ||||
|             , Cmd.map AddonArchiveMsg aac | ||||
|             , Sub.map AddonArchiveMsg aas | ||||
|             ) | ||||
|  | ||||
|         AddonRunConfigMsg lm -> | ||||
|             let | ||||
|                 ( arm, arc, ars ) = | ||||
|                     Comp.AddonRunConfigManage.update flags uiSettings.timeZone lm model.addonRunConfigModel | ||||
|             in | ||||
|             ( { model | addonRunConfigModel = arm } | ||||
|             , Cmd.map AddonRunConfigMsg arc | ||||
|             , Sub.map AddonRunConfigMsg ars | ||||
|             ) | ||||
|   | ||||
| @@ -7,6 +7,8 @@ | ||||
|  | ||||
| module Page.ManageData.View2 exposing (viewContent, viewSidebar) | ||||
|  | ||||
| import Comp.AddonArchiveManage | ||||
| import Comp.AddonRunConfigManage | ||||
| import Comp.BookmarkManage | ||||
| import Comp.CustomFieldManage | ||||
| import Comp.EquipmentManage | ||||
| @@ -27,7 +29,7 @@ import Styles as S | ||||
|  | ||||
|  | ||||
| viewSidebar : Texts -> Bool -> Flags -> UiSettings -> Model -> Html Msg | ||||
| viewSidebar texts visible _ settings model = | ||||
| viewSidebar texts visible flags settings model = | ||||
|     div | ||||
|         [ id "sidebar" | ||||
|         , class S.sidebar | ||||
| @@ -134,6 +136,32 @@ viewSidebar texts visible _ settings model = | ||||
|                     [ text texts.bookmarks | ||||
|                     ] | ||||
|                 ] | ||||
|             , a | ||||
|                 [ href "#" | ||||
|                 , onClick (SetTab AddonArchiveTab) | ||||
|                 , menuEntryActive model AddonArchiveTab | ||||
|                 , class S.sidebarLink | ||||
|                 , classList [ ( "hidden", not flags.config.addonsEnabled ) ] | ||||
|                 ] | ||||
|                 [ Icons.addonIcon "" | ||||
|                 , span | ||||
|                     [ class "ml-3" ] | ||||
|                     [ text texts.addonArchives | ||||
|                     ] | ||||
|                 ] | ||||
|             , a | ||||
|                 [ href "#" | ||||
|                 , onClick (SetTab AddonRunConfigTab) | ||||
|                 , menuEntryActive model AddonRunConfigTab | ||||
|                 , class S.sidebarLink | ||||
|                 , classList [ ( "hidden", not flags.config.addonsEnabled ) ] | ||||
|                 ] | ||||
|                 [ Icons.addonRunConfigIcon "" | ||||
|                 , span | ||||
|                     [ class "ml-3" ] | ||||
|                     [ text texts.addonRunConfigs | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|         ] | ||||
|  | ||||
| @@ -166,6 +194,20 @@ viewContent texts flags settings model = | ||||
|             Just BookmarkTab -> | ||||
|                 viewBookmarks texts flags settings model | ||||
|  | ||||
|             Just AddonArchiveTab -> | ||||
|                 if flags.config.addonsEnabled then | ||||
|                     viewAddonArchives texts flags settings model | ||||
|  | ||||
|                 else | ||||
|                     [] | ||||
|  | ||||
|             Just AddonRunConfigTab -> | ||||
|                 if flags.config.addonsEnabled then | ||||
|                     viewAddonRunConfigs texts flags settings model | ||||
|  | ||||
|                 else | ||||
|                     [] | ||||
|  | ||||
|             Nothing -> | ||||
|                 [] | ||||
|         ) | ||||
| @@ -306,3 +348,33 @@ viewBookmarks texts flags settings model = | ||||
|         ] | ||||
|     , Html.map BookmarkMsg (Comp.BookmarkManage.view texts.bookmarkManage settings flags model.bookmarkModel) | ||||
|     ] | ||||
|  | ||||
|  | ||||
| viewAddonArchives : Texts -> Flags -> UiSettings -> Model -> List (Html Msg) | ||||
| viewAddonArchives texts flags settings model = | ||||
|     [ h2 | ||||
|         [ class S.header1 | ||||
|         , class "inline-flex items-center" | ||||
|         ] | ||||
|         [ Icons.addonIcon "" | ||||
|         , div [ class "ml-2" ] | ||||
|             [ text texts.addonArchives | ||||
|             ] | ||||
|         ] | ||||
|     , Html.map AddonArchiveMsg (Comp.AddonArchiveManage.view texts.addonArchiveManage settings flags model.addonArchiveModel) | ||||
|     ] | ||||
|  | ||||
|  | ||||
| viewAddonRunConfigs : Texts -> Flags -> UiSettings -> Model -> List (Html Msg) | ||||
| viewAddonRunConfigs texts flags settings model = | ||||
|     [ h2 | ||||
|         [ class S.header1 | ||||
|         , class "inline-flex items-center" | ||||
|         ] | ||||
|         [ Icons.addonRunConfigIcon "mr-4" | ||||
|         , div [ class "ml-2" ] | ||||
|             [ text texts.addonRunConfigs | ||||
|             ] | ||||
|         ] | ||||
|     , Html.map AddonRunConfigMsg (Comp.AddonRunConfigManage.view texts.addonRunConfigManage settings flags model.addonRunConfigModel) | ||||
|     ] | ||||
|   | ||||
| @@ -35,6 +35,11 @@ sidebarLink = | ||||
|     " mb-2 px-4 py-3 flex flex-row hover:bg-blue-100 dark:hover:bg-slate-600 hover:font-bold rounded rounded-lg items-center " | ||||
|  | ||||
|  | ||||
| successText : String | ||||
| successText = | ||||
|     " text-green-600  dark:text-lime-500 " | ||||
|  | ||||
|  | ||||
| successMessage : String | ||||
| successMessage = | ||||
|     " border border-green-600 bg-green-50 text-green-600 dark:border-lime-800 dark:bg-lime-300 dark:text-lime-800 px-4 py-2 rounded " | ||||
| @@ -281,7 +286,7 @@ link = | ||||
|  | ||||
| inputErrorBorder : String | ||||
| inputErrorBorder = | ||||
|     " border-red-600 dark:border-orange-600 " | ||||
|     " ring dark:ring-0 ring-red-600 dark:border-orange-600 " | ||||
|  | ||||
|  | ||||
| inputLabel : String | ||||
|   | ||||
| @@ -14,12 +14,42 @@ module Util.List exposing | ||||
|     , findNext | ||||
|     , findPrev | ||||
|     , get | ||||
|     , removeByIndex | ||||
|     , replaceByIndex | ||||
|     , sliding | ||||
|     ) | ||||
|  | ||||
| import Html.Attributes exposing (list) | ||||
|  | ||||
|  | ||||
| removeByIndex : Int -> List a -> List a | ||||
| removeByIndex index list = | ||||
|     List.indexedMap | ||||
|         (\idx -> | ||||
|             \e -> | ||||
|                 if idx == index then | ||||
|                     Nothing | ||||
|  | ||||
|                 else | ||||
|                     Just e | ||||
|         ) | ||||
|         list | ||||
|         |> List.filterMap identity | ||||
|  | ||||
|  | ||||
| replaceByIndex : Int -> a -> List a -> List a | ||||
| replaceByIndex index element list = | ||||
|     let | ||||
|         repl idx e = | ||||
|             if idx == index then | ||||
|                 element | ||||
|  | ||||
|             else | ||||
|                 e | ||||
|     in | ||||
|     List.indexedMap repl list | ||||
|  | ||||
|  | ||||
| changePosition : Int -> Int -> List a -> List a | ||||
| changePosition source target list = | ||||
|     let | ||||
|   | ||||
| @@ -9,6 +9,7 @@ module Util.String exposing | ||||
|     ( appendIfAbsent | ||||
|     , crazyEncode | ||||
|     , ellipsis | ||||
|     , firstSentenceOrMax | ||||
|     , isBlank | ||||
|     , isNothingOrBlank | ||||
|     , underscoreToSpace | ||||
| @@ -16,7 +17,6 @@ module Util.String exposing | ||||
|     ) | ||||
|  | ||||
| import Base64 | ||||
| import Html exposing (strong) | ||||
|  | ||||
|  | ||||
| crazyEncode : String -> String | ||||
| @@ -45,6 +45,26 @@ ellipsis len str = | ||||
|         String.left (len - 1) str ++ "…" | ||||
|  | ||||
|  | ||||
| firstSentenceOrMax : Int -> String -> Maybe String | ||||
| firstSentenceOrMax maxLen str = | ||||
|     let | ||||
|         idx = | ||||
|             String.indexes "." str | ||||
|                 |> List.head | ||||
|                 |> Maybe.map ((+) 2) | ||||
|                 |> Maybe.map (min maxLen) | ||||
|                 |> Maybe.withDefault maxLen | ||||
|  | ||||
|         len = | ||||
|             String.length str | ||||
|     in | ||||
|     if len <= maxLen then | ||||
|         Nothing | ||||
|  | ||||
|     else | ||||
|         Just <| String.left (idx - 1) str ++ "…" | ||||
|  | ||||
|  | ||||
| withDefault : String -> String -> String | ||||
| withDefault default str = | ||||
|     if str == "" then | ||||
|   | ||||
| @@ -96,4 +96,8 @@ | ||||
|   .markdown-preview a { | ||||
|       @apply text-blue-400 hover:text-blue-500 dark:text-sky-200 dark:hover:text-sky-100 cursor-pointer; | ||||
|   } | ||||
|  | ||||
|   .markdown-preview pre { | ||||
|       @apply font-mono px-2 py-2 text-sm border dark:border-slate-600 rounded my-2; | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user