1
0
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:
Sylvain 2022-05-11 14:37:39 +02:00
parent ac1d518ddc
commit 1d2b814d6f
7 changed files with 204 additions and 29 deletions

View File

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

View 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']));

View File

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

View File

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

View 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;
}
}
}
}

View File

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

View File

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