1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-01 21:52:19 +01:00

(feat) admin can refuse child's supporting document file

This commit is contained in:
Du Peng 2023-05-26 12:54:39 +02:00
parent f1d3fdf2de
commit 42946effe0
22 changed files with 172 additions and 24 deletions

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useState } from 'react';
import { useForm, useWatch } from 'react-hook-form'; import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import moment from 'moment'; import moment from 'moment';
@ -9,25 +9,32 @@ import { FormFileUpload } from '../form/form-file-upload';
import { FileType } from '../../models/file'; import { FileType } from '../../models/file';
import { SupportingDocumentType } from '../../models/supporting-document-type'; import { SupportingDocumentType } from '../../models/supporting-document-type';
import { User } from '../../models/user'; import { User } from '../../models/user';
import { SupportingDocumentsRefusalModal } from '../supporting-documents/supporting-documents-refusal-modal';
interface ChildFormProps { interface ChildFormProps {
child: Child; child: Child;
operator: User; operator: User;
onSubmit: (data: Child) => void; onSubmit: (data: Child) => void;
supportingDocumentsTypes: Array<SupportingDocumentType>; supportingDocumentsTypes: Array<SupportingDocumentType>;
onSuccess: (message: string) => void,
onError: (message: string) => void,
} }
/** /**
* A form for creating or editing a child. * A form for creating or editing a child.
*/ */
export const ChildForm: React.FC<ChildFormProps> = ({ child, onSubmit, supportingDocumentsTypes, operator }) => { export const ChildForm: React.FC<ChildFormProps> = ({ child, onSubmit, supportingDocumentsTypes, operator, onSuccess, onError }) => {
const { t } = useTranslation('public'); const { t } = useTranslation('public');
const { register, formState, handleSubmit, setValue, control } = useForm<Child>({ const { register, formState, handleSubmit, setValue, control } = useForm<Child>({
defaultValues: child defaultValues: child
}); });
const output = useWatch<Child>({ control }); // eslint-disable-line const output = useWatch<Child>({ control }); // eslint-disable-line
const [refuseModalIsOpen, setRefuseModalIsOpen] = useState<boolean>(false);
/**
* get the name of the supporting document type by id
*/
const getSupportingDocumentsTypeName = (id: number): string => { const getSupportingDocumentsTypeName = (id: number): string => {
const supportingDocumentType = supportingDocumentsTypes.find((supportingDocumentType) => supportingDocumentType.id === id); const supportingDocumentType = supportingDocumentsTypes.find((supportingDocumentType) => supportingDocumentType.id === id);
return supportingDocumentType ? supportingDocumentType.name : ''; return supportingDocumentType ? supportingDocumentType.name : '';
@ -40,9 +47,24 @@ export const ChildForm: React.FC<ChildFormProps> = ({ child, onSubmit, supportin
return (operator?.role === 'admin' || operator?.role === 'manager'); return (operator?.role === 'admin' || operator?.role === 'manager');
}; };
/**
* Open/closes the modal dialog to refuse the documents
*/
const toggleRefuseModal = (): void => {
setRefuseModalIsOpen(!refuseModalIsOpen);
};
/**
* Callback triggered when the refusal was successfully saved
*/
const onSaveRefusalSuccess = (message: string): void => {
setRefuseModalIsOpen(false);
onSuccess(message);
};
return ( return (
<div className="child-form"> <div className="child-form">
{isPrivileged() && {!isPrivileged() &&
<div className="info-area"> <div className="info-area">
{t('app.public.child_form.child_form_info')} {t('app.public.child_form.child_form_info')}
</div> </div>
@ -113,6 +135,20 @@ export const ChildForm: React.FC<ChildFormProps> = ({ child, onSubmit, supportin
<FabButton type="button" onClick={handleSubmit(onSubmit)}> <FabButton type="button" onClick={handleSubmit(onSubmit)}>
{t('app.public.child_form.save')} {t('app.public.child_form.save')}
</FabButton> </FabButton>
{isPrivileged() &&
<div>
<FabButton className="refuse-btn" onClick={toggleRefuseModal}>{t('app.public.child_form.refuse_documents')}</FabButton>
<SupportingDocumentsRefusalModal
isOpen={refuseModalIsOpen}
proofOfIdentityTypes={supportingDocumentsTypes}
toggleModal={toggleRefuseModal}
operator={operator}
supportable={child}
documentType="Child"
onError={onError}
onSuccess={onSaveRefusalSuccess} />
</div>
}
</div> </div>
</form> </form>
</div> </div>

View File

@ -13,7 +13,7 @@ interface ChildModalProps {
operator: User; operator: User;
isOpen: boolean; isOpen: boolean;
toggleModal: () => void; toggleModal: () => void;
onSuccess: (child: Child, msg: string) => void; onSuccess: (msg: string) => void;
onError: (error: string) => void; onError: (error: string) => void;
supportingDocumentsTypes: Array<SupportingDocumentType>; supportingDocumentsTypes: Array<SupportingDocumentType>;
} }
@ -35,7 +35,7 @@ export const ChildModal: React.FC<ChildModalProps> = ({ child, isOpen, toggleMod
await ChildAPI.create(data); await ChildAPI.create(data);
} }
toggleModal(); toggleModal();
onSuccess(data, ''); onSuccess('');
} catch (error) { } catch (error) {
onError(error); onError(error);
} }
@ -51,7 +51,14 @@ export const ChildModal: React.FC<ChildModalProps> = ({ child, isOpen, toggleMod
{(operator?.role === 'admin' || operator?.role === 'manager') && {(operator?.role === 'admin' || operator?.role === 'manager') &&
<ChildValidation child={child} onSuccess={onSuccess} onError={onError} /> <ChildValidation child={child} onSuccess={onSuccess} onError={onError} />
} }
<ChildForm child={child} onSubmit={handleSaveChild} supportingDocumentsTypes={supportingDocumentsTypes} operator={operator}/> <ChildForm
child={child}
onSubmit={handleSaveChild}
supportingDocumentsTypes={supportingDocumentsTypes}
operator={operator}
onSuccess={onSuccess}
onError={onError}
/>
</FabModal> </FabModal>
); );
}; };

View File

@ -9,7 +9,7 @@ import { TDateISO } from '../../typings/date-iso';
interface ChildValidationProps { interface ChildValidationProps {
child: Child child: Child
onSuccess: (child: Child, message: string) => void, onSuccess: (message: string) => void,
onError: (message: string) => void, onError: (message: string) => void,
} }
@ -37,8 +37,8 @@ export const ChildValidation: React.FC<ChildValidationProps> = ({ child, onSucce
_child.validated_at = null; _child.validated_at = null;
} }
ChildAPI.validate(_child) ChildAPI.validate(_child)
.then((child: Child) => { .then(() => {
onSuccess(child, t(`app.admin.child_validation.${_value ? 'validate' : 'invalidate'}_child_success`)); onSuccess(t(`app.admin.child_validation.${_value ? 'validate' : 'invalidate'}_child_success`));
}).catch(err => { }).catch(err => {
setValue(!_value); setValue(!_value);
onError(t(`app.admin.child_validation.${_value ? 'validate' : 'invalidate'}_child_error`) + err); onError(t(`app.admin.child_validation.${_value ? 'validate' : 'invalidate'}_child_error`) + err);

View File

@ -78,7 +78,7 @@ export const ChildrenList: React.FC<ChildrenListProps> = ({ user, operator, onEr
/** /**
* Handle save child success from the API * Handle save child success from the API
*/ */
const handleSaveChildSuccess = (_child: Child, msg: string) => { const handleSaveChildSuccess = (msg: string) => {
ChildAPI.index({ user_id: user.id }).then(setChildren); ChildAPI.index({ user_id: user.id }).then(setChildren);
if (msg) { if (msg) {
onSuccess(msg); onSuccess(msg);

View File

@ -5,6 +5,7 @@ import { FabModal } from '../base/fab-modal';
import { SupportingDocumentType } from '../../models/supporting-document-type'; import { SupportingDocumentType } from '../../models/supporting-document-type';
import { SupportingDocumentRefusal } from '../../models/supporting-document-refusal'; import { SupportingDocumentRefusal } from '../../models/supporting-document-refusal';
import { User } from '../../models/user'; import { User } from '../../models/user';
import { Child } from '../../models/child';
import SupportingDocumentRefusalAPI from '../../api/supporting-document-refusal'; import SupportingDocumentRefusalAPI from '../../api/supporting-document-refusal';
import { SupportingDocumentsRefusalForm } from './supporting-documents-refusal-form'; import { SupportingDocumentsRefusalForm } from './supporting-documents-refusal-form';
@ -15,20 +16,21 @@ interface SupportingDocumentsRefusalModalProps {
onError: (message: string) => void, onError: (message: string) => void,
proofOfIdentityTypes: Array<SupportingDocumentType>, proofOfIdentityTypes: Array<SupportingDocumentType>,
operator: User, operator: User,
member: User supportable: User | Child,
documentType: 'User' | 'Child',
} }
/** /**
* Modal dialog to notify the member that his documents are refused * Modal dialog to notify the member that his documents are refused
*/ */
export const SupportingDocumentsRefusalModal: React.FC<SupportingDocumentsRefusalModalProps> = ({ isOpen, toggleModal, onSuccess, proofOfIdentityTypes, operator, member, onError }) => { export const SupportingDocumentsRefusalModal: React.FC<SupportingDocumentsRefusalModalProps> = ({ isOpen, toggleModal, onSuccess, proofOfIdentityTypes, operator, supportable, onError, documentType }) => {
const { t } = useTranslation('admin'); const { t } = useTranslation('admin');
const [data, setData] = useState<SupportingDocumentRefusal>({ const [data, setData] = useState<SupportingDocumentRefusal>({
id: null, id: null,
operator_id: operator.id, operator_id: operator.id,
supportable_id: member.id, supportable_id: supportable.id,
supportable_type: 'User', supportable_type: documentType,
supporting_document_type_ids: [], supporting_document_type_ids: [],
message: '' message: ''
}); });

View File

@ -19,6 +19,7 @@ declare const Application: IApplication;
interface SupportingDocumentsValidationProps { interface SupportingDocumentsValidationProps {
operator: User, operator: User,
member: User member: User
documentType: 'User' | 'Child',
onSuccess: (message: string) => void, onSuccess: (message: string) => void,
onError: (message: string) => void, onError: (message: string) => void,
} }
@ -26,7 +27,7 @@ interface SupportingDocumentsValidationProps {
/** /**
* This component shows a list of supporting documents file of member, admin can download and valid * This component shows a list of supporting documents file of member, admin can download and valid
**/ **/
const SupportingDocumentsValidation: React.FC<SupportingDocumentsValidationProps> = ({ operator, member, onSuccess, onError }) => { const SupportingDocumentsValidation: React.FC<SupportingDocumentsValidationProps> = ({ operator, member, onSuccess, onError, documentType }) => {
const { t } = useTranslation('admin'); const { t } = useTranslation('admin');
// list of supporting documents type // list of supporting documents type
@ -112,7 +113,8 @@ const SupportingDocumentsValidation: React.FC<SupportingDocumentsValidationProps
proofOfIdentityTypes={documentsTypes} proofOfIdentityTypes={documentsTypes}
toggleModal={toggleModal} toggleModal={toggleModal}
operator={operator} operator={operator}
member={member} supportable={member}
documentType={documentType}
onError={onError} onError={onError}
onSuccess={onSaveRefusalSuccess}/> onSuccess={onSaveRefusalSuccess}/>
</FabPanel> </FabPanel>
@ -131,4 +133,4 @@ const SupportingDocumentsValidationWrapper: React.FC<SupportingDocumentsValidati
export { SupportingDocumentsValidationWrapper as SupportingDocumentsValidation }; export { SupportingDocumentsValidationWrapper as SupportingDocumentsValidation };
Application.Components.component('supportingDocumentsValidation', react2angular(SupportingDocumentsValidationWrapper, ['operator', 'member', 'onSuccess', 'onError'])); Application.Components.component('supportingDocumentsValidation', react2angular(SupportingDocumentsValidationWrapper, ['operator', 'member', 'onSuccess', 'onError', 'documentType']));

View File

@ -70,6 +70,7 @@
<supporting-documents-validation <supporting-documents-validation
operator="currentUser" operator="currentUser"
member="user" member="user"
document-type="User"
on-error="onError" on-error="onError"
on-success="onSuccess" /> on-success="onSuccess" />
</uib-tab> </uib-tab>

View File

@ -17,4 +17,8 @@ class Child < ApplicationRecord
def validate_age def validate_age
errors.add(:birthday, I18n.t('.errors.messages.birthday_less_than_18_years_ago')) if birthday.blank? || birthday > 18.years.ago errors.add(:birthday, I18n.t('.errors.messages.birthday_less_than_18_years_ago')) if birthday.blank? || birthday > 18.years.ago
end end
def full_name
"#{(first_name || '').humanize.titleize} #{(last_name || '').humanize.titleize}"
end
end end

View File

@ -14,13 +14,23 @@ class SupportingDocumentRefusalService
def self.create(supporting_document_refusal) def self.create(supporting_document_refusal)
saved = supporting_document_refusal.save saved = supporting_document_refusal.save
if saved && supporting_document_refusal.supportable_type == 'User' if saved
case supporting_document_refusal.supportable_type
when 'User'
NotificationCenter.call type: 'notify_admin_user_supporting_document_refusal', NotificationCenter.call type: 'notify_admin_user_supporting_document_refusal',
receiver: User.admins_and_managers, receiver: User.admins_and_managers,
attached_object: supporting_document_refusal attached_object: supporting_document_refusal
NotificationCenter.call type: 'notify_user_supporting_document_refusal', NotificationCenter.call type: 'notify_user_supporting_document_refusal',
receiver: supporting_document_refusal.supportable, receiver: supporting_document_refusal.supportable,
attached_object: supporting_document_refusal attached_object: supporting_document_refusal
when 'Child'
NotificationCenter.call type: 'notify_admin_user_child_supporting_document_refusal',
receiver: User.admins_and_managers,
attached_object: SupportingDocumentRefusal.last
NotificationCenter.call type: 'notify_user_child_supporting_document_refusal',
receiver: SupportingDocumentRefusal.last.supportable.user,
attached_object: SupportingDocumentRefusal.last
end
end end
saved saved
end end

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
json.title notification.notification_type
json.description t('.refusal',
NAME: notification.attached_object&.supportable&.full_name || t('api.notifications.deleted_user'))

View File

@ -0,0 +1,4 @@
# frozen_string_literal: true
json.title notification.notification_type
json.description t('.refusal')

View File

@ -0,0 +1,15 @@
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
<p>
<%= t('.body.user_child_supporting_document_files_refusal',
NAME: @attached_object&.supportable&.full_name || t('api.notifications.deleted_user'),
OPERATOR: @attached_object&.operator&.profile&.full_name || t('api.notifications.deleted_user')) %>
</p>
<ul>
<% @attached_object.supporting_document_types.each do |type| %>
<li><%= type.name %></li>
<% end %>
</ul>
<p>
<%= @attached_object.message %>
</p>

View File

@ -0,0 +1,16 @@
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
<p>
<%= t('.body.user_child_supporting_document_files_refusal') %>
</p>
<ul>
<% @attached_object.supporting_document_types.each do |type| %>
<li><%= type.name %></li>
<% end %>
</ul>
<p>
<%= @attached_object.message %>
</p>
<p>
<%= t('.body.action') %>
</p>

View File

@ -291,6 +291,7 @@ en:
notify_admin_import_complete: "An import is done" notify_admin_import_complete: "An import is done"
notify_admin_user_group_changed: "A user has changed his group" notify_admin_user_group_changed: "A user has changed his group"
notify_admin_user_supporting_document_refusal: "A supporting document has been rejected" notify_admin_user_supporting_document_refusal: "A supporting document has been rejected"
notify_admin_user_child_supporting_document_refusal: "A supporting document of child has been rejected"
notify_admin_user_supporting_document_files_created: "A user has uploaded a supporting document" notify_admin_user_supporting_document_files_created: "A user has uploaded a supporting document"
notify_admin_user_supporting_document_files_updated: "A user has updated a supporting document" notify_admin_user_supporting_document_files_updated: "A user has updated a supporting document"
notify_admin_member_create_reservation: "A member books a reservation" notify_admin_member_create_reservation: "A member books a reservation"

View File

@ -291,6 +291,7 @@ fr:
notify_admin_import_complete: "Un import est terminé" notify_admin_import_complete: "Un import est terminé"
notify_admin_user_group_changed: "Un utilisateur a changé de groupe" notify_admin_user_group_changed: "Un utilisateur a changé de groupe"
notify_admin_user_supporting_document_refusal: "Un justificatif a été refusé" notify_admin_user_supporting_document_refusal: "Un justificatif a été refusé"
notify_admin_user_child_supporting_document_refusal: "Un justificatif de l'enfant a été refusé"
notify_admin_user_supporting_document_files_created: "Un utilisateur a téléversé un justificatif" notify_admin_user_supporting_document_files_created: "Un utilisateur a téléversé un justificatif"
notify_admin_user_supporting_document_files_updated: "Un utilisateur a mis à jour un justificatif" notify_admin_user_supporting_document_files_updated: "Un utilisateur a mis à jour un justificatif"
notify_admin_member_create_reservation: "Un membre fait une réservation" notify_admin_member_create_reservation: "Un membre fait une réservation"

View File

@ -492,6 +492,7 @@ en:
phone: "Phone" phone: "Phone"
save: "Save" save: "Save"
to_complete: "To complete" to_complete: "To complete"
refuse_documents: "Refusing the documents"
child_item: child_item:
first_name: "First name of the child" first_name: "First name of the child"
last_name: "Last name of the child" last_name: "Last name of the child"

View File

@ -492,6 +492,7 @@ fr:
phone: "Téléphone" phone: "Téléphone"
save: "Enregistrer" save: "Enregistrer"
to_complete: "À compléter" to_complete: "À compléter"
refuse_documents: "Refuser les documents"
child_item: child_item:
first_name: "Prénom de l'enfant" first_name: "Prénom de l'enfant"
last_name: "Nom de l'enfant" last_name: "Nom de l'enfant"

View File

@ -449,6 +449,10 @@ en:
refusal: "Your supporting documents were refused" refusal: "Your supporting documents were refused"
notify_admin_user_supporting_document_refusal: notify_admin_user_supporting_document_refusal:
refusal: "Member's supporting document <strong><em>%{NAME}</strong></em> was refused." refusal: "Member's supporting document <strong><em>%{NAME}</strong></em> was refused."
notify_user_child_supporting_document_refusal:
refusal: "Your child's supporting documents were refused"
notify_admin_user_child_supporting_document_refusal:
refusal: "Child's supporting document <strong><em>%{NAME}</strong></em> was refused."
notify_user_order_is_ready: notify_user_order_is_ready:
order_ready: "Your command %{REFERENCE} is ready" order_ready: "Your command %{REFERENCE} is ready"
notify_user_order_is_canceled: notify_user_order_is_canceled:

View File

@ -449,6 +449,10 @@ fr:
refusal: "Vos pièces justificatives ont été refusées" refusal: "Vos pièces justificatives ont été refusées"
notify_admin_user_supporting_document_refusal: notify_admin_user_supporting_document_refusal:
refusal: "Le justificatif du membre <strong><em>%{NAME}</strong></em> a été refusé." refusal: "Le justificatif du membre <strong><em>%{NAME}</strong></em> a été refusé."
notify_user_child_supporting_document_refusal:
refusal: "Vos pièces justificatives de l'enfant ont été refusées"
notify_admin_user_child_supporting_document_refusal:
refusal: "Le justificatif de l'enfant <strong><em>%{NAME}</strong></em> a été refusé."
notify_user_order_is_ready: notify_user_order_is_ready:
order_ready: "Votre commande %{REFERENCE} est prête" order_ready: "Votre commande %{REFERENCE} est prête"
notify_user_order_is_canceled: notify_user_order_is_canceled:

View File

@ -406,6 +406,15 @@ en:
subject: "A member's supporting documents were refused" subject: "A member's supporting documents were refused"
body: body:
user_supporting_document_files_refusal: "Member %{NAME}'s supporting documents were rejected by %{OPERATOR}:" user_supporting_document_files_refusal: "Member %{NAME}'s supporting documents were rejected by %{OPERATOR}:"
notify_user_child_supporting_document_refusal:
subject: "Your child's supporting documents were refused"
body:
user_child_supporting_document_files_refusal: "Your supporting documents were refused:"
action: "Please re-upload some new supporting documents."
notify_admin_user_child_supporting_document_refusal:
subject: "A child's supporting documents were refused"
body:
user_child_supporting_document_files_refusal: "Child %{NAME}'s supporting documents were rejected by %{OPERATOR}:"
shared: shared:
hello: "Hello %{user_name}" hello: "Hello %{user_name}"
notify_user_order_is_ready: notify_user_order_is_ready:

View File

@ -406,6 +406,15 @@ fr:
subject: "Les justificatifs d'un membre ont été refusés" subject: "Les justificatifs d'un membre ont été refusés"
body: body:
user_supporting_document_files_refusal: "Le justificatif du membre %{NAME} a été refusé par %{OPERATOR} :" user_supporting_document_files_refusal: "Le justificatif du membre %{NAME} a été refusé par %{OPERATOR} :"
notify_user_child_supporting_document_refusal:
subject: "Vos pièces justificatives de l'enfant ont été refusées"
body:
user_child_supporting_document_files_refusal: "Vos pièces justificatives ont été refusées :"
action: "Veuillez téléverser de nouvelles pièces justificatives."
notify_admin_user_child_supporting_document_refusal:
subject: "Les justificatifs d'un enfant ont été refusés"
body:
user_child_supporting_document_files_refusal: "Le justificatif de l'enfant %{NAME} a été refusé par %{OPERATOR} :"
shared: shared:
hello: "Bonjour %{user_name}" hello: "Bonjour %{user_name}"
notify_user_order_is_ready: notify_user_order_is_ready:

View File

@ -31,3 +31,19 @@ unless NotificationType.find_by(name: 'notify_member_reservation_limit_reached')
is_configurable: false is_configurable: false
) )
end end
unless NotificationType.find_by(name: 'notify_admin_user_child_supporting_document_refusal')
NotificationType.create!(
name: 'notify_admin_user_child_supporting_document_refusal',
category: 'supporting_documents',
is_configurable: true
)
end
unless NotificationType.find_by(name: 'notify_user_child_supporting_document_refusal')
NotificationType.create!(
name: 'notify_user_child_supporting_document_refusal',
category: 'supporting_documents',
is_configurable: false
)
end