diff --git a/.mergify.yml b/.mergify.yml index a1afd023..88a01f58 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -7,3 +7,11 @@ pull_request_rules: actions: merge: method: merge + - name: automatically merge my (eikek) PRs on CI success + conditions: + - author=eikek + - base=master + - status-success=continuous-integration/travis-ci/pr + actions: + merge: + method: merge diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OUserTask.scala b/modules/backend/src/main/scala/docspell/backend/ops/OUserTask.scala index 21b916ab..3582240d 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OUserTask.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OUserTask.scala @@ -3,7 +3,6 @@ package docspell.backend.ops import cats.implicits._ import cats.effect._ import cats.data.OptionT -import com.github.eikek.calev.CalEvent import io.circe.Encoder import fs2.Stream @@ -30,12 +29,16 @@ trait OUserTask[F[_]] { task: UserTask[ScanMailboxArgs] ): F[Unit] - /** Return the settings for the notify-due-items task of the current - * user. There is at most one such task per user. If no task has - * been created/submitted a new one with default values is - * returned. + /** Return the settings for all the notify-due-items task of the + * current user. */ - def getNotifyDueItems(account: AccountId): F[UserTask[NotifyDueItemsArgs]] + def getNotifyDueItems(account: AccountId): Stream[F, UserTask[NotifyDueItemsArgs]] + + /** Find a notify-due-items task by the given id. */ + def findNotifyDueItems( + id: Ident, + account: AccountId + ): OptionT[F, UserTask[NotifyDueItemsArgs]] /** Updates the notify-due-items tasks and notifies the joex nodes. */ @@ -100,62 +103,24 @@ object OUserTask { _ <- joex.notifyAllNodes } yield () - def getNotifyDueItems(account: AccountId): F[UserTask[NotifyDueItemsArgs]] = + def getNotifyDueItems(account: AccountId): Stream[F, UserTask[NotifyDueItemsArgs]] = store - .getOneByName[NotifyDueItemsArgs](account, NotifyDueItemsArgs.taskName) - .getOrElseF(notifyDueItemsDefault(account)) + .getByName[NotifyDueItemsArgs](account, NotifyDueItemsArgs.taskName) + + def findNotifyDueItems( + id: Ident, + account: AccountId + ): OptionT[F, UserTask[NotifyDueItemsArgs]] = + OptionT(getNotifyDueItems(account).find(_.id == id).compile.last) def submitNotifyDueItems( account: AccountId, task: UserTask[NotifyDueItemsArgs] ): F[Unit] = for { - _ <- store.updateOneTask[NotifyDueItemsArgs](account, task) + _ <- store.updateTask[NotifyDueItemsArgs](account, task) _ <- joex.notifyAllNodes } yield () - - private def notifyDueItemsDefault( - account: AccountId - ): F[UserTask[NotifyDueItemsArgs]] = - for { - id <- Ident.randomId[F] - } yield UserTask( - id, - NotifyDueItemsArgs.taskName, - false, - CalEvent.unsafe("*-*-1/7 12:00"), - NotifyDueItemsArgs( - account, - Ident.unsafe(""), - Nil, - None, - 5, - None, - Nil, - Nil - ) - ) - - // private def scanMailboxDefault( - // account: AccountId - // ): F[UserTask[ScanMailboxArgs]] = - // for { - // id <- Ident.randomId[F] - // } yield UserTask( - // id, - // ScanMailboxArgs.taskName, - // false, - // CalEvent.unsafe("*-*-* 0,12:00"), - // ScanMailboxArgs( - // account, - // Ident.unsafe(""), - // Nil, - // Some(Duration.hours(12)), - // None, - // false, - // None - // ) - // ) }) } diff --git a/modules/restapi/src/main/resources/docspell-openapi.yml b/modules/restapi/src/main/resources/docspell-openapi.yml index 4c3fb7f0..b9aff661 100644 --- a/modules/restapi/src/main/resources/docspell-openapi.yml +++ b/modules/restapi/src/main/resources/docspell-openapi.yml @@ -2008,7 +2008,8 @@ paths: description: | Return the current notification settings of the authenticated user. Users can be notified on due items via e-mail. This is - done by periodically querying items. + done by periodically querying items. It is possible to have + multiple tasks. security: - authTokenHeader: [] responses: @@ -2017,13 +2018,13 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/NotificationSettings" + $ref: "#/components/schemas/NotificationSettingsList" post: tags: [ User Tasks ] - summary: Change current settings for "Notify Due Items" task + summary: Create settings for "Notify Due Items" task description: | - Change the current notification settings of the authenticated - user. + Create a new notification settings task of the authenticated + user. The id field in the input is ignored. security: - authTokenHeader: [] requestBody: @@ -2038,6 +2039,58 @@ paths: application/json: schema: $ref: "#/components/schemas/BasicResult" + put: + tags: [ User Tasks ] + summary: Change settings for "Notify Due Items" task + description: | + Change the settings for a notify-due-items task. The task is + looked up by its id. + security: + - authTokenHeader: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/NotificationSettings" + responses: + 200: + description: Ok + content: + application/json: + schema: + $ref: "#/components/schemas/BasicResult" + /sec/usertask/notifydueitems/{id}: + parameters: + - $ref: "#/components/parameters/id" + get: + tags: [ User Tasks ] + description: | + Return the current settings for a single notify-due-items task + of the authenticated user. + security: + - authTokenHeader: [] + responses: + 200: + description: Ok + content: + application/json: + schema: + $ref: "#/components/schemas/NotificationSettings" + delete: + tags: [ User Tasks ] + description: | + Delete the settings to a notify-due-items task of the + authenticated user. + security: + - authTokenHeader: [] + responses: + 200: + description: Ok + content: + application/json: + schema: + $ref: "#/components/schemas/BasicResult" + /sec/usertask/notifydueitems/startonce: post: tags: [ User Tasks ] @@ -2100,7 +2153,7 @@ paths: $ref: "#/components/schemas/BasicResult" put: tags: [ User Tasks ] - summary: Change current settings for "Scan Mailbox" task + summary: Change settings for a "Scan Mailbox" task description: | Change the settings for a scan-mailbox task. The task is looked up by its id. @@ -2312,6 +2365,16 @@ components: properties: event: type: string + NotificationSettingsList: + description: | + A list of notification settings. + required: + - items + properties: + items: + type: array + items: + $ref: "#/components/schemas/NotificationSettings" NotificationSettings: description: | Settings for notifying about due items. diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/NotifyDueItemsRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/NotifyDueItemsRoutes.scala index 88c7a67f..6e9b9cde 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/NotifyDueItemsRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/NotifyDueItemsRoutes.scala @@ -2,6 +2,7 @@ package docspell.restserver.routes import cats.effect._ import cats.implicits._ +import cats.data.OptionT import org.http4s._ import org.http4s.dsl.Http4sDsl import org.http4s.circe.CirceEntityEncoder._ @@ -27,10 +28,18 @@ object NotifyDueItemsRoutes { import dsl._ HttpRoutes.of { + case GET -> Root / Ident(id) => + (for { + task <- ut.findNotifyDueItems(id, user.account) + res <- OptionT.liftF(taskToSettings(user.account, backend, task)) + resp <- OptionT.liftF(Ok(res)) + } yield resp).getOrElseF(NotFound()) + case req @ POST -> Root / "startonce" => for { - data <- req.as[NotificationSettings] - task = makeTask(cfg, user.account, data) + data <- req.as[NotificationSettings] + newId <- Ident.randomId[F] + task <- makeTask(newId, cfg, user.account, data) res <- ut.executeNow(user.account, task) .attempt @@ -38,46 +47,77 @@ object NotifyDueItemsRoutes { resp <- Ok(res) } yield resp - case GET -> Root => + case DELETE -> Root / Ident(id) => for { - task <- ut.getNotifyDueItems(user.account) - res <- taskToSettings(user.account, backend, task) + res <- + ut.deleteTask(user.account, id) + .attempt + .map(Conversions.basicResult(_, "Deleted successfully")) resp <- Ok(res) } yield resp + case req @ PUT -> Root => + def run(data: NotificationSettings) = + for { + task <- makeTask(data.id, cfg, user.account, data) + res <- + ut.submitNotifyDueItems(user.account, task) + .attempt + .map(Conversions.basicResult(_, "Saved successfully")) + resp <- Ok(res) + } yield resp + for { + data <- req.as[NotificationSettings] + resp <- + if (data.id.isEmpty) Ok(BasicResult(false, "Empty id is not allowed")) + else run(data) + } yield resp + case req @ POST -> Root => for { - data <- req.as[NotificationSettings] - task = makeTask(cfg, user.account, data) + data <- req.as[NotificationSettings] + newId <- Ident.randomId[F] + task <- makeTask(newId, cfg, user.account, data) res <- ut.submitNotifyDueItems(user.account, task) .attempt .map(Conversions.basicResult(_, "Saved successfully.")) resp <- Ok(res) } yield resp + + case GET -> Root => + ut.getNotifyDueItems(user.account) + .evalMap(task => taskToSettings(user.account, backend, task)) + .compile + .toVector + .map(v => NotificationSettingsList(v.toList)) + .flatMap(Ok(_)) } } - def makeTask( + def makeTask[F[_]: Sync]( + id: Ident, cfg: Config, user: AccountId, settings: NotificationSettings - ): UserTask[NotifyDueItemsArgs] = - UserTask( - settings.id, - NotifyDueItemsArgs.taskName, - settings.enabled, - settings.schedule, - NotifyDueItemsArgs( - user, - settings.smtpConnection, - settings.recipients, - Some(cfg.baseUrl / "app" / "item"), - settings.remindDays, - if (settings.capOverdue) Some(settings.remindDays) - else None, - settings.tagsInclude.map(_.id), - settings.tagsExclude.map(_.id) + ): F[UserTask[NotifyDueItemsArgs]] = + Sync[F].pure( + UserTask( + id, + NotifyDueItemsArgs.taskName, + settings.enabled, + settings.schedule, + NotifyDueItemsArgs( + user, + settings.smtpConnection, + settings.recipients, + Some(cfg.baseUrl / "app" / "item"), + settings.remindDays, + if (settings.capOverdue) Some(settings.remindDays) + else None, + settings.tagsInclude.map(_.id), + settings.tagsExclude.map(_.id) + ) ) ) diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm index 284df71b..92be445b 100644 --- a/modules/webapp/src/main/elm/Api.elm +++ b/modules/webapp/src/main/elm/Api.elm @@ -9,12 +9,14 @@ module Api exposing , checkCalEvent , createImapSettings , createMailSettings + , createNotifyDueItems , createScanMailbox , deleteAttachment , deleteEquip , deleteImapSettings , deleteItem , deleteMailSettings + , deleteNotifyDueItems , deleteOrg , deletePerson , deleteScanMailbox @@ -75,6 +77,7 @@ module Api exposing , startOnceNotifyDueItems , startOnceScanMailbox , submitNotifyDueItems + , updateNotifyDueItems , updateScanMailbox , upload , uploadAmend @@ -108,6 +111,7 @@ import Api.Model.ItemUploadMeta exposing (ItemUploadMeta) import Api.Model.JobQueueState exposing (JobQueueState) import Api.Model.MoveAttachment exposing (MoveAttachment) import Api.Model.NotificationSettings exposing (NotificationSettings) +import Api.Model.NotificationSettingsList exposing (NotificationSettingsList) import Api.Model.OptionalDate exposing (OptionalDate) import Api.Model.OptionalId exposing (OptionalId) import Api.Model.OptionalText exposing (OptionalText) @@ -216,6 +220,19 @@ getScanMailbox flags receive = --- NotifyDueItems +deleteNotifyDueItems : + Flags + -> String + -> (Result Http.Error BasicResult -> msg) + -> Cmd msg +deleteNotifyDueItems flags id receive = + Http2.authDelete + { url = flags.config.baseUrl ++ "/api/v1/sec/usertask/notifydueitems/" ++ id + , account = getAccount flags + , expect = Http.expectJson receive Api.Model.BasicResult.decoder + } + + startOnceNotifyDueItems : Flags -> NotificationSettings @@ -230,6 +247,46 @@ startOnceNotifyDueItems flags settings receive = } +updateNotifyDueItems : + Flags + -> NotificationSettings + -> (Result Http.Error BasicResult -> msg) + -> Cmd msg +updateNotifyDueItems flags settings receive = + Http2.authPut + { url = flags.config.baseUrl ++ "/api/v1/sec/usertask/notifydueitems" + , account = getAccount flags + , body = Http.jsonBody (Api.Model.NotificationSettings.encode settings) + , expect = Http.expectJson receive Api.Model.BasicResult.decoder + } + + +createNotifyDueItems : + Flags + -> NotificationSettings + -> (Result Http.Error BasicResult -> msg) + -> Cmd msg +createNotifyDueItems flags settings receive = + Http2.authPost + { url = flags.config.baseUrl ++ "/api/v1/sec/usertask/notifydueitems" + , account = getAccount flags + , body = Http.jsonBody (Api.Model.NotificationSettings.encode settings) + , expect = Http.expectJson receive Api.Model.BasicResult.decoder + } + + +getNotifyDueItems : + Flags + -> (Result Http.Error NotificationSettingsList -> msg) + -> Cmd msg +getNotifyDueItems flags receive = + Http2.authGet + { url = flags.config.baseUrl ++ "/api/v1/sec/usertask/notifydueitems" + , account = getAccount flags + , expect = Http.expectJson receive Api.Model.NotificationSettingsList.decoder + } + + submitNotifyDueItems : Flags -> NotificationSettings @@ -244,18 +301,6 @@ submitNotifyDueItems flags settings receive = } -getNotifyDueItems : - Flags - -> (Result Http.Error NotificationSettings -> msg) - -> Cmd msg -getNotifyDueItems flags receive = - Http2.authGet - { url = flags.config.baseUrl ++ "/api/v1/sec/usertask/notifydueitems" - , account = getAccount flags - , expect = Http.expectJson receive Api.Model.NotificationSettings.decoder - } - - --- CalEvent diff --git a/modules/webapp/src/main/elm/Comp/NotificationForm.elm b/modules/webapp/src/main/elm/Comp/NotificationForm.elm index 68855c39..119cff34 100644 --- a/modules/webapp/src/main/elm/Comp/NotificationForm.elm +++ b/modules/webapp/src/main/elm/Comp/NotificationForm.elm @@ -1,7 +1,9 @@ module Comp.NotificationForm exposing - ( Model + ( Action(..) + , Model , Msg , init + , initWith , update , view ) @@ -16,6 +18,7 @@ import Comp.CalEventInput import Comp.Dropdown import Comp.EmailInput import Comp.IntField +import Comp.YesNoDimmer import Data.CalEvent exposing (CalEvent) import Data.Flags exposing (Flags) import Data.UiSettings exposing (UiSettings) @@ -45,9 +48,18 @@ type alias Model = , scheduleModel : Comp.CalEventInput.Model , formMsg : Maybe BasicResult , loading : Int + , yesNoDelete : Comp.YesNoDimmer.Model } +type Action + = SubmitAction NotificationSettings + | StartOnceAction NotificationSettings + | CancelAction + | DeleteAction String + | NoAction + + type Msg = Submit | TagIncMsg (Comp.Dropdown.Msg Tag) @@ -60,22 +72,63 @@ type Msg | ToggleEnabled | ToggleCapOverdue | CalEventMsg Comp.CalEventInput.Msg - | SetNotificationSettings (Result Http.Error NotificationSettings) - | SubmitResp (Result Http.Error BasicResult) | StartOnce + | Cancel + | RequestDelete + | YesNoDeleteMsg Comp.YesNoDimmer.Msg -initCmd : Flags -> Cmd Msg -initCmd flags = - Cmd.batch - [ Api.getMailSettings flags "" ConnResp - , Api.getTags flags "" GetTagsResp - , Api.getNotifyDueItems flags SetNotificationSettings +initWith : Flags -> NotificationSettings -> ( Model, Cmd Msg ) +initWith flags s = + let + ( im, ic ) = + init flags + + smtp = + Util.Maybe.fromString s.smtpConnection + |> Maybe.map List.singleton + |> Maybe.withDefault [] + + removeAction ( tm, _, tc ) = + ( tm, tc ) + + ( nm, nc ) = + Util.Update.andThen1 + [ update flags (ConnMsg (Comp.Dropdown.SetSelection smtp)) >> removeAction + , update flags (TagIncMsg (Comp.Dropdown.SetSelection s.tagsInclude)) >> removeAction + , update flags (TagExcMsg (Comp.Dropdown.SetSelection s.tagsExclude)) >> removeAction + ] + im + + newSchedule = + Data.CalEvent.fromEvent s.schedule + |> Maybe.withDefault Data.CalEvent.everyMonth + + ( sm, sc ) = + Comp.CalEventInput.init flags newSchedule + in + ( { nm + | settings = s + , recipients = s.recipients + , remindDays = Just s.remindDays + , enabled = s.enabled + , capOverdue = s.capOverdue + , schedule = Data.Validated.Unknown newSchedule + , scheduleModel = sm + , formMsg = Nothing + , loading = im.loading + , yesNoDelete = Comp.YesNoDimmer.emptyModel + } + , Cmd.batch + [ nc + , ic + , Cmd.map CalEventMsg sc ] + ) -init : Flags -> UiSettings -> ( Model, Cmd Msg ) -init flags settings = +init : Flags -> ( Model, Cmd Msg ) +init flags = let initialSchedule = Data.Validated.Unknown Data.CalEvent.everyMonth @@ -100,10 +153,12 @@ init flags settings = , schedule = initialSchedule , scheduleModel = sm , formMsg = Nothing - , loading = 3 + , loading = 2 + , yesNoDelete = Comp.YesNoDimmer.emptyModel } , Cmd.batch - [ initCmd flags + [ Api.getMailSettings flags "" ConnResp + , Api.getTags flags "" GetTagsResp , Cmd.map CalEventMsg sc ] ) @@ -155,12 +210,13 @@ makeSettings model = model.schedule -withValidSettings : (NotificationSettings -> Cmd Msg) -> Model -> ( Model, Cmd Msg ) +withValidSettings : (NotificationSettings -> Action) -> Model -> ( Model, Action, Cmd Msg ) withValidSettings mkcmd model = case makeSettings model of Valid set -> ( { model | formMsg = Nothing } , mkcmd set + , Cmd.none ) Invalid errs _ -> @@ -168,15 +224,19 @@ withValidSettings mkcmd model = errMsg = String.join ", " errs in - ( { model | formMsg = Just (BasicResult False errMsg) }, Cmd.none ) + ( { model | formMsg = Just (BasicResult False errMsg) } + , NoAction + , Cmd.none + ) Unknown _ -> ( { model | formMsg = Just (BasicResult False "An unknown error occured") } + , NoAction , Cmd.none ) -update : Flags -> Msg -> Model -> ( Model, Cmd Msg ) +update : Flags -> Msg -> Model -> ( Model, Action, Cmd Msg ) update flags msg model = case msg of CalEventMsg lmsg -> @@ -192,6 +252,7 @@ update flags msg model = , scheduleModel = cm , formMsg = Nothing } + , NoAction , Cmd.map CalEventMsg cc ) @@ -205,6 +266,7 @@ update flags msg model = , recipientsModel = em , formMsg = Nothing } + , NoAction , Cmd.map RecipientMsg ec ) @@ -217,6 +279,7 @@ update flags msg model = | connectionModel = cm , formMsg = Nothing } + , NoAction , Cmd.map ConnMsg cc ) @@ -246,6 +309,7 @@ update flags msg model = else Nothing } + , NoAction , Cmd.none ) @@ -254,6 +318,7 @@ update flags msg model = | formMsg = Just (BasicResult False (Util.Http.errorToString err)) , loading = model.loading - 1 } + , NoAction , Cmd.none ) @@ -266,6 +331,7 @@ update flags msg model = | tagInclModel = m2 , formMsg = Nothing } + , NoAction , Cmd.map TagIncMsg c2 ) @@ -278,6 +344,7 @@ update flags msg model = | tagExclModel = m2 , formMsg = Nothing } + , NoAction , Cmd.map TagExcMsg c2 ) @@ -285,18 +352,25 @@ update flags msg model = let tagList = Comp.Dropdown.SetOptions tags.items + + removeAction ( tm, _, tc ) = + ( tm, tc ) + + ( m, c ) = + Util.Update.andThen1 + [ update flags (TagIncMsg tagList) >> removeAction + , update flags (TagExcMsg tagList) >> removeAction + ] + { model | loading = model.loading - 1 } in - Util.Update.andThen1 - [ update flags (TagIncMsg tagList) - , update flags (TagExcMsg tagList) - ] - { model | loading = model.loading - 1 } + ( m, NoAction, c ) GetTagsResp (Err err) -> ( { model | loading = model.loading - 1 , formMsg = Just (BasicResult False (Util.Http.errorToString err)) } + , NoAction , Cmd.none ) @@ -310,6 +384,7 @@ update flags msg model = , remindDays = val , formMsg = Nothing } + , NoAction , Cmd.none ) @@ -318,6 +393,7 @@ update flags msg model = | enabled = not model.enabled , formMsg = Nothing } + , NoAction , Cmd.none ) @@ -326,75 +402,49 @@ update flags msg model = | capOverdue = not model.capOverdue , formMsg = Nothing } - , Cmd.none - ) - - SetNotificationSettings (Ok s) -> - let - smtp = - Util.Maybe.fromString s.smtpConnection - |> Maybe.map List.singleton - |> Maybe.withDefault [] - - ( nm, nc ) = - Util.Update.andThen1 - [ update flags (ConnMsg (Comp.Dropdown.SetSelection smtp)) - , update flags (TagIncMsg (Comp.Dropdown.SetSelection s.tagsInclude)) - , update flags (TagExcMsg (Comp.Dropdown.SetSelection s.tagsExclude)) - ] - model - - newSchedule = - Data.CalEvent.fromEvent s.schedule - |> Maybe.withDefault Data.CalEvent.everyMonth - - ( sm, sc ) = - Comp.CalEventInput.init flags newSchedule - in - ( { nm - | settings = s - , recipients = s.recipients - , remindDays = Just s.remindDays - , enabled = s.enabled - , capOverdue = s.capOverdue - , schedule = Data.Validated.Unknown newSchedule - , scheduleModel = sm - , formMsg = Nothing - , loading = model.loading - 1 - } - , Cmd.batch - [ nc - , Cmd.map CalEventMsg sc - ] - ) - - SetNotificationSettings (Err err) -> - ( { model - | formMsg = Just (BasicResult False (Util.Http.errorToString err)) - , loading = model.loading - 1 - } + , NoAction , Cmd.none ) Submit -> withValidSettings - (\set -> Api.submitNotifyDueItems flags set SubmitResp) + SubmitAction model StartOnce -> withValidSettings - (\set -> Api.startOnceNotifyDueItems flags set SubmitResp) + StartOnceAction model - SubmitResp (Ok res) -> - ( { model | formMsg = Just res } + Cancel -> + ( model, CancelAction, Cmd.none ) + + RequestDelete -> + let + ( ym, _ ) = + Comp.YesNoDimmer.update + Comp.YesNoDimmer.activate + model.yesNoDelete + in + ( { model | yesNoDelete = ym } + , NoAction , Cmd.none ) - SubmitResp (Err err) -> - ( { model - | formMsg = Just (BasicResult False (Util.Http.errorToString err)) - } + YesNoDeleteMsg lm -> + let + ( ym, flag ) = + Comp.YesNoDimmer.update lm model.yesNoDelete + + act = + if flag then + DeleteAction model.settings.id + + else + NoAction + in + ( { model | yesNoDelete = ym } + , act , Cmd.none ) @@ -426,7 +476,8 @@ view extraClasses settings model = , ( "success", isFormSuccess model ) ] ] - [ div + [ Html.map YesNoDeleteMsg (Comp.YesNoDimmer.view model.yesNoDelete) + , div [ classList [ ( "ui dimmer", True ) , ( "active", model.loading > 0 ) @@ -550,6 +601,21 @@ view extraClasses settings model = ] [ text "Submit" ] + , button + [ class "ui secondary button" + , onClick Cancel + ] + [ text "Cancel" + ] + , button + [ classList + [ ( "ui red button", True ) + , ( "hidden invisible", model.settings.id == "" ) + ] + , onClick RequestDelete + ] + [ text "Delete" + ] , button [ class "ui right floated button" , onClick StartOnce diff --git a/modules/webapp/src/main/elm/Comp/NotificationList.elm b/modules/webapp/src/main/elm/Comp/NotificationList.elm new file mode 100644 index 00000000..537668f9 --- /dev/null +++ b/modules/webapp/src/main/elm/Comp/NotificationList.elm @@ -0,0 +1,93 @@ +module Comp.NotificationList exposing + ( Action(..) + , Model + , Msg + , init + , update + , view + ) + +import Api.Model.NotificationSettings exposing (NotificationSettings) +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (onClick) +import Util.Html + + +type alias Model = + {} + + +type Msg + = EditSettings NotificationSettings + + +type Action + = NoAction + | EditAction NotificationSettings + + +init : Model +init = + {} + + +update : Msg -> Model -> ( Model, Action ) +update msg model = + case msg of + EditSettings settings -> + ( model, EditAction settings ) + + +view : Model -> List NotificationSettings -> Html Msg +view _ items = + div [] + [ table [ class "ui very basic center aligned table" ] + [ thead [] + [ th [ class "collapsing" ] [] + , th [ class "collapsing" ] + [ i [ class "check icon" ] [] + ] + , th [] [ text "Schedule" ] + , th [] [ text "Connection" ] + , th [] [ text "Recipients" ] + , th [] [ text "Remind Days" ] + ] + , tbody [] + (List.map viewItem items) + ] + ] + + +viewItem : NotificationSettings -> Html Msg +viewItem item = + tr [] + [ td [ class "collapsing" ] + [ a + [ href "#" + , class "ui basic small blue label" + , onClick (EditSettings item) + ] + [ i [ class "edit icon" ] [] + , text "Edit" + ] + ] + , td [ class "collapsing" ] + [ Util.Html.checkbox item.enabled + ] + , td [] + [ code [] + [ text item.schedule + ] + ] + , td [] + [ text item.smtpConnection + ] + , td [] + [ String.join ", " item.recipients |> text + ] + , td [] + [ String.fromInt item.remindDays + |> text + ] + ] diff --git a/modules/webapp/src/main/elm/Comp/NotificationManage.elm b/modules/webapp/src/main/elm/Comp/NotificationManage.elm new file mode 100644 index 00000000..b0851751 --- /dev/null +++ b/modules/webapp/src/main/elm/Comp/NotificationManage.elm @@ -0,0 +1,255 @@ +module Comp.NotificationManage exposing + ( Model + , Msg + , init + , update + , view + ) + +import Api +import Api.Model.BasicResult exposing (BasicResult) +import Api.Model.NotificationSettings exposing (NotificationSettings) +import Api.Model.NotificationSettingsList exposing (NotificationSettingsList) +import Comp.NotificationForm +import Comp.NotificationList +import Data.Flags exposing (Flags) +import Data.UiSettings exposing (UiSettings) +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (onClick) +import Http +import Util.Http + + +type alias Model = + { listModel : Comp.NotificationList.Model + , detailModel : Maybe Comp.NotificationForm.Model + , items : List NotificationSettings + , result : Maybe BasicResult + } + + +type Msg + = ListMsg Comp.NotificationList.Msg + | DetailMsg Comp.NotificationForm.Msg + | GetDataResp (Result Http.Error NotificationSettingsList) + | NewTask + | SubmitResp Bool (Result Http.Error BasicResult) + | DeleteResp (Result Http.Error BasicResult) + + +initModel : Model +initModel = + { listModel = Comp.NotificationList.init + , detailModel = Nothing + , items = [] + , result = Nothing + } + + +initCmd : Flags -> Cmd Msg +initCmd flags = + Api.getNotifyDueItems flags GetDataResp + + +init : Flags -> ( Model, Cmd Msg ) +init flags = + ( initModel, initCmd flags ) + + + +--- Update + + +update : Flags -> Msg -> Model -> ( Model, Cmd Msg ) +update flags msg model = + case msg of + GetDataResp (Ok res) -> + ( { model + | items = res.items + , result = Nothing + } + , Cmd.none + ) + + GetDataResp (Err err) -> + ( { model | result = Just (BasicResult False (Util.Http.errorToString err)) } + , Cmd.none + ) + + ListMsg lm -> + let + ( mm, action ) = + Comp.NotificationList.update lm model.listModel + + ( detail, cmd ) = + case action of + Comp.NotificationList.NoAction -> + ( Nothing, Cmd.none ) + + Comp.NotificationList.EditAction settings -> + let + ( dm, dc ) = + Comp.NotificationForm.initWith flags settings + in + ( Just dm, Cmd.map DetailMsg dc ) + in + ( { model + | listModel = mm + , detailModel = detail + } + , cmd + ) + + DetailMsg lm -> + case model.detailModel of + Just dm -> + let + ( mm, action, mc ) = + Comp.NotificationForm.update flags lm dm + + ( model_, cmd_ ) = + case action of + Comp.NotificationForm.NoAction -> + ( { model | detailModel = Just mm } + , Cmd.none + ) + + Comp.NotificationForm.SubmitAction settings -> + ( { model + | detailModel = Just mm + , result = Nothing + } + , if settings.id == "" then + Api.createNotifyDueItems flags settings (SubmitResp True) + + else + Api.updateNotifyDueItems flags settings (SubmitResp True) + ) + + Comp.NotificationForm.CancelAction -> + ( { model + | detailModel = Nothing + , result = Nothing + } + , initCmd flags + ) + + Comp.NotificationForm.StartOnceAction settings -> + ( { model + | detailModel = Just mm + , result = Nothing + } + , Api.startOnceNotifyDueItems flags settings (SubmitResp False) + ) + + Comp.NotificationForm.DeleteAction id -> + ( { model + | detailModel = Just mm + , result = Nothing + } + , Api.deleteNotifyDueItems flags id DeleteResp + ) + in + ( model_ + , Cmd.batch + [ Cmd.map DetailMsg mc + , cmd_ + ] + ) + + Nothing -> + ( model, Cmd.none ) + + NewTask -> + let + ( mm, mc ) = + Comp.NotificationForm.init flags + in + ( { model | detailModel = Just mm }, Cmd.map DetailMsg mc ) + + SubmitResp close (Ok res) -> + ( { model + | result = Just res + , detailModel = + if close then + Nothing + + else + model.detailModel + } + , if close then + initCmd flags + + else + Cmd.none + ) + + SubmitResp _ (Err err) -> + ( { model | result = Just (BasicResult False (Util.Http.errorToString err)) } + , Cmd.none + ) + + DeleteResp (Ok res) -> + if res.success then + ( { model | result = Nothing, detailModel = Nothing } + , initCmd flags + ) + + else + ( { model | result = Just res } + , Cmd.none + ) + + DeleteResp (Err err) -> + ( { model | result = Just (BasicResult False (Util.Http.errorToString err)) } + , Cmd.none + ) + + + +--- View + + +view : UiSettings -> Model -> Html Msg +view settings model = + div [] + [ div [ class "ui menu" ] + [ a + [ class "link item" + , href "#" + , onClick NewTask + ] + [ i [ class "add icon" ] [] + , text "New Task" + ] + ] + , div + [ classList + [ ( "ui message", True ) + , ( "error", Maybe.map .success model.result == Just False ) + , ( "success", Maybe.map .success model.result == Just True ) + , ( "invisible hidden", model.result == Nothing ) + ] + ] + [ Maybe.map .message model.result + |> Maybe.withDefault "" + |> text + ] + , case model.detailModel of + Just msett -> + viewForm settings msett + + Nothing -> + viewList model + ] + + +viewForm : UiSettings -> Comp.NotificationForm.Model -> Html Msg +viewForm settings model = + Html.map DetailMsg (Comp.NotificationForm.view "segment" settings model) + + +viewList : Model -> Html Msg +viewList model = + Html.map ListMsg (Comp.NotificationList.view model.listModel model.items) diff --git a/modules/webapp/src/main/elm/Comp/ScanMailboxList.elm b/modules/webapp/src/main/elm/Comp/ScanMailboxList.elm index d21640bc..98d6f9d5 100644 --- a/modules/webapp/src/main/elm/Comp/ScanMailboxList.elm +++ b/modules/webapp/src/main/elm/Comp/ScanMailboxList.elm @@ -8,7 +8,6 @@ module Comp.ScanMailboxList exposing ) import Api.Model.ScanMailboxSettings exposing (ScanMailboxSettings) -import Api.Model.ScanMailboxSettingsList exposing (ScanMailboxSettingsList) import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) @@ -41,17 +40,17 @@ update msg model = view : Model -> List ScanMailboxSettings -> Html Msg -view model items = +view _ items = div [] - [ table [ class "ui very basic table" ] + [ table [ class "ui very basic center aligned table" ] [ thead [] [ th [ class "collapsing" ] [] , th [ class "collapsing" ] [ i [ class "check icon" ] [] ] + , th [] [ text "Schedule" ] , th [] [ text "Connection" ] , th [] [ text "Folders" ] - , th [] [ text "Schedule" ] , th [] [ text "Received Since" ] , th [] [ text "Target" ] , th [] [ text "Delete" ] @@ -78,21 +77,22 @@ viewItem item = , td [ class "collapsing" ] [ Util.Html.checkbox item.enabled ] + , td [] + [ code [] + [ text item.schedule + ] + ] , td [] [ text item.imapConnection ] , td [] [ String.join ", " item.folders |> text ] - , td [] - [ code [] - [ text item.schedule - ] - ] , td [] [ Maybe.map String.fromInt item.receivedSinceHours |> Maybe.withDefault "-" |> text + , text " h" ] , td [] [ Maybe.withDefault "-" item.targetFolder diff --git a/modules/webapp/src/main/elm/Comp/ScanMailboxManage.elm b/modules/webapp/src/main/elm/Comp/ScanMailboxManage.elm index 164d831a..9bbd854a 100644 --- a/modules/webapp/src/main/elm/Comp/ScanMailboxManage.elm +++ b/modules/webapp/src/main/elm/Comp/ScanMailboxManage.elm @@ -34,7 +34,7 @@ type Msg | DetailMsg Comp.ScanMailboxForm.Msg | GetDataResp (Result Http.Error ScanMailboxSettingsList) | NewTask - | SubmitResp (Result Http.Error BasicResult) + | SubmitResp Bool (Result Http.Error BasicResult) | DeleteResp (Result Http.Error BasicResult) @@ -121,10 +121,10 @@ update flags msg model = , result = Nothing } , if settings.id == "" then - Api.createScanMailbox flags settings SubmitResp + Api.createScanMailbox flags settings (SubmitResp True) else - Api.updateScanMailbox flags settings SubmitResp + Api.updateScanMailbox flags settings (SubmitResp True) ) Comp.ScanMailboxForm.CancelAction -> @@ -140,7 +140,7 @@ update flags msg model = | detailModel = Just mm , result = Nothing } - , Api.startOnceScanMailbox flags settings SubmitResp + , Api.startOnceScanMailbox flags settings (SubmitResp False) ) Comp.ScanMailboxForm.DeleteAction id -> @@ -168,12 +168,24 @@ update flags msg model = in ( { model | detailModel = Just mm }, Cmd.map DetailMsg mc ) - SubmitResp (Ok res) -> - ( { model | result = Just res } - , Cmd.none + SubmitResp close (Ok res) -> + ( { model + | result = Just res + , detailModel = + if close then + Nothing + + else + model.detailModel + } + , if close then + initCmd flags + + else + Cmd.none ) - SubmitResp (Err err) -> + SubmitResp _ (Err err) -> ( { model | result = Just (BasicResult False (Util.Http.errorToString err)) } , Cmd.none ) diff --git a/modules/webapp/src/main/elm/Page/UserSettings/Data.elm b/modules/webapp/src/main/elm/Page/UserSettings/Data.elm index ac3f772e..d0d08613 100644 --- a/modules/webapp/src/main/elm/Page/UserSettings/Data.elm +++ b/modules/webapp/src/main/elm/Page/UserSettings/Data.elm @@ -8,7 +8,7 @@ module Page.UserSettings.Data exposing import Comp.ChangePasswordForm import Comp.EmailSettingsManage import Comp.ImapSettingsManage -import Comp.NotificationForm +import Comp.NotificationManage import Comp.ScanMailboxManage import Comp.UiSettingsManage import Data.Flags exposing (Flags) @@ -20,7 +20,7 @@ type alias Model = , changePassModel : Comp.ChangePasswordForm.Model , emailSettingsModel : Comp.EmailSettingsManage.Model , imapSettingsModel : Comp.ImapSettingsManage.Model - , notificationModel : Comp.NotificationForm.Model + , notificationModel : Comp.NotificationManage.Model , scanMailboxModel : Comp.ScanMailboxManage.Model , uiSettingsModel : Comp.UiSettingsManage.Model } @@ -36,7 +36,7 @@ init flags settings = , changePassModel = Comp.ChangePasswordForm.emptyModel , emailSettingsModel = Comp.EmailSettingsManage.emptyModel , imapSettingsModel = Comp.ImapSettingsManage.emptyModel - , notificationModel = Tuple.first (Comp.NotificationForm.init flags settings) + , notificationModel = Tuple.first (Comp.NotificationManage.init flags) , scanMailboxModel = Tuple.first (Comp.ScanMailboxManage.init flags) , uiSettingsModel = um } @@ -57,7 +57,7 @@ type Msg = SetTab Tab | ChangePassMsg Comp.ChangePasswordForm.Msg | EmailSettingsMsg Comp.EmailSettingsManage.Msg - | NotificationMsg Comp.NotificationForm.Msg + | NotificationMsg Comp.NotificationManage.Msg | ImapSettingsMsg Comp.ImapSettingsManage.Msg | ScanMailboxMsg Comp.ScanMailboxManage.Msg | UiSettingsMsg Comp.UiSettingsManage.Msg diff --git a/modules/webapp/src/main/elm/Page/UserSettings/Update.elm b/modules/webapp/src/main/elm/Page/UserSettings/Update.elm index 7bc0551a..d3f1fb6e 100644 --- a/modules/webapp/src/main/elm/Page/UserSettings/Update.elm +++ b/modules/webapp/src/main/elm/Page/UserSettings/Update.elm @@ -3,7 +3,7 @@ module Page.UserSettings.Update exposing (update) import Comp.ChangePasswordForm import Comp.EmailSettingsManage import Comp.ImapSettingsManage -import Comp.NotificationForm +import Comp.NotificationManage import Comp.ScanMailboxManage import Comp.UiSettingsManage import Data.Flags exposing (Flags) @@ -41,7 +41,7 @@ update flags settings msg model = let initCmd = Cmd.map NotificationMsg - (Tuple.second (Comp.NotificationForm.init flags settings)) + (Tuple.second (Comp.NotificationManage.init flags)) in ( m, initCmd, Sub.none ) @@ -80,7 +80,7 @@ update flags settings msg model = NotificationMsg lm -> let ( m2, c2 ) = - Comp.NotificationForm.update flags lm model.notificationModel + Comp.NotificationManage.update flags lm model.notificationModel in ( { model | notificationModel = m2 } , Cmd.map NotificationMsg c2 diff --git a/modules/webapp/src/main/elm/Page/UserSettings/View.elm b/modules/webapp/src/main/elm/Page/UserSettings/View.elm index b2678a68..42b3a74a 100644 --- a/modules/webapp/src/main/elm/Page/UserSettings/View.elm +++ b/modules/webapp/src/main/elm/Page/UserSettings/View.elm @@ -3,7 +3,7 @@ module Page.UserSettings.View exposing (view) import Comp.ChangePasswordForm import Comp.EmailSettingsManage import Comp.ImapSettingsManage -import Comp.NotificationForm +import Comp.NotificationManage import Comp.ScanMailboxManage import Comp.UiSettingsManage import Data.UiSettings exposing (UiSettings) @@ -42,7 +42,7 @@ view settings model = viewEmailSettings settings model Just NotificationTab -> - viewNotificationForm settings model + viewNotificationManage settings model Just ImapSettingsTab -> viewImapSettings settings model @@ -127,8 +127,8 @@ viewChangePassword model = ] -viewNotificationForm : UiSettings -> Model -> List (Html Msg) -viewNotificationForm settings model = +viewNotificationManage : UiSettings -> Model -> List (Html Msg) +viewNotificationManage settings model = [ h2 [ class "ui header" ] [ i [ class "ui bullhorn icon" ] [] , div [ class "content" ] @@ -147,7 +147,7 @@ viewNotificationForm settings model = , text " days and sends this list via e-mail." ] , Html.map NotificationMsg - (Comp.NotificationForm.view "segment" settings model.notificationModel) + (Comp.NotificationManage.view settings model.notificationModel) ]