diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm index f09d3a0c..aa2ff687 100644 --- a/modules/webapp/src/main/elm/Api.elm +++ b/modules/webapp/src/main/elm/Api.elm @@ -3,12 +3,15 @@ module Api exposing , addConcPerson , addCorrOrg , addCorrPerson + , addMember , addTag , cancelJob , changePassword + , changeSpaceName , checkCalEvent , createImapSettings , createMailSettings + , createNewSpace , createNotifyDueItems , createScanMailbox , deleteAttachment @@ -21,6 +24,7 @@ module Api exposing , deletePerson , deleteScanMailbox , deleteSource + , deleteSpace , deleteTag , deleteUser , getAttachmentMeta @@ -42,6 +46,7 @@ module Api exposing , getScanMailbox , getSentMails , getSources + , getSpaceDetail , getSpaces , getTags , getUsers @@ -62,6 +67,7 @@ module Api exposing , putUser , refreshSession , register + , removeMember , sendMail , setAttachmentName , setCollectiveSettings @@ -103,6 +109,7 @@ import Api.Model.EmailSettingsList exposing (EmailSettingsList) import Api.Model.Equipment exposing (Equipment) import Api.Model.EquipmentList exposing (EquipmentList) import Api.Model.GenInvite exposing (GenInvite) +import Api.Model.IdResult exposing (IdResult) import Api.Model.ImapSettings exposing (ImapSettings) import Api.Model.ImapSettingsList exposing (ImapSettingsList) import Api.Model.InviteResult exposing (InviteResult) @@ -115,6 +122,7 @@ import Api.Model.ItemSearch exposing (ItemSearch) import Api.Model.ItemUploadMeta exposing (ItemUploadMeta) import Api.Model.JobQueueState exposing (JobQueueState) import Api.Model.MoveAttachment exposing (MoveAttachment) +import Api.Model.NewSpace exposing (NewSpace) import Api.Model.NotificationSettings exposing (NotificationSettings) import Api.Model.NotificationSettingsList exposing (NotificationSettingsList) import Api.Model.OptionalDate exposing (OptionalDate) @@ -133,6 +141,7 @@ import Api.Model.SentMails exposing (SentMails) import Api.Model.SimpleMail exposing (SimpleMail) import Api.Model.Source exposing (Source) import Api.Model.SourceList exposing (SourceList) +import Api.Model.SpaceDetail exposing (SpaceDetail) import Api.Model.SpaceList exposing (SpaceList) import Api.Model.Tag exposing (Tag) import Api.Model.TagList exposing (TagList) @@ -155,11 +164,68 @@ import Util.Http as Http2 --- Spaces -getSpaces : Flags -> (Result Http.Error SpaceList -> msg) -> Cmd msg -getSpaces flags receive = - Http2.authGet +deleteSpace : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg +deleteSpace flags id receive = + Http2.authDelete + { url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id + , account = getAccount flags + , expect = Http.expectJson receive Api.Model.BasicResult.decoder + } + + +removeMember : Flags -> String -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg +removeMember flags id user receive = + Http2.authDelete + { url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id ++ "/member/" ++ user + , account = getAccount flags + , expect = Http.expectJson receive Api.Model.BasicResult.decoder + } + + +addMember : Flags -> String -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg +addMember flags id user receive = + Http2.authPut + { url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id ++ "/member/" ++ user + , account = getAccount flags + , body = Http.emptyBody + , expect = Http.expectJson receive Api.Model.BasicResult.decoder + } + + +changeSpaceName : Flags -> String -> NewSpace -> (Result Http.Error BasicResult -> msg) -> Cmd msg +changeSpaceName flags id ns receive = + Http2.authPut + { url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id + , account = getAccount flags + , body = Http.jsonBody (Api.Model.NewSpace.encode ns) + , expect = Http.expectJson receive Api.Model.BasicResult.decoder + } + + +createNewSpace : Flags -> NewSpace -> (Result Http.Error IdResult -> msg) -> Cmd msg +createNewSpace flags ns receive = + Http2.authPost { url = flags.config.baseUrl ++ "/api/v1/sec/space" , account = getAccount flags + , body = Http.jsonBody (Api.Model.NewSpace.encode ns) + , expect = Http.expectJson receive Api.Model.IdResult.decoder + } + + +getSpaceDetail : Flags -> String -> (Result Http.Error SpaceDetail -> msg) -> Cmd msg +getSpaceDetail flags id receive = + Http2.authGet + { url = flags.config.baseUrl ++ "/api/v1/sec/space/" ++ id + , account = getAccount flags + , expect = Http.expectJson receive Api.Model.SpaceDetail.decoder + } + + +getSpaces : Flags -> String -> (Result Http.Error SpaceList -> msg) -> Cmd msg +getSpaces flags query receive = + Http2.authGet + { url = flags.config.baseUrl ++ "/api/v1/sec/space?q=" ++ Url.percentEncode query + , account = getAccount flags , expect = Http.expectJson receive Api.Model.SpaceList.decoder } diff --git a/modules/webapp/src/main/elm/App/View.elm b/modules/webapp/src/main/elm/App/View.elm index bbf90084..376f5153 100644 --- a/modules/webapp/src/main/elm/App/View.elm +++ b/modules/webapp/src/main/elm/App/View.elm @@ -160,7 +160,11 @@ viewCollectiveSettings model = viewManageData : Model -> Html Msg viewManageData model = - Html.map ManageDataMsg (Page.ManageData.View.view model.uiSettings model.manageDataModel) + Html.map ManageDataMsg + (Page.ManageData.View.view model.flags + model.uiSettings + model.manageDataModel + ) viewLogin : Model -> Html Msg diff --git a/modules/webapp/src/main/elm/Comp/SpaceDetail.elm b/modules/webapp/src/main/elm/Comp/SpaceDetail.elm index 3df6c4cf..b6e9c571 100644 --- a/modules/webapp/src/main/elm/Comp/SpaceDetail.elm +++ b/modules/webapp/src/main/elm/Comp/SpaceDetail.elm @@ -2,6 +2,7 @@ module Comp.SpaceDetail exposing ( Model , Msg , init + , initEmpty , update , view ) @@ -9,10 +10,13 @@ module Comp.SpaceDetail exposing import Api import Api.Model.BasicResult exposing (BasicResult) import Api.Model.IdName exposing (IdName) +import Api.Model.IdResult exposing (IdResult) +import Api.Model.NewSpace exposing (NewSpace) import Api.Model.SpaceDetail exposing (SpaceDetail) import Api.Model.User exposing (User) import Api.Model.UserList exposing (UserList) import Comp.FixedDropdown +import Comp.YesNoDimmer import Data.Flags exposing (Flags) import Html exposing (..) import Html.Attributes exposing (..) @@ -24,40 +28,62 @@ import Util.Maybe type alias Model = { result : Maybe BasicResult + , space : SpaceDetail , name : Maybe String , members : List IdName , users : List User , memberDropdown : Comp.FixedDropdown.Model IdName , selectedMember : Maybe IdName + , loading : Bool + , deleteDimmer : Comp.YesNoDimmer.Model } type Msg = SetName String | MemberDropdownMsg (Comp.FixedDropdown.Msg IdName) + | SaveName + | NewSpaceResp (Result Http.Error IdResult) + | ChangeSpaceResp (Result Http.Error BasicResult) + | ChangeNameResp (Result Http.Error BasicResult) + | SpaceDetailResp (Result Http.Error SpaceDetail) + | AddMember + | RemoveMember IdName + | RequestDelete + | DeleteMsg Comp.YesNoDimmer.Msg + | DeleteResp (Result Http.Error BasicResult) + | GoBack init : List User -> SpaceDetail -> Model init users space = { result = Nothing + , space = space , name = Util.Maybe.fromString space.name , members = space.members , users = users , memberDropdown = Comp.FixedDropdown.initMap .name - (makeOptions users space.members) + (makeOptions users space) , selectedMember = Nothing + , loading = False + , deleteDimmer = Comp.YesNoDimmer.emptyModel } -makeOptions : List User -> List IdName -> List IdName -makeOptions users members = +initEmpty : List User -> Model +initEmpty users = + init users Api.Model.SpaceDetail.empty + + +makeOptions : List User -> SpaceDetail -> List IdName +makeOptions users space = let toIdName u = IdName u.id u.login notMember idn = - List.member idn members |> not + List.member idn (space.owner :: space.members) |> not in List.map toIdName users |> List.filter notMember @@ -67,12 +93,16 @@ makeOptions users members = --- Update -update : Flags -> Msg -> Model -> ( Model, Cmd Msg ) +update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Bool ) update flags msg model = case msg of + GoBack -> + ( model, Cmd.none, True ) + SetName str -> ( { model | name = Util.Maybe.fromString str } , Cmd.none + , False ) MemberDropdownMsg lmsg -> @@ -82,9 +112,160 @@ update flags msg model = in ( { model | memberDropdown = mm - , selectedMember = sel + , selectedMember = + case sel of + Just _ -> + sel + + Nothing -> + model.selectedMember } , Cmd.none + , False + ) + + SaveName -> + case model.name of + Just name -> + let + cmd = + if model.space.id == "" then + Api.createNewSpace flags (NewSpace name) NewSpaceResp + + else + Api.changeSpaceName flags + model.space.id + (NewSpace name) + ChangeNameResp + in + ( { model + | loading = True + , result = Nothing + } + , cmd + , False + ) + + Nothing -> + ( model, Cmd.none, False ) + + NewSpaceResp (Ok ir) -> + if ir.success then + ( model, Api.getSpaceDetail flags ir.id SpaceDetailResp, False ) + + else + ( { model + | loading = False + , result = Just (BasicResult ir.success ir.message) + } + , Cmd.none + , False + ) + + NewSpaceResp (Err err) -> + ( { model + | loading = False + , result = Just (BasicResult False (Util.Http.errorToString err)) + } + , Cmd.none + , False + ) + + ChangeSpaceResp (Ok r) -> + if r.success then + ( model + , Api.getSpaceDetail flags model.space.id SpaceDetailResp + , False + ) + + else + ( { model | loading = False, result = Just r } + , Cmd.none + , False + ) + + ChangeSpaceResp (Err err) -> + ( { model + | loading = False + , result = Just (BasicResult False (Util.Http.errorToString err)) + } + , Cmd.none + , False + ) + + ChangeNameResp (Ok r) -> + let + model_ = + { model | result = Just r, loading = False } + in + ( model_, Cmd.none, False ) + + ChangeNameResp (Err err) -> + ( { model + | result = Just (BasicResult False (Util.Http.errorToString err)) + , loading = False + } + , Cmd.none + , False + ) + + SpaceDetailResp (Ok sd) -> + ( init model.users sd, Cmd.none, False ) + + SpaceDetailResp (Err err) -> + ( { model + | loading = False + , result = Just (BasicResult False (Util.Http.errorToString err)) + } + , Cmd.none + , False + ) + + AddMember -> + case model.selectedMember of + Just mem -> + ( { model | loading = True } + , Api.addMember flags model.space.id mem.id ChangeSpaceResp + , False + ) + + Nothing -> + ( model, Cmd.none, False ) + + RemoveMember idname -> + ( { model | loading = True } + , Api.removeMember flags model.space.id idname.id ChangeSpaceResp + , False + ) + + RequestDelete -> + let + ( dm, _ ) = + Comp.YesNoDimmer.update Comp.YesNoDimmer.activate model.deleteDimmer + in + ( { model | deleteDimmer = dm }, Cmd.none, False ) + + DeleteMsg lm -> + let + ( dm, flag ) = + Comp.YesNoDimmer.update lm model.deleteDimmer + + cmd = + if flag then + Api.deleteSpace flags model.space.id DeleteResp + + else + Cmd.none + in + ( { model | deleteDimmer = dm }, cmd, False ) + + DeleteResp (Ok r) -> + ( { model | result = Just r }, Cmd.none, r.success ) + + DeleteResp (Err err) -> + ( { model | result = Just (BasicResult False (Util.Http.errorToString err)) } + , Cmd.none + , False ) @@ -92,13 +273,54 @@ update flags msg model = --- View -view : Model -> Html Msg -view model = +view : Flags -> Model -> Html Msg +view flags model = + let + isOwner = + Maybe.map .user flags.account + |> Maybe.map ((==) model.space.owner.name) + |> Maybe.withDefault False + in div [] - [ div [ class "ui header" ] + ([ Html.map DeleteMsg (Comp.YesNoDimmer.view model.deleteDimmer) + , if model.space.id == "" then + div [] + [ text "Create a new space. You are automatically set as owner of this new space." + ] + + else + div [] + [ text "Modify this space by changing the name or add/remove members." + ] + , if model.space.id /= "" && not isOwner then + div [ class "ui info message" ] + [ text "You are not the owner of this space and therefore are not allowed to edit it." + ] + + else + div [] [] + , div + [ classList + [ ( "ui message", True ) + , ( "invisible hidden", model.result == Nothing ) + , ( "error", Maybe.map .success model.result == Just False ) + , ( "success", Maybe.map .success model.result == Just True ) + ] + ] + [ Maybe.map .message model.result + |> Maybe.withDefault "" + |> text + ] + , div [ class "ui header" ] + [ text "Owner" + ] + , div [ class "" ] + [ text model.space.owner.name + ] + , div [ class "ui header" ] [ text "Name" ] - , div [ class "ui action input" ] + , div [ class "ui action input" ] [ input [ type_ "text" , onInput SetName @@ -108,11 +330,42 @@ view model = [] , button [ class "ui icon button" + , onClick SaveName ] [ i [ class "save icon" ] [] ] ] - , div [ class "ui header" ] + ] + ++ viewMembers model + ++ viewButtons model + ) + + +viewButtons : Model -> List (Html Msg) +viewButtons _ = + [ div [ class "ui divider" ] [] + , button + [ class "ui button" + , onClick GoBack + ] + [ text "Back" + ] + , button + [ class "ui red button" + , onClick RequestDelete + ] + [ text "Delete" + ] + ] + + +viewMembers : Model -> List (Html Msg) +viewMembers model = + if model.space.id == "" then + [] + + else + [ div [ class "ui header" ] [ text "Members" ] , div [ class "ui form" ] @@ -124,6 +377,8 @@ view model = ) , button [ class "ui primary button" + , title "Add a new member" + , onClick AddMember ] [ text "Add" ] @@ -146,9 +401,15 @@ viewMember member = div [ class "item" ] - [ button - [ class "ui primary icon button" + [ a + [ class "link icon" + , href "#" + , title "Remove this member" + , onClick (RemoveMember member) ] - [ i [ class "delete icon" ] [] + [ i [ class "red trash icon" ] [] + ] + , span [] + [ text member.name ] ] diff --git a/modules/webapp/src/main/elm/Comp/SpaceManage.elm b/modules/webapp/src/main/elm/Comp/SpaceManage.elm index a2af0fda..66aa2487 100644 --- a/modules/webapp/src/main/elm/Comp/SpaceManage.elm +++ b/modules/webapp/src/main/elm/Comp/SpaceManage.elm @@ -58,7 +58,7 @@ init flags = ( empty , Cmd.batch [ Api.getUsers flags UserListResp - , Api.getSpaces flags SpaceListResp + , Api.getSpaces flags "" SpaceListResp ] ) @@ -69,15 +69,109 @@ init flags = update : Flags -> Msg -> Model -> ( Model, Cmd Msg ) update flags msg model = - ( model, Cmd.none ) + case msg of + TableMsg lm -> + let + ( tm, action ) = + Comp.SpaceTable.update lm model.tableModel + + cmd = + case action of + Comp.SpaceTable.EditAction item -> + Api.getSpaceDetail flags item.id SpaceDetailResp + + Comp.SpaceTable.NoAction -> + Cmd.none + in + ( { model | tableModel = tm }, cmd ) + + DetailMsg lm -> + case model.detailModel of + Just detail -> + let + ( dm, dc, back ) = + Comp.SpaceDetail.update flags lm detail + + cmd = + if back then + Api.getSpaces flags model.query SpaceListResp + + else + Cmd.none + in + ( { model + | detailModel = + if back then + Nothing + + else + Just dm + } + , Cmd.batch + [ Cmd.map DetailMsg dc + , cmd + ] + ) + + Nothing -> + ( model, Cmd.none ) + + SetQuery str -> + ( { model | query = str }, Api.getSpaces flags str SpaceListResp ) + + UserListResp (Ok ul) -> + ( { model | users = ul.items }, Cmd.none ) + + UserListResp (Err err) -> + ( model, Cmd.none ) + + SpaceListResp (Ok sl) -> + ( { model | spaces = sl.items }, Cmd.none ) + + SpaceListResp (Err err) -> + ( model, Cmd.none ) + + SpaceDetailResp (Ok sd) -> + ( { model | detailModel = Comp.SpaceDetail.init model.users sd |> Just } + , Cmd.none + ) + + SpaceDetailResp (Err err) -> + ( model, Cmd.none ) + + InitNewSpace -> + let + sd = + Comp.SpaceDetail.initEmpty model.users + in + ( { model | detailModel = Just sd } + , Cmd.none + ) --- View -view : Model -> Html Msg -view model = +view : Flags -> Model -> Html Msg +view flags model = + case model.detailModel of + Just dm -> + viewDetail flags dm + + Nothing -> + viewTable model + + +viewDetail : Flags -> Comp.SpaceDetail.Model -> Html Msg +viewDetail flags detailModel = + div [] + [ Html.map DetailMsg (Comp.SpaceDetail.view flags detailModel) + ] + + +viewTable : Model -> Html Msg +viewTable model = div [] [ div [ class "ui secondary menu" ] [ div [ class "horizontally fitted item" ] diff --git a/modules/webapp/src/main/elm/Comp/SpaceTable.elm b/modules/webapp/src/main/elm/Comp/SpaceTable.elm index 10432ea2..acfac4dc 100644 --- a/modules/webapp/src/main/elm/Comp/SpaceTable.elm +++ b/modules/webapp/src/main/elm/Comp/SpaceTable.elm @@ -70,9 +70,7 @@ viewItem item = ] ] , td [] - [ code [] - [ text item.name - ] + [ text item.name ] , td [] [ text item.owner.name diff --git a/modules/webapp/src/main/elm/Page/ManageData/View.elm b/modules/webapp/src/main/elm/Page/ManageData/View.elm index ae3fa852..0829c920 100644 --- a/modules/webapp/src/main/elm/Page/ManageData/View.elm +++ b/modules/webapp/src/main/elm/Page/ManageData/View.elm @@ -5,6 +5,7 @@ import Comp.OrgManage import Comp.PersonManage import Comp.SpaceManage import Comp.TagManage +import Data.Flags exposing (Flags) import Data.Icons as Icons import Data.UiSettings exposing (UiSettings) import Html exposing (..) @@ -14,8 +15,8 @@ import Page.ManageData.Data exposing (..) import Util.Html exposing (classActive) -view : UiSettings -> Model -> Html Msg -view settings model = +view : Flags -> UiSettings -> Model -> Html Msg +view flags settings model = div [ class "managedata-page ui padded grid" ] [ div [ class "sixteen wide mobile four wide tablet four wide computer column" ] [ h4 [ class "ui top attached ablue-comp header" ] @@ -77,7 +78,7 @@ view settings model = viewPerson settings model Just SpaceTab -> - viewSpace settings model + viewSpace flags settings model Nothing -> [] @@ -86,8 +87,8 @@ view settings model = ] -viewSpace : UiSettings -> Model -> List (Html Msg) -viewSpace _ model = +viewSpace : Flags -> UiSettings -> Model -> List (Html Msg) +viewSpace flags _ model = [ h2 [ class "ui header" ] @@ -98,7 +99,7 @@ viewSpace _ model = [ text "Spaces" ] ] - , Html.map SpaceMsg (Comp.SpaceManage.view model.spaceManageModel) + , Html.map SpaceMsg (Comp.SpaceManage.view flags model.spaceManageModel) ]