diff --git a/src/UnisonShare/AddOrgMemberModal.elm b/src/UnisonShare/AddOrgMemberModal.elm index 9db6d3c3..2c15ead4 100644 --- a/src/UnisonShare/AddOrgMemberModal.elm +++ b/src/UnisonShare/AddOrgMemberModal.elm @@ -112,13 +112,10 @@ update appContext orgHandle currentMembers msg model = userHandles m = case m of OrgMember.UserMember { user } -> - Just user.handle - - _ -> - Nothing + user.handle usersToIgnore = - account.handle :: List.filterMap userHandles currentMembers + account.handle :: List.map userHandles currentMembers model_ = if Search.queryEquals q search then @@ -158,7 +155,7 @@ update appContext orgHandle currentMembers msg model = AddMemberFinished res -> case ( model, res ) of ( Saving member, Ok _ ) -> - ( Success member, Cmd.none, AddedMember (OrgMember.UserMember { user = member.user, roles = [ member.role ] }) ) + ( Success member, Cmd.none, AddedMember (OrgMember.UserMember { user = member.user, role = member.role }) ) ( Saving member, Err e ) -> ( Failure e member, Cmd.none, NoOutMsg ) @@ -197,7 +194,7 @@ fetchUsers appContext query = addMember : AppContext -> UserHandle -> PotentialOrgMember -> Cmd Msg addMember appContext orgHandle member = - ShareApi.createOrgRoleAssignment orgHandle [ OrgMember.UserMember { user = member.user, roles = [ member.role ] } ] + ShareApi.createOrgRoleMember orgHandle [ OrgMember.UserMember { user = member.user, role = member.role } ] |> HttpApi.toRequestWithEmptyResponse AddMemberFinished |> HttpApi.perform appContext.api diff --git a/src/UnisonShare/Api.elm b/src/UnisonShare/Api.elm index 57516d1c..acbc9b58 100644 --- a/src/UnisonShare/Api.elm +++ b/src/UnisonShare/Api.elm @@ -251,95 +251,60 @@ createOrg owner name orgHandle isCommercial = } -orgRoleAssignments : UserHandle -> Endpoint -orgRoleAssignments orgHandle = +orgRoleMembers : UserHandle -> Endpoint +orgRoleMembers orgHandle = let handle = UserHandle.toUnprefixedString orgHandle in GET - { path = [ "orgs", handle, "roles" ] + { path = [ "orgs", handle, "members" ] , queryParams = [] } -createOrgRoleAssignment : UserHandle -> List OrgMember -> Endpoint -createOrgRoleAssignment orgHandle members = +createOrgRoleMember : UserHandle -> List OrgMember -> Endpoint +createOrgRoleMember orgHandle members = let handle = UserHandle.toUnprefixedString orgHandle - toAssignment member = + encodeMember member = case member of OrgMember.UserMember u -> Encode.object - [ ( "subject" - , Encode.object - [ ( "kind", Encode.string "user" ) - , ( "id", Encode.string u.user.id ) - ] - ) - , ( "roles", Encode.list OrgRole.encode u.roles ) - ] - - OrgMember.TeamMember t -> - Encode.object - [ ( "subject" - , Encode.object - [ ( "kind", Encode.string "team" ) - , ( "id", Encode.string t.teamId ) - ] - ) - , ( "roles", Encode.list OrgRole.encode t.roles ) + [ ( "subject", Encode.string (UserHandle.toUnprefixedString u.user.handle) ) + , ( "role", OrgRole.encode u.role ) ] body = Encode.object - [ ( "role_assignments", Encode.list toAssignment members ) ] + [ ( "members", Encode.list encodeMember members ) ] in POST - { path = [ "orgs", handle, "roles" ] + { path = [ "orgs", handle, "members" ] , queryParams = [] , body = Http.jsonBody body } -deleteOrgRoleAssignment : UserHandle -> OrgMember -> Endpoint -deleteOrgRoleAssignment orgHandle member = +deleteOrgRoleMember : UserHandle -> OrgMember -> Endpoint +deleteOrgRoleMember orgHandle member = let handle = UserHandle.toUnprefixedString orgHandle - toAssignment member_ = + toMember member_ = case member_ of OrgMember.UserMember u -> - Encode.object - [ ( "subject" - , Encode.object - [ ( "kind", Encode.string "user" ) - , ( "id", Encode.string u.user.id ) - ] - ) - , ( "roles", Encode.list OrgRole.encode u.roles ) - ] - - OrgMember.TeamMember t -> - Encode.object - [ ( "subject" - , Encode.object - [ ( "kind", Encode.string "team" ) - , ( "id", Encode.string t.teamId ) - ] - ) - , ( "roles", Encode.list OrgRole.encode t.roles ) - ] + Encode.string (UserHandle.toUnprefixedString u.user.handle) body = Encode.object - [ ( "role_assignments", Encode.list toAssignment [ member ] ) ] + [ ( "members", Encode.list toMember [ member ] ) ] in DELETE - { path = [ "orgs", handle, "roles" ] + { path = [ "orgs", handle, "members" ] , queryParams = [] , body = Http.jsonBody body } diff --git a/src/UnisonShare/OrgMember.elm b/src/UnisonShare/OrgMember.elm index 76c71ad7..953532ce 100644 --- a/src/UnisonShare/OrgMember.elm +++ b/src/UnisonShare/OrgMember.elm @@ -2,37 +2,27 @@ module UnisonShare.OrgMember exposing (..) import Json.Decode as Decode exposing (string) import Json.Decode.Extra exposing (when) -import Json.Decode.Pipeline exposing (required, requiredAt) +import Json.Decode.Pipeline exposing (required) import Lib.Decode.Helpers exposing (failInvalid) import UnisonShare.OrgRole as OrgRole exposing (OrgRole) import UnisonShare.User as User exposing (UserSummaryWithId) -type OrgMember - = UserMember { roles : List OrgRole, user : UserSummaryWithId } - | TeamMember { roles : List OrgRole, teamId : String } +type + OrgMember + -- | TeamMember { role : OrgRole, teamId : String } + = UserMember { role : OrgRole, user : UserSummaryWithId } decodeOrgUserMember : Decode.Decoder OrgMember decodeOrgUserMember = let - make roles user = - UserMember { roles = roles, user = user } + make role user = + UserMember { role = role, user = user } in Decode.succeed make - |> required "roles" (Decode.list OrgRole.decode) - |> requiredAt [ "subject", "data" ] User.decodeSummaryWithId - - -decodeOrgTeamMember : Decode.Decoder OrgMember -decodeOrgTeamMember = - let - make roles teamId = - TeamMember { roles = roles, teamId = teamId } - in - Decode.succeed make - |> required "roles" (Decode.list OrgRole.decode) - |> requiredAt [ "subject", "data", "teamId" ] Decode.string + |> required "role" OrgRole.decode + |> required "subject" User.decodeSummaryWithId whenPathIs : List String -> String -> Decode.Decoder a -> Decode.Decoder a @@ -42,13 +32,8 @@ whenPathIs path val = decodeMaybe : Decode.Decoder (Maybe OrgMember) decodeMaybe = - let - whenKindIs kind = - whenPathIs [ "subject", "kind" ] kind - in Decode.oneOf - [ whenKindIs "user" (Decode.map Just decodeOrgUserMember) - , whenKindIs "team" (Decode.map Just decodeOrgTeamMember) + [ Decode.map Just decodeOrgUserMember , Decode.succeed Nothing ] diff --git a/src/UnisonShare/Page/OrgPeoplePage.elm b/src/UnisonShare/Page/OrgPeoplePage.elm index e9488e27..6379108f 100644 --- a/src/UnisonShare/Page/OrgPeoplePage.elm +++ b/src/UnisonShare/Page/OrgPeoplePage.elm @@ -110,9 +110,6 @@ update appContext orgHandle msg model = OrgMember.UserMember { user } -> not (UserHandle.equals user.handle handle) - _ -> - True - members = RemoteData.map (List.filter withoutRemovedMember) @@ -180,16 +177,16 @@ update appContext orgHandle msg model = fetchMembers : AppContext -> UserHandle -> Cmd Msg fetchMembers appContext orgHandle = - ShareApi.orgRoleAssignments orgHandle + ShareApi.orgRoleMembers orgHandle |> HttpApi.toRequest - (Decode.field "role_assignments" OrgMember.decodeList) + (Decode.field "members" OrgMember.decodeList) (RemoteData.fromResult >> FetchMembersFinished) |> HttpApi.perform appContext.api removeMember : AppContext -> UserHandle -> UserHandle -> OrgMember -> Cmd Msg removeMember appContext orgHandle memberHandle member = - ShareApi.deleteOrgRoleAssignment orgHandle member + ShareApi.deleteOrgRoleMember orgHandle member |> HttpApi.toRequestWithEmptyResponse (RemoveMemberFinished memberHandle) |> HttpApi.perform appContext.api @@ -206,11 +203,11 @@ viewRole role = |> Tooltip.view (role |> OrgRole.toString |> text) -viewUserMember : ConfirmDeletes -> Bool -> { user : UserSummaryWithId, roles : List OrgRole } -> Html Msg -viewUserMember deletes isLastUser ({ user, roles } as member) = +viewUserMember : ConfirmDeletes -> Bool -> { user : UserSummaryWithId, role : OrgRole } -> Html Msg +viewUserMember deletes isLastUser ({ user, role } as member) = let canRemove = - not (List.member OrgRole.Owner roles) && not isLastUser + OrgRole.Owner /= role && not isLastUser remove = if canRemove then @@ -236,7 +233,7 @@ viewUserMember deletes isLastUser ({ user, roles } as member) = div [ class "member" ] [ div [ class "member_profile-snippet" ] [ ProfileSnippet.profileSnippet user |> ProfileSnippet.view ] - , div [ class "member_role" ] (roles |> List.map viewRole |> List.intersperse (text ", ")) + , div [ class "member_role" ] [ viewRole role ] , div [ class "member_remove" ] [ remove ] ] @@ -247,10 +244,6 @@ viewMember deletes isLastUser member = OrgMember.UserMember m -> viewUserMember deletes isLastUser m - OrgMember.TeamMember _ -> - -- TODO: Teams are not yet fully supported - UI.nothing - viewContent : Model -> List (Html Msg) viewContent model = diff --git a/src/css/confirm-delete.css b/src/css/confirm-delete.css index 274eef52..873f725e 100644 --- a/src/css/confirm-delete.css +++ b/src/css/confirm-delete.css @@ -5,3 +5,14 @@ font-size: var(--font-size-small); align-items: center; } + +.confirm-delete .status-banner { + font-size: var(--font-size-small); + gap: 0.25rem; + padding: 0.2rem 0.5rem; + padding-left: 0.25rem; +} +.confirm-delete .status-indicator.status-indicator_regular { + --c-size_status-indicator: 1rem; + --c-size_status-indicator_icon: 0.75rem; +} diff --git a/src/css/unison-share/page/org-people-page.css b/src/css/unison-share/page/org-people-page.css index 3a486121..17326229 100644 --- a/src/css/unison-share/page/org-people-page.css +++ b/src/css/unison-share/page/org-people-page.css @@ -25,9 +25,9 @@ .org-people-page .member_profile-snippet { width: 50%; } -.org-people-page .member_role, + .org-people-page .member_remove { - width: 25%; + width: 21rem; } .org-people-page .member_role { @@ -87,13 +87,9 @@ width: 8rem; text-align: right; } - - .org-people-page .member_remove { - width: 2rem; - } } -@container org-people-members (max-width: 500px) { +@container org-people-members (max-width: 750px) { .org-people-page .members_list { gap: 2rem; } diff --git a/tests/e2e/OrgPeoplePage.spec.ts b/tests/e2e/OrgPeoplePage.spec.ts index 75761ffa..1cf049cb 100644 --- a/tests/e2e/OrgPeoplePage.spec.ts +++ b/tests/e2e/OrgPeoplePage.spec.ts @@ -14,7 +14,7 @@ test.describe("without being signed in", () => { test("can *NOT* view the org people page", async ({ page }) => { const orgHandle = "@unison"; await API.getOrgProfile(page, orgHandle); - await API.getOrgRoleAssignments_(page, orgHandle, { status: 403 }); + await API.getOrgRoleMembers_(page, orgHandle, { status: 403 }); const response = await page.goto( `http://localhost:1234/${orgHandle}/p/people`, @@ -37,7 +37,7 @@ test.describe("without org:manage permission", () => { test("can *NOT* view the org people page", async ({ page }) => { const orgHandle = "@unison"; await API.getOrgProfile(page, orgHandle, { permissions: ["org:view"] }); - await API.getOrgRoleAssignments_(page, orgHandle, { status: 403 }); + await API.getOrgRoleMembers_(page, orgHandle, { status: 403 }); const response = await page.goto( `http://localhost:1234/${orgHandle}/p/people`, @@ -60,7 +60,7 @@ test.describe("with org:manage permission", () => { test("can view the org people page", async ({ page }) => { const orgHandle = "@unison"; await API.getOrgProfile(page, orgHandle, { permissions: ["org:manage"] }); - await API.getOrgRoleAssignments(page, orgHandle); + await API.getOrgRoleMembers(page, orgHandle); const response = await page.goto( `http://localhost:1234/${orgHandle}/p/people`, diff --git a/tests/e2e/TestHelpers/Api.ts b/tests/e2e/TestHelpers/Api.ts index aa2f5780..4d0469ac 100644 --- a/tests/e2e/TestHelpers/Api.ts +++ b/tests/e2e/TestHelpers/Api.ts @@ -182,7 +182,7 @@ async function getCatalog(page: Page) { }, { createdAt: "2023-04-03T17:05:08.873717Z", - isFaved: false, + ismembers: false, numFavs: 5, owner: { handle: "@systemfw", @@ -242,37 +242,33 @@ async function getOrgProfile(page: Page, handle: string, orgData = {}) { }); } -async function getOrgRoleAssignments( - page: Page, - handle: string, - assignments = null, -) { - return getOrgRoleAssignments_(page, handle, { +async function getOrgRoleMembers(page: Page, handle: string, members = null) { + return getOrgRoleMembers_(page, handle, { status: 200, - data: assignments, + data: members, }); } -async function getOrgRoleAssignments_( +async function getOrgRoleMembers_( page: Page, handle: string, resp: { status: number; data?: unknown[] }, ) { - function roleAssignment(roles: string[]) { + function roleMember(role: string) { return { - roles: roles, - subject: { data: user(), kind: "user" }, + role: role, + subject: user(), }; } return get(page, { - url: `/orgs/${handle.replace("@", "")}/roles`, + url: `/orgs/${handle.replace("@", "")}/members`, status: resp.status, data: { - role_assignments: resp.data || [ - roleAssignment(["org_admin"]), - roleAssignment(["org_viewer"]), - roleAssignment(["org_owner"]), + members: resp.data || [ + roleMember("org_admin"), + roleMember("org_viewer"), + roleMember("org_owner"), ], }, }); @@ -671,8 +667,8 @@ export { getAccount, getUserProfile, getOrgProfile, - getOrgRoleAssignments, - getOrgRoleAssignments_, + getOrgRoleMembers, + getOrgRoleMembers_, getProjects, getProject, getProject_,