Skip to content

Commit

Permalink
Roles edit page (thewca#8411)
Browse files Browse the repository at this point in the history
* Roles edit page

* Review changes

* Review changes

* Added authorization

* Added a test case

* Adding failed test case

* removed unused keys

* Review changes

* removed scope

* Try adding scope again

* review changes
  • Loading branch information
danieljames-dj authored Nov 2, 2023
1 parent 6a012e9 commit 25be47b
Show file tree
Hide file tree
Showing 13 changed files with 431 additions and 44 deletions.
88 changes: 88 additions & 0 deletions WcaOnRails/app/controllers/api/v0/roles_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# frozen_string_literal: true

class Api::V0::RolesController < Api::V0::ApiController
before_action :current_user_is_authorized_for_action!, only: [:update, :destroy]
private def current_user_is_authorized_for_action!
unless current_user.board_member? || current_user.senior_delegate?
render json: {}, status: 401
end
end

def index
user_id = params.require(:userId)
user = User.find(user_id)
is_delegate = user.delegate_status.present?
active_roles = is_delegate ? [{ role: user.delegate_status }] : []

render json: {
activeRoles: active_roles,
}
end

def show
user_id = params.require(:userId)
is_active_role = ActiveRecord::Type::Boolean.new.cast(params.require(:isActiveRole))
senior_delegates = User.where(delegate_status: "senior_delegate")

if is_active_role
user = User.find(user_id)
render json: {
roleData: {
delegateStatus: user.delegate_status,
seniorDelegateId: user.senior_delegate.id,
location: user.location,
},
seniorDelegates: senior_delegates,
}
else
render json: {
roleData: {},
seniorDelegates: senior_delegates,
}
end
end

def update
user_id = params.require(:userId)
delegate_status = params.require(:delegateStatus)
senior_delegate_id = params.require(:seniorDelegateId)
location = params.require(:location)

user = User.find(user_id)
user.update!(delegate_status: delegate_status, senior_delegate_id: senior_delegate_id, location: location)
send_role_change_notification(user)

render json: {
success: true,
}
end

def destroy
user_id = params.require(:userId)

user = User.find(user_id)
user.update!(delegate_status: '', senior_delegate_id: '', location: '')
send_role_change_notification(user)

render json: {
success: true,
}
end

private def send_role_change_notification(user)
if user.saved_change_to_delegate_status
if user.delegate_status
user_senior_delegate = user.senior_or_self
else
user_senior_delegate = User.find(user.senior_delegate_id_before_last_save)
end
DelegateStatusChangeMailer.notify_board_and_assistants_of_delegate_status_change(
user,
current_user,
user_senior_delegate,
user.delegate_status_before_last_save,
user.delegate_status,
).deliver_later
end
end
end
19 changes: 5 additions & 14 deletions WcaOnRails/app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ def edit
nil if redirect_if_cannot_edit_user(@user)
end

def role
@user_id = params[:user_id]
@role_id = params[:role_id]
end

def claim_wca_id
@user = current_user
end
Expand Down Expand Up @@ -169,20 +174,6 @@ def update

old_confirmation_sent_at = @user.confirmation_sent_at
if @user.update(user_params)
if @user.saved_change_to_delegate_status
if @user.delegate_status
@user_senior_delegate = @user.senior_or_self
else
@user_senior_delegate = User.find(@user.senior_delegate_id_before_last_save)
end
DelegateStatusChangeMailer.notify_board_and_assistants_of_delegate_status_change(
@user,
current_user,
@user_senior_delegate,
@user.delegate_status_before_last_save,
@user.delegate_status,
).deliver_later
end
if current_user == @user
# Sign in the user, bypassing validation in case their password changed
bypass_sign_in @user
Expand Down
14 changes: 8 additions & 6 deletions WcaOnRails/app/views/users/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
<% if current_user.can_change_users_avatar?(@user) %>
<li><a href="#avatar" data-toggle="tab">Avatar</a></li>
<% end %>
<% if current_user.can_view_all_users? %>
<li><a href="#roles" data-toggle="tab">Roles</a></li>
<% end %>
</ul>

<div class="tab-content" id="edit-user-tabs">
Expand Down Expand Up @@ -123,12 +126,6 @@
</p>
<% end %>

<% if current_user.can_view_all_users? %>
<%= f.input :delegate_status, disabled: !editable_fields.include?(:senior_delegate_id) %>
<%= f.association :senior_delegate, disabled: !editable_fields.include?(:senior_delegate_id) %>
<%= f.input :location, disabled: !editable_fields.include?(:location) %>
<% end %>

<% unless @user.current_teams.reject(&:hidden?).empty? %>
<div class="form-group">
<label><%= t '.member_of' %></label>
Expand Down Expand Up @@ -265,6 +262,11 @@
</div>
</div>
<% end %>
<div class="tab-pane" id="roles">
<%= react_component("RolesTab", {
userId: @user.id,
}) %>
</div>
</div>
<script>
$('a[href="#<%=j tab_to_show(params[:section]) %>"]').tab('show');
Expand Down
4 changes: 4 additions & 0 deletions WcaOnRails/app/views/users/role.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= react_component("RolesTab/RoleForm", {
userId: @user_id,
roleId: @role_id,
}) %>
53 changes: 53 additions & 0 deletions WcaOnRails/app/webpacker/components/RolesTab/DelegateForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';

import { Form } from 'semantic-ui-react';
import I18n from '../../lib/i18n';

// let i18n-tasks know the key is used
// i18n-tasks-use t('enums.user.delegate_status.trainee_delegate')
// i18n-tasks-use t('enums.user.delegate_status.candidate_delegate')
// i18n-tasks-use t('enums.user.delegate_status.delegate')

export default function DelegateForm({
formValues,
updateFormProperty,
seniorDelegates,
delegateStatusOptions,
}) {
const handleFormChange = (_, { name, value }) => updateFormProperty({ [name]: value });

return (
<>
<Form.Dropdown
label={I18n.t('activerecord.attributes.user.delegate_status')}
fluid
selection
name="delegateStatus"
value={formValues.delegateStatus}
options={delegateStatusOptions.map((option) => ({
text: I18n.t(`enums.user.delegate_status.${option}`),
value: option,
}))}
onChange={handleFormChange}
/>
{formValues.delegateStatus !== 'senior_delegate'
&& (
<Form.Dropdown
label={I18n.t('enums.user.delegate_status.senior_delegate')}
fluid
selection
name="seniorDelegateId"
value={formValues.seniorDelegateId || ''}
options={seniorDelegates}
onChange={handleFormChange}
/>
)}
<Form.Input
label={I18n.t('activerecord.attributes.user.location')}
name="location"
value={formValues.location || ''}
onChange={handleFormChange}
/>
</>
);
}
118 changes: 118 additions & 0 deletions WcaOnRails/app/webpacker/components/RolesTab/RoleForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React from 'react';

import { Form, Grid } from 'semantic-ui-react';

import _ from 'lodash';
import useLoadedData from '../../lib/hooks/useLoadedData';
import {
roleDataUrl,
roleUpdateUrl,
} from '../../lib/requests/routes.js.erb';
import useSaveAction from '../../lib/hooks/useSaveAction';
import Errored from '../Requests/Errored';
import DelegateForm from './DelegateForm';
import Loading from '../Requests/Loading';

const groups = [{
key: 'delegates',
text: 'Delegates',
value: 'Delegates',
}];

const delegateStatusOptions = ['trainee_delegate', 'candidate_delegate', 'delegate', 'senior_delegate'];

export default function RoleForm({ userId, isActiveRole }) {
const { data, loading, error } = useLoadedData(roleDataUrl(userId, isActiveRole));
const { save, saving } = useSaveAction();
const selectedGroup = groups[0].value;
const [formValues, setFormValues] = React.useState({});
const [apiError, setError] = React.useState(false);
const [finished, setFinished] = React.useState(false);

React.useEffect(() => {
setFormValues({
delegateStatus: delegateStatusOptions[0],
location: '',
...(data?.roleData || {}),
});
}, [data]);

const updateRole = () => {
save(
roleUpdateUrl,
{
userId,
...formValues,
},
() => setFinished(true),
{ method: 'PATCH' },
() => setError(true),
);
};

const endRole = () => {
save(
roleUpdateUrl,
{
userId,
},
() => setFinished(true),
{ method: 'DELETE' },
() => setError(true),
);
};

if (loading) return <Loading />;
if (error || apiError) return <Errored />;
if (finished) return 'Success...';

return (
<Form
onSubmit={updateRole}
loading={saving}
>
<Form.Dropdown
label="Group"
fluid
selection
value={selectedGroup}
options={groups}
/>
{selectedGroup === groups[0].value // Delegates is selected
&& (
<DelegateForm
formValues={formValues}
updateFormProperty={(values) => {
setFormValues({
...formValues,
...values,
});
}}
seniorDelegates={data?.seniorDelegates.map((seniorDelegate) => ({
key: seniorDelegate.id,
text: seniorDelegate.name,
value: seniorDelegate.id,
})) || []}
delegateStatusOptions={delegateStatusOptions}
/>
)}
<Grid>
<Form.Button
primary
type="submit"
disabled={(_.isEqual(formValues, data?.roleData))}
>
{isActiveRole ? 'Update Role' : 'Create Role'}
</Form.Button>
<Form.Button
secondary
type="button"
onClick={endRole}
disabled={!isActiveRole}
>
End Role
</Form.Button>
</Grid>
</Form>
);
}
Loading

0 comments on commit 25be47b

Please sign in to comment.