Manage spaces in web-ui

This commit is contained in:
Eike Kettner 2020-07-09 01:12:09 +02:00
parent 752a94a9e2
commit 6c304b4e7a
6 changed files with 455 additions and 31 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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
]
]

View File

@ -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" ]

View File

@ -70,9 +70,7 @@ viewItem item =
]
]
, td []
[ code []
[ text item.name
]
[ text item.name
]
, td []
[ text item.owner.name

View File

@ -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)
]