Skip to content

Commit

Permalink
Admin can revoke user invites from Manage Users page (bigbluebutton#5846
Browse files Browse the repository at this point in the history
)

* Admin can revoke user invites from Manage Users page

* Added tests for revoking user invites

* - checked for user count changing when testing for #destroy for invitations\n- fixed locale strings for revoking invites\n- other minor fixes

* Added license header for Changed Revoke Invite to Revoke with archive box x mark as icon
  • Loading branch information
alihadimazeh authored Jun 19, 2024
1 parent 8aad7b2 commit ff77208
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 7 deletions.
6 changes: 4 additions & 2 deletions app/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@
"empty_invited_users_subtext": "When a user's status gets changed to invited, they will appear here.",
"invited": {
"time_sent": "Time Sent",
"valid": "Valid"
"valid": "Valid",
"revoke": "Revoke"
}
},
"server_rooms": {
Expand Down Expand Up @@ -430,7 +431,8 @@
"role_permission_updated": "The role permission has been updated."
},
"invitations": {
"invitation_sent": "An invitation has been sent."
"invitation_sent": "An invitation has been sent.",
"invitation_revoked": "An invitation has been revoked"
}
},
"error": {
Expand Down
9 changes: 9 additions & 0 deletions app/controllers/api/v1/admin/invitations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ def create
logger.error "Failed to send invitations to #{params[:invitations][:emails]} - #{e}"
render_error status: :bad_request
end

def destroy
invitation = Invitation.find(params[:id])
if invitation.destroy
render_data status: :ok
else
render_error status: :not_found
end
end
end
end
end
Expand Down
18 changes: 16 additions & 2 deletions app/javascript/components/admin/manage_users/InvitedUsersTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Table } from 'react-bootstrap';
import { Table, Dropdown } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import { CheckIcon, XMarkIcon } from '@heroicons/react/24/solid';
import { CheckIcon, XMarkIcon, ArchiveBoxXMarkIcon } from '@heroicons/react/24/solid';
import { EllipsisVerticalIcon } from '@heroicons/react/24/outline';
import SortBy from '../../shared_components/search/SortBy';
import useInvitations from '../../../hooks/queries/admin/manage_users/useInvitations';
import Pagination from '../../shared_components/Pagination';
Expand All @@ -27,12 +28,14 @@ import EmptyUsersList from './EmptyUsersList';
import { localizeDateTimeString } from '../../../helpers/DateTimeHelper';
import { useAuth } from '../../../contexts/auth/AuthProvider';
import ManageUsersInvitedRowPlaceHolder from './ManageUsersInvitedRowPlaceHolder';
import useRevokeUserInvite from '../../../hooks/mutations/admin/manage_users/useRevokeUserInvite';

export default function InvitedUsersTable({ searchInput }) {
const { t } = useTranslation();
const [page, setPage] = useState();
const { isLoading, data: invitations } = useInvitations(searchInput, page);
const currentUser = useAuth();
const revokeUserInvite = useRevokeUserInvite();

if (!searchInput && invitations?.data?.length === 0) {
return <EmptyUsersList text={t('admin.manage_users.empty_invited_users')} subtext={t('admin.manage_users.empty_invited_users_subtext')} />;
Expand Down Expand Up @@ -73,6 +76,17 @@ export default function InvitedUsersTable({ searchInput }) {
<td className="text-dark border-0">
{ invitation.valid ? <CheckIcon className="text-success hi-s" /> : <XMarkIcon className="text-danger hi-s" />}
</td>
<td className="text-dark border-0">
<Dropdown className="float-end cursor-pointer">
<Dropdown.Toggle className="hi-s" as={EllipsisVerticalIcon} />
<Dropdown.Menu>
<Dropdown.Item onClick={() => revokeUserInvite.mutate(invitation.id)}>
<ArchiveBoxXMarkIcon className="hi-s me-2" />
{t('admin.manage_users.invited.revoke')}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</td>
</tr>
))
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with Greenlight; if not, see <http://www.gnu.org/licenses/>.

import { useMutation, useQueryClient } from 'react-query';
import { toast } from 'react-toastify';
import { useTranslation } from 'react-i18next';
import axios from '../../../../helpers/Axios';

export default function useRevokeUserInvite() {
const { t } = useTranslation();
const queryClient = useQueryClient();

return useMutation(
(id) => axios.delete(`/admin/invitations/${id}.json`),
{
onSuccess: () => {
queryClient.invalidateQueries(['getInvitations']);
toast.success(t('toast.success.invitations.invitation_revoked'));
},
onError: () => {
toast.error(t('toast.error.problem_completing_action'));
},
},
);
}
2 changes: 1 addition & 1 deletion app/serializers/invitation_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# frozen_string_literal: true

class InvitationSerializer < ApplicationSerializer
attributes :email, :updated_at, :valid
attributes :id, :email, :updated_at, :valid

def valid
object.updated_at.in(Invitation::INVITATION_VALIDITY_PERIOD)
Expand Down
2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
end
resources :rooms_configurations, only: :update, param: :name
resources :roles
resources :invitations, only: %i[index create]
resources :invitations, only: %i[index create destroy]
resources :role_permissions, only: [:index] do
collection do
post '/', to: 'role_permissions#update'
Expand Down
15 changes: 14 additions & 1 deletion spec/controllers/admin/invitations_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

require 'rails_helper'

RSpec.describe Api::V1::Admin::InvitationsController, type: :controller do
RSpec.describe Api::V1::Admin::InvitationsController do
let(:user) { create(:user) }
let(:user_with_manage_users_permission) { create(:user, :with_manage_users_permission) }

Expand Down Expand Up @@ -105,4 +105,17 @@
end
end
end

describe 'invitation#destroy' do
it 'deletes the invitation' do
invitation = create(:invitation)
expect { delete :destroy, params: { id: invitation.id } }.to change(Invitation, :count).by(-1)
expect(response).to have_http_status(:ok)
end

it 'fails to delete the invitation if the id does not exist' do
expect { delete :destroy, params: { id: 'invalid-id' } }.not_to change(Invitation, :count)
expect(response).to have_http_status(:not_found)
end
end
end

0 comments on commit ff77208

Please sign in to comment.