mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-12-01 12:24:28 +01:00
(ui) react component to change user's group
This commit is contained in:
parent
ac1d518ddc
commit
1d2b814d6f
@ -11,7 +11,7 @@ export default class MemberAPI {
|
||||
|
||||
static async create (user: User): Promise<User> {
|
||||
const data = serialize({ user });
|
||||
if (user.profile_attributes.user_avatar_attributes.attachment_files[0]) {
|
||||
if (user.profile_attributes?.user_avatar_attributes?.attachment_files[0]) {
|
||||
data.set('user[profile_attributes][user_avatar_attributes][attachment]', user.profile_attributes.user_avatar_attributes.attachment_files[0]);
|
||||
}
|
||||
const res: AxiosResponse<User> = await apiClient.post('/api/members', data, {
|
||||
@ -24,7 +24,7 @@ export default class MemberAPI {
|
||||
|
||||
static async update (user: User): Promise<User> {
|
||||
const data = serialize({ user });
|
||||
if (user.profile_attributes.user_avatar_attributes.attachment_files[0]) {
|
||||
if (user.profile_attributes?.user_avatar_attributes?.attachment_files[0]) {
|
||||
data.set('user[profile_attributes][user_avatar_attributes][attachment]', user.profile_attributes.user_avatar_attributes.attachment_files[0]);
|
||||
}
|
||||
const res: AxiosResponse<User> = await apiClient.patch(`/api/members/${user.id}`, data, {
|
||||
|
117
app/frontend/src/javascript/components/group/change-group.tsx
Normal file
117
app/frontend/src/javascript/components/group/change-group.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { User } from '../../models/user';
|
||||
import { Loader } from '../base/loader';
|
||||
import { IApplication } from '../../models/application';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { Group } from '../../models/group';
|
||||
import GroupAPI from '../../api/group';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { FormSelect } from '../form/form-select';
|
||||
import MemberAPI from '../../api/member';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
interface ChangeGroupProps {
|
||||
user: User,
|
||||
onSuccess: (message: string, user: User) => void,
|
||||
onError: (message: string) => void,
|
||||
allowChange?: boolean,
|
||||
}
|
||||
|
||||
/**
|
||||
* Option format, expected by react-select
|
||||
* @see https://github.com/JedWatson/react-select
|
||||
*/
|
||||
type selectOption = { value: number, label: string };
|
||||
|
||||
export const ChangeGroup: React.FC<ChangeGroupProps> = ({ user, onSuccess, onError, allowChange }) => {
|
||||
const { t } = useTranslation('shared');
|
||||
|
||||
const [groups, setGroups] = useState<Array<Group>>([]);
|
||||
const [changeRequested, setChangeRequested] = useState<boolean>(false);
|
||||
const [operator, setOperator] = useState<User>(null);
|
||||
|
||||
const { handleSubmit, control } = useForm();
|
||||
|
||||
useEffect(() => {
|
||||
GroupAPI.index({ disabled: false, admins: false }).then(setGroups).catch(onError);
|
||||
MemberAPI.current().then(setOperator).catch(onError);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setChangeRequested(false);
|
||||
}, [user, allowChange]);
|
||||
|
||||
/**
|
||||
* Displays or hide the form to change the group.
|
||||
*/
|
||||
const toggleChangeRequest = () => {
|
||||
setChangeRequested(!changeRequested);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the group changing is currently allowed.
|
||||
*/
|
||||
const canChangeGroup = (): boolean => {
|
||||
return allowChange;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the provided array of items to the react-select format
|
||||
*/
|
||||
const buildGroupsOptions = (): Array<selectOption> => {
|
||||
return groups?.map(t => {
|
||||
return { value: t.id, label: t.name };
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the group changing form is submitted.
|
||||
*/
|
||||
const onSubmit = (data: { group_id: number }) => {
|
||||
MemberAPI.update({ id: user.id, group_id: data.group_id } as User).then(res => {
|
||||
toggleChangeRequest();
|
||||
onSuccess(t('app.shared.change_group.success'), res);
|
||||
}).catch(onError);
|
||||
};
|
||||
|
||||
// do not render the component if no user were provided (we cannot change th group of nobody)
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<div className="change-group">
|
||||
<h3>{t('app.shared.change_group.title', { OPERATOR: operator?.id === user.id ? 'self' : 'admin' })}</h3>
|
||||
{!changeRequested && <div className="display">
|
||||
<div className="current-group">
|
||||
{groups.find(group => group.id === user.group_id)?.name}
|
||||
</div>
|
||||
{canChangeGroup() && <FabButton className="request-change-btn" onClick={toggleChangeRequest}>
|
||||
{t('app.shared.change_group.change', { OPERATOR: operator?.id === user.id ? 'self' : 'admin' })}
|
||||
</FabButton>}
|
||||
</div>}
|
||||
{changeRequested && <form className="change-group-form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<FormSelect options={buildGroupsOptions()} control={control} id="group_id" valueDefault={user.group_id} />
|
||||
<div className="actions">
|
||||
<FabButton className="cancel-btn" onClick={toggleChangeRequest}>{t('app.shared.change_group.cancel')}</FabButton>
|
||||
<FabButton type="submit" className="validate-btn">{t('app.shared.change_group.validate')}</FabButton>
|
||||
</div>
|
||||
</form>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ChangeGroup.defaultProps = {
|
||||
allowChange: true
|
||||
};
|
||||
|
||||
const ChangeGroupWrapper: React.FC<ChangeGroupProps> = (props) => {
|
||||
return (
|
||||
<Loader>
|
||||
<ChangeGroup {...props} />
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('changeGroup', react2angular(ChangeGroupWrapper, ['user', 'onSuccess', 'onError', 'allowChange']));
|
@ -76,8 +76,6 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
if ($scope.selectedPlan !== plan) {
|
||||
$scope.selectedPlan = plan;
|
||||
$scope.planSelectionTime = new Date();
|
||||
} else {
|
||||
$scope.selectedPlan = null;
|
||||
}
|
||||
} else {
|
||||
$scope.login();
|
||||
@ -184,6 +182,29 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
|
||||
$scope.coupon.applied = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the user has successfully changed his group
|
||||
*/
|
||||
$scope.onGroupUpdateSuccess = function (message, user) {
|
||||
growl.success(message);
|
||||
setTimeout(() => {
|
||||
$scope.ctrl.member = _.cloneDeep(user);
|
||||
$scope.$apply();
|
||||
}, 50);
|
||||
if (AuthService.isAuthorized('member') ||
|
||||
(AuthService.isAuthorized('manager') && $scope.currentUser.id !== $scope.ctrl.member.id)) {
|
||||
$rootScope.currentUser.group_id = user.group_id;
|
||||
Auth._currentUser.group_id = user.group_id;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if it is allowed the change the group of teh selected user
|
||||
*/
|
||||
$scope.isAllowedChangingGroup = function () {
|
||||
return $scope.ctrl.member && !$scope.selectedPlan && !$scope.paid.plan;
|
||||
};
|
||||
|
||||
/* PRIVATE SCOPE */
|
||||
|
||||
/**
|
||||
|
@ -33,6 +33,7 @@
|
||||
@import "modules/form/form-item";
|
||||
@import "modules/form/form-rich-text";
|
||||
@import "modules/form/form-switch";
|
||||
@import "modules/group/change-group";
|
||||
@import "modules/machines/machine-card";
|
||||
@import "modules/machines/machines-filters";
|
||||
@import "modules/machines/machines-list";
|
||||
|
50
app/frontend/src/stylesheets/modules/group/change-group.scss
Normal file
50
app/frontend/src/stylesheets/modules/group/change-group.scss
Normal file
@ -0,0 +1,50 @@
|
||||
.change-group {
|
||||
border: 1px solid #dddddd;
|
||||
border-radius: var(--border-radius);
|
||||
margin: 30px 15px;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
padding: 18px 15px;
|
||||
line-height: 1.8rem;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
.display,
|
||||
.change-group-form {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.display {
|
||||
.current-group {
|
||||
background-color: #999;
|
||||
color: #000;
|
||||
min-height: 20px;
|
||||
border: 1px solid #999;
|
||||
margin-bottom: 20px;
|
||||
padding: 5px 10px;
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 1.4rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.request-change-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.change-group-form {
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.validate-btn {
|
||||
border-color: var(--information-light);
|
||||
background-color: var(--information);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -39,31 +39,11 @@
|
||||
<select-member subscription="false"></select-member>
|
||||
</div>
|
||||
|
||||
<section class="widget panel b-a m m-t-lg" ng-show="ctrl.member">
|
||||
<div class="panel-heading b-b">
|
||||
<h3 ng-show="isAuthorized('member')" translate>{{ 'app.public.plans.my_group' }}</h3>
|
||||
<h3 ng-show="isAuthorized(['admin', 'manager'])" translate translate-values="{GENDER:getGender(ctrl.member)}">{{ 'app.public.plans.his_group' }}</h3>
|
||||
</div>
|
||||
<div class="widget-content no-bg auto wrapper">
|
||||
<div ng-show="!group.change">
|
||||
<div class="well well-warning">
|
||||
<strong>{{getUserGroup().name}}</strong>
|
||||
</div>
|
||||
<button class="btn btn-default btn-no-overflow m-t"
|
||||
ng-click="group.change = !group.change"
|
||||
ng-show="(!selectedPlan && ctrl.member && !ctrl.member.subscribed_plan && ctrl.member.subscription) || (!paid.plan)"
|
||||
translate
|
||||
translate-values="{ROLE:ctrl.member.role}">{{ 'app.public.plans.he_wants_to_change_group' }}</button>
|
||||
</div>
|
||||
<div ng-show="group.change">
|
||||
<select class="form-control" ng-options="g.id as g.name for g in groups" ng-model="group.id"></select>
|
||||
<button class="btn btn-success m-t"
|
||||
ng-click="selectGroup()"
|
||||
translate
|
||||
translate-values="{ROLE:ctrl.member.role, GENDER:getGender(ctrl.member)}">{{ 'app.public.plans.change_my_group' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<change-group user="ctrl.member"
|
||||
on-success="onGroupUpdateSuccess"
|
||||
on-error="onError"
|
||||
allow-change="isAllowedChangingGroup()">
|
||||
</change-group>
|
||||
|
||||
<section class="widget panel b-a m m-t-lg" ng-if="!selectedPlan && ctrl.member && !ctrl.member.subscribed_plan && ctrl.member.subscription">
|
||||
<div class="panel-heading b-b">
|
||||
|
@ -23,6 +23,12 @@ en:
|
||||
you_will_lose_any_unsaved_modification_if_you_reload_this_page: "You will lose any unsaved modification if you reload this page"
|
||||
payment_card_error: "A problem has occurred with your credit card:"
|
||||
payment_card_declined: "Your card was declined."
|
||||
change_group:
|
||||
title: "{OPERATOR, select, self{My group} other{User's group}}"
|
||||
change: "Change {OPERATOR, select, self{my} other{his}} group"
|
||||
cancel: "Cancel"
|
||||
validate: "Validate group change"
|
||||
success: "Group successfully changed"
|
||||
#text editor
|
||||
text_editor:
|
||||
text_placeholder: "Type something…"
|
||||
|
Loading…
Reference in New Issue
Block a user