mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-26 20:54:21 +01:00
(wip) linting supporting-documents (ex proof-of-identify)
remaining: type-form, type-modal, types-list
This commit is contained in:
parent
21ee80ab19
commit
4d6af2c1c0
@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { FabModal } from '../base/fab-modal';
|
||||
import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type';
|
||||
|
||||
interface DeleteProofOfIdentityTypeModalProps {
|
||||
interface DeleteSupportingDocumentsTypeModalProps {
|
||||
isOpen: boolean,
|
||||
proofOfIdentityTypeId: number,
|
||||
toggleModal: () => void,
|
||||
@ -14,7 +14,7 @@ interface DeleteProofOfIdentityTypeModalProps {
|
||||
/**
|
||||
* Modal dialog to remove a requested type of supporting documents
|
||||
*/
|
||||
export const DeleteProofOfIdentityTypeModal: React.FC<DeleteProofOfIdentityTypeModalProps> = ({ isOpen, toggleModal, onSuccess, proofOfIdentityTypeId, onError }) => {
|
||||
export const DeleteSupportingDocumentsTypeModal: React.FC<DeleteSupportingDocumentsTypeModalProps> = ({ isOpen, toggleModal, onSuccess, proofOfIdentityTypeId, onError }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
/**
|
||||
@ -23,21 +23,21 @@ export const DeleteProofOfIdentityTypeModal: React.FC<DeleteProofOfIdentityTypeM
|
||||
const handleDeleteProofOfIdentityType = async (): Promise<void> => {
|
||||
try {
|
||||
await ProofOfIdentityTypeAPI.destroy(proofOfIdentityTypeId);
|
||||
onSuccess(t('app.admin.settings.account.delete_proof_of_identity_type_modal.deleted'));
|
||||
onSuccess(t('app.admin.settings.account.delete_supporting_documents_type_modal.deleted'));
|
||||
} catch (e) {
|
||||
onError(t('app.admin.settings.account.delete_proof_of_identity_type_modal.unable_to_delete') + e);
|
||||
onError(t('app.admin.settings.account.delete_supporting_documents_type_modal.unable_to_delete') + e);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<FabModal title={t('app.admin.settings.account.delete_proof_of_identity_type_modal.confirmation_required')}
|
||||
<FabModal title={t('app.admin.settings.account.delete_supporting_documents_type_modal.confirmation_required')}
|
||||
isOpen={isOpen}
|
||||
toggleModal={toggleModal}
|
||||
closeButton={true}
|
||||
confirmButton={t('app.admin.settings.account.delete_proof_of_identity_type_modal.confirm')}
|
||||
confirmButton={t('app.admin.settings.account.delete_supporting_documents_type_modal.confirm')}
|
||||
onConfirm={handleDeleteProofOfIdentityType}
|
||||
className="delete-proof-of-identity-type-modal">
|
||||
<p>{t('app.admin.settings.account.delete_proof_of_identity_type_modal.confirm_delete_proof_of_identity')}</p>
|
||||
className="delete-supporting-documents-type-modal">
|
||||
<p>{t('app.admin.settings.account.delete_supporting_documents_type_modal.confirm_delete_supporting_documents_type')}</p>
|
||||
</FabModal>
|
||||
);
|
||||
};
|
@ -1,173 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { react2angular } from 'react2angular';
|
||||
import _ from 'lodash';
|
||||
import { HtmlTranslate } from '../base/html-translate';
|
||||
import { Loader } from '../base/loader';
|
||||
import { User } from '../../models/user';
|
||||
import { IApplication } from '../../models/application';
|
||||
import { ProofOfIdentityType } from '../../models/proof-of-identity-type';
|
||||
import { ProofOfIdentityFile } from '../../models/proof-of-identity-file';
|
||||
import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type';
|
||||
import ProofOfIdentityFileAPI from '../../api/proof-of-identity-file';
|
||||
import { IFablab } from '../../models/fablab';
|
||||
|
||||
declare let Fablab: IFablab;
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
interface ProofOfIdentityFilesProps {
|
||||
currentUser: User,
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
}
|
||||
|
||||
interface FilesType {
|
||||
number?: File
|
||||
}
|
||||
|
||||
/**
|
||||
* This component upload the supporting documents file of the member
|
||||
*/
|
||||
export const ProofOfIdentityFiles: React.FC<ProofOfIdentityFilesProps> = ({ currentUser, onSuccess, onError }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const maxProofOfIdentityFileSizeMb = (Fablab.maxProofOfIdentityFileSize / 1024 / 1024).toFixed();
|
||||
|
||||
// list of supporting documents type
|
||||
const [proofOfIdentityTypes, setProofOfIdentityTypes] = useState<Array<ProofOfIdentityType>>([]);
|
||||
const [proofOfIdentityFiles, setProofOfIdentityFiles] = useState<Array<ProofOfIdentityFile>>([]);
|
||||
const [files, setFiles] = useState<FilesType>({});
|
||||
const [errors, setErrors] = useState<Array<number>>([]);
|
||||
|
||||
// get supporting documents type and files
|
||||
useEffect(() => {
|
||||
ProofOfIdentityTypeAPI.index({ group_id: currentUser.group_id }).then(tData => {
|
||||
setProofOfIdentityTypes(tData);
|
||||
});
|
||||
ProofOfIdentityFileAPI.index({ user_id: currentUser.id }).then(fData => {
|
||||
setProofOfIdentityFiles(fData);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getProofOfIdentityFileByType = (proofOfIdentityTypeId: number): ProofOfIdentityFile => {
|
||||
return _.find<ProofOfIdentityFile>(proofOfIdentityFiles, { proof_of_identity_type_id: proofOfIdentityTypeId });
|
||||
};
|
||||
|
||||
const hasFile = (proofOfIdentityTypeId: number): boolean => {
|
||||
return files[proofOfIdentityTypeId] || getProofOfIdentityFileByType(proofOfIdentityTypeId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the current collection of supporting documents types is empty or not.
|
||||
*/
|
||||
const hasProofOfIdentityTypes = (): boolean => {
|
||||
return proofOfIdentityTypes.length > 0;
|
||||
};
|
||||
|
||||
const onFileChange = (poitId: number) => {
|
||||
return (event) => {
|
||||
const fileSize = event.target.files[0].size;
|
||||
let _errors = errors;
|
||||
if (fileSize > Fablab.maxProofOfIdentityFileSize) {
|
||||
_errors = errors.concat(poitId);
|
||||
setErrors(_errors);
|
||||
} else {
|
||||
_errors = errors.filter(e => e !== poitId);
|
||||
}
|
||||
setErrors(_errors);
|
||||
setFiles({
|
||||
...files,
|
||||
[poitId]: event.target.files[0]
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onFileUpload = async () => {
|
||||
try {
|
||||
for (const proofOfIdentityTypeId of Object.keys(files)) {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('proof_of_identity_file[user_id]', currentUser.id.toString());
|
||||
formData.append('proof_of_identity_file[proof_of_identity_type_id]', proofOfIdentityTypeId);
|
||||
formData.append('proof_of_identity_file[attachment]', files[proofOfIdentityTypeId]);
|
||||
const proofOfIdentityFile = getProofOfIdentityFileByType(parseInt(proofOfIdentityTypeId, 10));
|
||||
if (proofOfIdentityFile) {
|
||||
await ProofOfIdentityFileAPI.update(proofOfIdentityFile.id, formData);
|
||||
} else {
|
||||
await ProofOfIdentityFileAPI.create(formData);
|
||||
}
|
||||
}
|
||||
if (Object.keys(files).length > 0) {
|
||||
ProofOfIdentityFileAPI.index({ user_id: currentUser.id }).then(fData => {
|
||||
setProofOfIdentityFiles(fData);
|
||||
setFiles({});
|
||||
onSuccess(t('app.admin.members_edit.proof_of_identity_files_successfully_uploaded'));
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
onError(t('app.admin.members_edit.proof_of_identity_files_unable_to_upload') + e);
|
||||
}
|
||||
};
|
||||
|
||||
const getProofOfIdentityFileUrl = (poifId: number) => {
|
||||
return `/api/proof_of_identity_files/${poifId}/download`;
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="panel panel-default bg-light m-lg col-sm-12 col-md-12 col-lg-9">
|
||||
<h3>{t('app.admin.members_edit.proof_of_identity_files')}</h3>
|
||||
<p className="text-black font-sbold">{t('app.admin.members_edit.my_documents_info')}</p>
|
||||
<div className="alert alert-warning">
|
||||
<HtmlTranslate trKey="app.admin.members_edit.my_documents_alert" options={{ SIZE: maxProofOfIdentityFileSizeMb }}/>
|
||||
</div>
|
||||
<div className="widget-content no-bg auto">
|
||||
{proofOfIdentityTypes.map((poit: ProofOfIdentityType) => {
|
||||
return (
|
||||
<div className={`form-group ${errors.includes(poit.id) ? 'has-error' : ''}`} key={poit.id}>
|
||||
<label className="control-label m-r">{poit.name}</label>
|
||||
<div className="fileinput input-group">
|
||||
<div className="form-control">
|
||||
{hasFile(poit.id) && (
|
||||
<div>
|
||||
<i className="glyphicon glyphicon-file fileinput-exists"></i> <span className="fileinput-filename">{files[poit.id]?.name || getProofOfIdentityFileByType(poit.id).attachment}</span>
|
||||
</div>
|
||||
)}
|
||||
{getProofOfIdentityFileByType(poit.id) && !files[poit.id] && (
|
||||
<a href={getProofOfIdentityFileUrl(getProofOfIdentityFileByType(poit.id).id)} target="_blank" style={{ position: 'absolute', right: '10px' }} rel="noreferrer"><i className="fa fa-download text-black "></i></a>
|
||||
)}
|
||||
</div>
|
||||
<span className="input-group-addon btn btn-default btn-file">
|
||||
{!hasFile(poit.id) && (
|
||||
<span className="fileinput-new">Parcourir</span>
|
||||
)}
|
||||
{hasFile(poit.id) && (
|
||||
<span className="fileinput-exists">Modifier</span>
|
||||
)}
|
||||
<input type="file"
|
||||
accept="application/pdf,image/jpeg,image/jpg,image/png"
|
||||
onChange={onFileChange(poit.id)}
|
||||
required />
|
||||
</span>
|
||||
</div>
|
||||
{errors.includes(poit.id) && <span className="help-block">{t('app.admin.members_edit.proof_of_identity_file_size_error', { SIZE: maxProofOfIdentityFileSizeMb })}</span>}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{hasProofOfIdentityTypes() && (
|
||||
<button type="button" className="btn btn-warning m-b m-t pull-right" onClick={onFileUpload} disabled={errors.length > 0}>{t('app.admin.members_edit.save')}</button>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const ProofOfIdentityFilesWrapper: React.FC<ProofOfIdentityFilesProps> = ({ currentUser, onSuccess, onError }) => {
|
||||
return (
|
||||
<Loader>
|
||||
<ProofOfIdentityFiles currentUser={currentUser} onSuccess={onSuccess} onError={onError} />
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('proofOfIdentityFiles', react2angular(ProofOfIdentityFilesWrapper, ['currentUser', 'onSuccess', 'onError']));
|
@ -1,63 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FabModal } from '../base/fab-modal';
|
||||
import { ProofOfIdentityType } from '../../models/proof-of-identity-type';
|
||||
import { ProofOfIdentityRefusal } from '../../models/proof-of-identity-refusal';
|
||||
import { User } from '../../models/user';
|
||||
import ProofOfIdentityRefusalAPI from '../../api/proof-of-identity-refusal';
|
||||
import { ProofOfIdentityRefusalForm } from './proof-of-identity-refusal-form';
|
||||
|
||||
interface ProofOfIdentityRefusalModalProps {
|
||||
isOpen: boolean,
|
||||
toggleModal: () => void,
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
proofOfIdentityTypes: Array<ProofOfIdentityType>,
|
||||
operator: User,
|
||||
member: User
|
||||
}
|
||||
|
||||
export const ProofOfIdentityRefusalModal: React.FC<ProofOfIdentityRefusalModalProps> = ({ isOpen, toggleModal, onSuccess, proofOfIdentityTypes, operator, member, onError }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const [data, setData] = useState<ProofOfIdentityRefusal>({
|
||||
id: null,
|
||||
operator_id: operator.id,
|
||||
user_id: member.id,
|
||||
proof_of_identity_type_ids: [],
|
||||
message: ''
|
||||
});
|
||||
|
||||
const handleProofOfIdentityRefusalChanged = (field: string, value: string | Array<number>) => {
|
||||
setData({
|
||||
...data,
|
||||
[field]: value
|
||||
});
|
||||
};
|
||||
|
||||
const handleSaveProofOfIdentityRefusal = async (): Promise<void> => {
|
||||
try {
|
||||
await ProofOfIdentityRefusalAPI.create(data);
|
||||
onSuccess(t('app.admin.members_edit.proof_of_identity_refusal_successfully_sent'));
|
||||
} catch (e) {
|
||||
onError(t('app.admin.members_edit.proof_of_identity_refusal_unable_to_send') + e);
|
||||
}
|
||||
};
|
||||
|
||||
const isPreventSaveProofOfIdentityRefusal = (): boolean => {
|
||||
return !data.message || data.proof_of_identity_type_ids.length === 0;
|
||||
};
|
||||
|
||||
return (
|
||||
<FabModal title={t('app.admin.members_edit.proof_of_identity_refusal')}
|
||||
isOpen={isOpen}
|
||||
toggleModal={toggleModal}
|
||||
closeButton={false}
|
||||
confirmButton={t('app.admin.members_edit.confirm')}
|
||||
onConfirm={handleSaveProofOfIdentityRefusal}
|
||||
preventConfirm={isPreventSaveProofOfIdentityRefusal()}
|
||||
className="proof-of-identity-type-modal">
|
||||
<ProofOfIdentityRefusalForm proofOfIdentityTypes={proofOfIdentityTypes} onChange={handleProofOfIdentityRefusalChanged}/>
|
||||
</FabModal>
|
||||
);
|
||||
};
|
@ -8,7 +8,7 @@ import { IApplication } from '../../models/application';
|
||||
import { ProofOfIdentityType } from '../../models/proof-of-identity-type';
|
||||
import { Group } from '../../models/group';
|
||||
import { ProofOfIdentityTypeModal } from './proof-of-identity-type-modal';
|
||||
import { DeleteProofOfIdentityTypeModal } from './delete-proof-of-identity-type-modal';
|
||||
import { DeleteSupportingDocumentsTypeModal } from './delete-supporting-documents-type-modal';
|
||||
import GroupAPI from '../../api/group';
|
||||
import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type';
|
||||
|
||||
@ -162,7 +162,7 @@ const ProofOfIdentityTypesList: React.FC<ProofOfIdentityTypesListProps> = ({ onS
|
||||
</div>
|
||||
|
||||
<ProofOfIdentityTypeModal isOpen={modalIsOpen} groups={groups} proofOfIdentityType={proofOfIdentityType} toggleModal={toggleCreateAndEditModal} onSuccess={saveProofOfIdentityTypeOnSuccess} onError={onError} />
|
||||
<DeleteProofOfIdentityTypeModal isOpen={destroyModalIsOpen} proofOfIdentityTypeId={proofOfIdentityTypeId} toggleModal={toggleDestroyModal} onSuccess={destroyProofOfIdentityTypeOnSuccess} onError={onError}/>
|
||||
<DeleteSupportingDocumentsTypeModal isOpen={destroyModalIsOpen} proofOfIdentityTypeId={proofOfIdentityTypeId} toggleModal={toggleDestroyModal} onSuccess={destroyProofOfIdentityTypeOnSuccess} onError={onError}/>
|
||||
|
||||
<table className="table proof-of-identity-type-list">
|
||||
<thead>
|
||||
|
@ -1,121 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { react2angular } from 'react2angular';
|
||||
import _ from 'lodash';
|
||||
import { Loader } from '../base/loader';
|
||||
import { User } from '../../models/user';
|
||||
import { IApplication } from '../../models/application';
|
||||
import { ProofOfIdentityType } from '../../models/proof-of-identity-type';
|
||||
import { ProofOfIdentityFile } from '../../models/proof-of-identity-file';
|
||||
import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type';
|
||||
import ProofOfIdentityFileAPI from '../../api/proof-of-identity-file';
|
||||
import { ProofOfIdentityRefusalModal } from './proof-of-identity-refusal-modal';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
interface ProofOfIdentityValidationProps {
|
||||
operator: User,
|
||||
member: User
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
}
|
||||
|
||||
/**
|
||||
* This component shows a list of supporting documents file of member, admin can download and valid
|
||||
**/
|
||||
const ProofOfIdentityValidation: React.FC<ProofOfIdentityValidationProps> = ({ operator, member, onSuccess, onError }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
// list of supporting documents type
|
||||
const [proofOfIdentityTypes, setProofOfIdentityTypes] = useState<Array<ProofOfIdentityType>>([]);
|
||||
const [proofOfIdentityFiles, setProofOfIdentityFiles] = useState<Array<ProofOfIdentityFile>>([]);
|
||||
const [modalIsOpen, setModalIsOpen] = useState<boolean>(false);
|
||||
|
||||
// get groups
|
||||
useEffect(() => {
|
||||
ProofOfIdentityTypeAPI.index({ group_id: member.group_id }).then(tData => {
|
||||
setProofOfIdentityTypes(tData);
|
||||
});
|
||||
ProofOfIdentityFileAPI.index({ user_id: member.id }).then(fData => {
|
||||
setProofOfIdentityFiles(fData);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getProofOfIdentityFileByType = (proofOfIdentityTypeId: number): ProofOfIdentityFile => {
|
||||
return _.find<ProofOfIdentityFile>(proofOfIdentityFiles, { proof_of_identity_type_id: proofOfIdentityTypeId });
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the current collection of supporting documents types is empty or not.
|
||||
*/
|
||||
const hasProofOfIdentityTypes = (): boolean => {
|
||||
return proofOfIdentityTypes.length > 0;
|
||||
};
|
||||
|
||||
const getProofOfIdentityFileUrl = (poifId: number): string => {
|
||||
return `/api/proof_of_identity_files/${poifId}/download`;
|
||||
};
|
||||
|
||||
const openProofOfIdentityRefusalModal = (): void => {
|
||||
setModalIsOpen(true);
|
||||
};
|
||||
|
||||
const toggleModal = (): void => {
|
||||
setModalIsOpen(false);
|
||||
};
|
||||
|
||||
const saveProofOfIdentityRefusalOnSuccess = (message: string): void => {
|
||||
setModalIsOpen(false);
|
||||
onSuccess(message);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<section className="panel panel-default bg-light m-lg col-sm-12 col-md-12 col-lg-7">
|
||||
<h3>{t('app.admin.members_edit.proof_of_identity_files')}</h3>
|
||||
<p className="text-black font-sbold">{t('app.admin.members_edit.find_below_the_proof_of_identity_files')}</p>
|
||||
{proofOfIdentityTypes.map((poit: ProofOfIdentityType) => {
|
||||
return (
|
||||
<div key={poit.id} className="m-b">
|
||||
<div className="m-b-xs">{poit.name}</div>
|
||||
{getProofOfIdentityFileByType(poit.id) && (
|
||||
<a href={getProofOfIdentityFileUrl(getProofOfIdentityFileByType(poit.id).id)} target="_blank" rel="noreferrer">
|
||||
<span className="m-r">{getProofOfIdentityFileByType(poit.id).attachment}</span>
|
||||
<i className="fa fa-download"></i>
|
||||
</a>
|
||||
)}
|
||||
{!getProofOfIdentityFileByType(poit.id) && (
|
||||
<div className="text-danger">{t('app.admin.members_edit.to_complete')}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
{hasProofOfIdentityTypes() && !member.validated_at && (
|
||||
<section className="panel panel-default bg-light m-t-lg col-sm-12 col-md-12 col-lg-4">
|
||||
<h3>{t('app.admin.members_edit.refuse_proof_of_identity_files')}</h3>
|
||||
<p className="text-black">{t('app.admin.members_edit.refuse_proof_of_identity_files_info')}</p>
|
||||
<button type="button" className="btn btn-warning m-b m-t" onClick={openProofOfIdentityRefusalModal}>{t('app.admin.members_edit.proof_of_identity_refusal')}</button>
|
||||
<ProofOfIdentityRefusalModal
|
||||
isOpen={modalIsOpen}
|
||||
proofOfIdentityTypes={proofOfIdentityTypes}
|
||||
toggleModal={toggleModal}
|
||||
operator={operator}
|
||||
member={member}
|
||||
onError={onError}
|
||||
onSuccess={saveProofOfIdentityRefusalOnSuccess}/>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ProofOfIdentityValidationWrapper: React.FC<ProofOfIdentityValidationProps> = ({ operator, member, onSuccess, onError }) => {
|
||||
return (
|
||||
<Loader>
|
||||
<ProofOfIdentityValidation operator={operator} member={member} onSuccess={onSuccess} onError={onError} />
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('proofOfIdentityValidation', react2angular(ProofOfIdentityValidationWrapper, ['operator', 'member', 'onSuccess', 'onError']));
|
@ -0,0 +1,204 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { react2angular } from 'react2angular';
|
||||
import _ from 'lodash';
|
||||
import { HtmlTranslate } from '../base/html-translate';
|
||||
import { Loader } from '../base/loader';
|
||||
import { User } from '../../models/user';
|
||||
import { IApplication } from '../../models/application';
|
||||
import { ProofOfIdentityType } from '../../models/proof-of-identity-type';
|
||||
import { ProofOfIdentityFile } from '../../models/proof-of-identity-file';
|
||||
import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type';
|
||||
import ProofOfIdentityFileAPI from '../../api/proof-of-identity-file';
|
||||
import { IFablab } from '../../models/fablab';
|
||||
import { FabAlert } from '../base/fab-alert';
|
||||
|
||||
declare let Fablab: IFablab;
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
interface SupportingDocumentsFilesProps {
|
||||
currentUser: User,
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
}
|
||||
|
||||
interface FilesType {
|
||||
number?: File
|
||||
}
|
||||
|
||||
/**
|
||||
* This component upload the supporting documents file of the member
|
||||
*/
|
||||
export const SupportingDocumentsFiles: React.FC<SupportingDocumentsFilesProps> = ({ currentUser, onSuccess, onError }) => {
|
||||
const { t } = useTranslation('logged');
|
||||
|
||||
const maxProofOfIdentityFileSizeMb = (Fablab.maxProofOfIdentityFileSize / 1024 / 1024).toFixed();
|
||||
|
||||
// list of supporting documents type
|
||||
const [supportingDocumentsTypes, setSupportingDocumentsTypes] = useState<Array<ProofOfIdentityType>>([]);
|
||||
const [supportingDocumentsFiles, setSupportingDocumentsFiles] = useState<Array<ProofOfIdentityFile>>([]);
|
||||
const [files, setFiles] = useState<FilesType>({});
|
||||
const [errors, setErrors] = useState<Array<number>>([]);
|
||||
|
||||
// get supporting documents type and files
|
||||
useEffect(() => {
|
||||
ProofOfIdentityTypeAPI.index({ group_id: currentUser.group_id }).then(tData => {
|
||||
setSupportingDocumentsTypes(tData);
|
||||
});
|
||||
ProofOfIdentityFileAPI.index({ user_id: currentUser.id }).then(fData => {
|
||||
setSupportingDocumentsFiles(fData);
|
||||
});
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Return the files matching the given type id
|
||||
*/
|
||||
const getSupportingDocumentsFileByType = (supportingDocumentsTypeId: number): ProofOfIdentityFile => {
|
||||
return _.find<ProofOfIdentityFile>(supportingDocumentsFiles, {
|
||||
proof_of_identity_type_id: supportingDocumentsTypeId
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given type has any uploaded files
|
||||
*/
|
||||
const hasFile = (proofOfIdentityTypeId: number): boolean => {
|
||||
return files[proofOfIdentityTypeId] || getSupportingDocumentsFileByType(proofOfIdentityTypeId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the current collection of supporting documents types is empty or not.
|
||||
*/
|
||||
const hasProofOfIdentityTypes = (): boolean => {
|
||||
return supportingDocumentsTypes.length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when a file is selected by the member: check if the file does not exceed the maximum allowed size
|
||||
*/
|
||||
const onFileChange = (documentId: number) => {
|
||||
return (event) => {
|
||||
const fileSize = event.target.files[0].size;
|
||||
let _errors: Array<number>;
|
||||
if (fileSize > Fablab.maxProofOfIdentityFileSize) {
|
||||
_errors = errors.concat(documentId);
|
||||
setErrors(_errors);
|
||||
} else {
|
||||
_errors = errors.filter(e => e !== documentId);
|
||||
}
|
||||
setErrors(_errors);
|
||||
setFiles({
|
||||
...files,
|
||||
[documentId]: event.target.files[0]
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the user clicks on save: upload the file to the API
|
||||
*/
|
||||
const onFileUpload = async () => {
|
||||
try {
|
||||
for (const proofOfIdentityTypeId of Object.keys(files)) {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('proof_of_identity_file[user_id]', currentUser.id.toString());
|
||||
formData.append('proof_of_identity_file[proof_of_identity_type_id]', proofOfIdentityTypeId);
|
||||
formData.append('proof_of_identity_file[attachment]', files[proofOfIdentityTypeId]);
|
||||
const proofOfIdentityFile = getSupportingDocumentsFileByType(parseInt(proofOfIdentityTypeId, 10));
|
||||
if (proofOfIdentityFile) {
|
||||
await ProofOfIdentityFileAPI.update(proofOfIdentityFile.id, formData);
|
||||
} else {
|
||||
await ProofOfIdentityFileAPI.create(formData);
|
||||
}
|
||||
}
|
||||
if (Object.keys(files).length > 0) {
|
||||
ProofOfIdentityFileAPI.index({ user_id: currentUser.id }).then(fData => {
|
||||
setSupportingDocumentsFiles(fData);
|
||||
setFiles({});
|
||||
onSuccess(t('app.logged.dashboard.supporting_documents_files.file_successfully_uploaded'));
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
onError(t('app.logged.dashboard.supporting_documents_files.unable_to_upload') + e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the download URL of the given file
|
||||
*/
|
||||
const getSupportingDocumentsFileUrl = (documentId: number) => {
|
||||
return `/api/proof_of_identity_files/${documentId}/download`;
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="supporting-documents-files">
|
||||
<h3>{t('app.logged.dashboard.supporting_documents_files.supporting_documents_files')}</h3>
|
||||
<p className="info-area">{t('app.logged.dashboard.supporting_documents_files.my_documents_info')}</p>
|
||||
<FabAlert level="warning">
|
||||
<HtmlTranslate trKey="app.logged.dashboard.supporting_documents_files.upload_limits_alert_html"
|
||||
options={{ SIZE: maxProofOfIdentityFileSizeMb }}/>
|
||||
</FabAlert>
|
||||
<div className="files-list">
|
||||
{supportingDocumentsTypes.map((documentType: ProofOfIdentityType) => {
|
||||
return (
|
||||
<div className={`file-item ${errors.includes(documentType.id) ? 'has-error' : ''}`} key={documentType.id}>
|
||||
<label>{documentType.name}</label>
|
||||
<div className="fileinput">
|
||||
<div className="filename-container">
|
||||
{hasFile(documentType.id) && (
|
||||
<div>
|
||||
<i className="fa fa-file fileinput-exists" />
|
||||
<span className="fileinput-filename">
|
||||
{files[documentType.id]?.name || getSupportingDocumentsFileByType(documentType.id).attachment}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{getSupportingDocumentsFileByType(documentType.id) && !files[documentType.id] && (
|
||||
<a href={getSupportingDocumentsFileUrl(getSupportingDocumentsFileByType(documentType.id).id)}
|
||||
target="_blank"
|
||||
className="file-download"
|
||||
rel="noreferrer">
|
||||
<i className="fa fa-download"/>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<span className="fileinput-button">
|
||||
{!hasFile(documentType.id) && (
|
||||
<span className="fileinput-new">{t('app.logged.dashboard.supporting_documents_files.browse')}</span>
|
||||
)}
|
||||
{hasFile(documentType.id) && (
|
||||
<span className="fileinput-exists">{t('app.logged.dashboard.supporting_documents_files.edit')}</span>
|
||||
)}
|
||||
<input type="file"
|
||||
accept="application/pdf,image/jpeg,image/jpg,image/png"
|
||||
onChange={onFileChange(documentType.id)}
|
||||
required />
|
||||
</span>
|
||||
</div>
|
||||
{errors.includes(documentType.id) && <span className="errors-area">
|
||||
{t('app.logged.dashboard.supporting_documents_files.file_size_error', { SIZE: maxProofOfIdentityFileSizeMb })}
|
||||
</span>}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{hasProofOfIdentityTypes() && (
|
||||
<button type="button" className="save-btn" onClick={onFileUpload} disabled={errors.length > 0}>
|
||||
{t('app.logged.dashboard.supporting_documents_files.save')}
|
||||
</button>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const SupportingDocumentsFilesWrapper: React.FC<SupportingDocumentsFilesProps> = ({ currentUser, onSuccess, onError }) => {
|
||||
return (
|
||||
<Loader>
|
||||
<SupportingDocumentsFiles currentUser={currentUser} onSuccess={onSuccess} onError={onError} />
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('supportingDocumentsFiles', react2angular(SupportingDocumentsFilesWrapper, ['currentUser', 'onSuccess', 'onError']));
|
@ -2,22 +2,22 @@ import React, { BaseSyntheticEvent, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ProofOfIdentityType } from '../../models/proof-of-identity-type';
|
||||
|
||||
interface ProofOfIdentityRefusalFormProps {
|
||||
interface SupportingDocumentsRefusalFormProps {
|
||||
proofOfIdentityTypes: Array<ProofOfIdentityType>,
|
||||
onChange: (field: string, value: string | Array<number>) => void,
|
||||
}
|
||||
|
||||
/**
|
||||
* Form to set the stripe's public and private keys
|
||||
* Form to set the refuse the uploaded supporting documents
|
||||
*/
|
||||
export const ProofOfIdentityRefusalForm: React.FC<ProofOfIdentityRefusalFormProps> = ({ proofOfIdentityTypes, onChange }) => {
|
||||
export const SupportingDocumentsRefusalForm: React.FC<SupportingDocumentsRefusalFormProps> = ({ proofOfIdentityTypes, onChange }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const [values, setValues] = useState<Array<number>>([]);
|
||||
const [message, setMessage] = useState<string>('');
|
||||
|
||||
/**
|
||||
* Callback triggered when the name has changed.
|
||||
* Callback triggered when the message has changed.
|
||||
*/
|
||||
const handleMessageChange = (e: BaseSyntheticEvent): void => {
|
||||
const { value } = e.target;
|
||||
@ -26,10 +26,9 @@ export const ProofOfIdentityRefusalForm: React.FC<ProofOfIdentityRefusalFormProp
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when a checkbox is ticked or unticked.
|
||||
* This function construct the resulting string, by adding or deleting the provided option identifier.
|
||||
* Callback triggered when the document type checkbox is ticked or unticked.
|
||||
*/
|
||||
const handleProofOfIdnentityTypesChange = (value: number) => {
|
||||
const handleTypeSelectionChange = (value: number) => {
|
||||
return (event: BaseSyntheticEvent) => {
|
||||
let newValues: Array<number>;
|
||||
if (event.target.checked) {
|
||||
@ -43,27 +42,32 @@ export const ProofOfIdentityRefusalForm: React.FC<ProofOfIdentityRefusalFormProp
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify if the provided option is currently ticked (i.e. included in the value string)
|
||||
* Verify if the provided type is currently ticked (i.e. about to be refused)
|
||||
*/
|
||||
const isChecked = (value: number) => {
|
||||
return values.includes(value);
|
||||
const isChecked = (typeId: number) => {
|
||||
return values.includes(typeId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="proof-of-identity-type-form">
|
||||
<div className="supporting-documents-refusal-form">
|
||||
<form name="proofOfIdentityRefusalForm">
|
||||
<div>
|
||||
{proofOfIdentityTypes.map(type => <div key={type.id} className="">
|
||||
{proofOfIdentityTypes.map(type => <div key={type.id}>
|
||||
<label htmlFor={`checkbox-${type.id}`}>{type.name}</label>
|
||||
<input id={`checkbox-${type.id}`} className="pull-right" type="checkbox" checked={isChecked(type.id)} onChange={handleProofOfIdnentityTypesChange(type.id)} />
|
||||
<input id={`checkbox-${type.id}`}
|
||||
type="checkbox"
|
||||
checked={isChecked(type.id)}
|
||||
onChange={handleTypeSelectionChange(type.id)} />
|
||||
</div>)}
|
||||
</div>
|
||||
<div className="proof-of-identity-refusal-comment-textarea m-t">
|
||||
<label htmlFor="proof-of-identity-refusal-comment">{t('app.admin.members_edit.proof_of_identity_refusal_comment')}</label>
|
||||
<div className="refusal-comment">
|
||||
<label htmlFor="proof-of-identity-refusal-comment">
|
||||
{t('app.admin.supporting_documents_refusal_form.refusal_comment')}
|
||||
</label>
|
||||
<textarea
|
||||
id="proof-of-identity-refusal-comment"
|
||||
value={message}
|
||||
placeholder={t('app.admin.members_edit.proof_of_identity_refuse_input_message')}
|
||||
placeholder={t('app.admin.supporting_documents_refusal_form.comment_placeholder')}
|
||||
onChange={handleMessageChange}
|
||||
style={{ width: '100%' }}
|
||||
rows={5}
|
@ -0,0 +1,75 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FabModal } from '../base/fab-modal';
|
||||
import { ProofOfIdentityType } from '../../models/proof-of-identity-type';
|
||||
import { ProofOfIdentityRefusal } from '../../models/proof-of-identity-refusal';
|
||||
import { User } from '../../models/user';
|
||||
import ProofOfIdentityRefusalAPI from '../../api/proof-of-identity-refusal';
|
||||
import { SupportingDocumentsRefusalForm } from './supporting-documents-refusal-form';
|
||||
|
||||
interface SupportingDocumentsRefusalModalProps {
|
||||
isOpen: boolean,
|
||||
toggleModal: () => void,
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
proofOfIdentityTypes: Array<ProofOfIdentityType>,
|
||||
operator: User,
|
||||
member: User
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal dialog to notify the member that his documents are refused
|
||||
*/
|
||||
export const SupportingDocumentsRefusalModal: React.FC<SupportingDocumentsRefusalModalProps> = ({ isOpen, toggleModal, onSuccess, proofOfIdentityTypes, operator, member, onError }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const [data, setData] = useState<ProofOfIdentityRefusal>({
|
||||
id: null,
|
||||
operator_id: operator.id,
|
||||
user_id: member.id,
|
||||
proof_of_identity_type_ids: [],
|
||||
message: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* Callback triggered when any field has changed in the child form
|
||||
*/
|
||||
const handleRefusalChanged = (field: string, value: string | Array<number>) => {
|
||||
setData({
|
||||
...data,
|
||||
[field]: value
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Save the refusal to the API and send a result message to the parent component
|
||||
*/
|
||||
const handleSaveRefusal = async (): Promise<void> => {
|
||||
try {
|
||||
await ProofOfIdentityRefusalAPI.create(data);
|
||||
onSuccess(t('app.admin.supporting_documents_refusal_modal.refusal_successfully_sent'));
|
||||
} catch (e) {
|
||||
onError(t('app.admin.supporting_documents_refusal_modal.unable_to_send') + e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the refusal can be saved (i.e. is not empty)
|
||||
*/
|
||||
const isPreventedSaveRefusal = (): boolean => {
|
||||
return !data.message || data.proof_of_identity_type_ids.length === 0;
|
||||
};
|
||||
|
||||
return (
|
||||
<FabModal title={t('app.admin.supporting_documents_refusal_modal.title')}
|
||||
isOpen={isOpen}
|
||||
toggleModal={toggleModal}
|
||||
closeButton={false}
|
||||
confirmButton={t('app.admin.supporting_documents_refusal_modal.confirm')}
|
||||
onConfirm={handleSaveRefusal}
|
||||
preventConfirm={isPreventedSaveRefusal()}
|
||||
className="supporting-documents-refusal-modal">
|
||||
<SupportingDocumentsRefusalForm proofOfIdentityTypes={proofOfIdentityTypes} onChange={handleRefusalChanged}/>
|
||||
</FabModal>
|
||||
);
|
||||
};
|
@ -0,0 +1,132 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { react2angular } from 'react2angular';
|
||||
import _ from 'lodash';
|
||||
import { Loader } from '../base/loader';
|
||||
import { User } from '../../models/user';
|
||||
import { IApplication } from '../../models/application';
|
||||
import { ProofOfIdentityType } from '../../models/proof-of-identity-type';
|
||||
import { ProofOfIdentityFile } from '../../models/proof-of-identity-file';
|
||||
import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type';
|
||||
import ProofOfIdentityFileAPI from '../../api/proof-of-identity-file';
|
||||
import { SupportingDocumentsRefusalModal } from './supporting-documents-refusal-modal';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
interface SupportingDocumentsValidationProps {
|
||||
operator: User,
|
||||
member: User
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 { t } = useTranslation('admin');
|
||||
|
||||
// list of supporting documents type
|
||||
const [documentsTypes, setDocumentsTypes] = useState<Array<ProofOfIdentityType>>([]);
|
||||
const [documentsFiles, setDocumentsFiles] = useState<Array<ProofOfIdentityFile>>([]);
|
||||
const [modalIsOpen, setModalIsOpen] = useState<boolean>(false);
|
||||
|
||||
// get groups
|
||||
useEffect(() => {
|
||||
ProofOfIdentityTypeAPI.index({ group_id: member.group_id }).then(tData => {
|
||||
setDocumentsTypes(tData);
|
||||
});
|
||||
ProofOfIdentityFileAPI.index({ user_id: member.id }).then(fData => {
|
||||
setDocumentsFiles(fData);
|
||||
});
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Return the file associated with the provided type
|
||||
*/
|
||||
const getFileByType = (typeId: number): ProofOfIdentityFile => {
|
||||
return _.find<ProofOfIdentityFile>(documentsFiles, { proof_of_identity_type_id: typeId });
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if any supporting documents types has been defined.
|
||||
*/
|
||||
const hasSupportingDocumentsTypes = (): boolean => {
|
||||
return documentsTypes.length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the download URL of the given file
|
||||
*/
|
||||
const getProofOfIdentityFileUrl = (documentId: number): string => {
|
||||
return `/api/proof_of_identity_files/${documentId}/download`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open/closes the modal dialog to refuse the documents
|
||||
*/
|
||||
const toggleModal = (): void => {
|
||||
setModalIsOpen(!modalIsOpen);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the refusal was successfully saved
|
||||
*/
|
||||
const onSaveRefusalSuccess = (message: string): void => {
|
||||
setModalIsOpen(false);
|
||||
onSuccess(message);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="supporting-documents-validation">
|
||||
<section>
|
||||
<h3>{t('app.admin.supporting_documents_validation.title')}</h3>
|
||||
<p className="info-area">{t('app.admin.supporting_documents_validation.find_below_documents_files')}</p>
|
||||
{documentsTypes.map((documentType: ProofOfIdentityType) => {
|
||||
return (
|
||||
<div key={documentType.id} className="document-type">
|
||||
<div className="type-name">{documentType.name}</div>
|
||||
{getFileByType(documentType.id) && (
|
||||
<a href={getProofOfIdentityFileUrl(getFileByType(documentType.id).id)} target="_blank" rel="noreferrer">
|
||||
<span className="filename">{getFileByType(documentType.id).attachment}</span>
|
||||
<i className="fa fa-download"></i>
|
||||
</a>
|
||||
)}
|
||||
{!getFileByType(documentType.id) && (
|
||||
<div className="missing-file">{t('app.admin.supporting_documents_validation.to_complete')}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
{hasSupportingDocumentsTypes() && !member.validated_at && (
|
||||
<section className="refusal">
|
||||
<h3>{t('app.admin.supporting_documents_validation.refuse_documents')}</h3>
|
||||
<p className="text-black">{t('app.admin.supporting_documents_validation.refuse_documents_info')}</p>
|
||||
<FabButton className="refuse-btn" onClick={toggleModal}>{t('app.admin.supporting_documents_validation.refuse_documents')}</FabButton>
|
||||
<SupportingDocumentsRefusalModal
|
||||
isOpen={modalIsOpen}
|
||||
proofOfIdentityTypes={documentsTypes}
|
||||
toggleModal={toggleModal}
|
||||
operator={operator}
|
||||
member={member}
|
||||
onError={onError}
|
||||
onSuccess={onSaveRefusalSuccess}/>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SupportingDocumentsValidationWrapper: React.FC<SupportingDocumentsValidationProps> = (props) => {
|
||||
return (
|
||||
<Loader>
|
||||
<SupportingDocumentsValidation {...props} />
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
export { SupportingDocumentsValidationWrapper as SupportingDocumentsValidation };
|
||||
|
||||
Application.Components.component('supportingDocumentsValidation', react2angular(SupportingDocumentsValidationWrapper, ['operator', 'member', 'onSuccess', 'onError']));
|
@ -4,8 +4,8 @@ export interface ProofOfIdentityFileIndexFilter {
|
||||
}
|
||||
|
||||
export interface ProofOfIdentityFile {
|
||||
id: number,
|
||||
attachment: string,
|
||||
user_id: number,
|
||||
proof_of_identity_file_id: number,
|
||||
id?: number,
|
||||
attachment?: string,
|
||||
user_id?: number,
|
||||
proof_of_identity_type_id: number,
|
||||
}
|
||||
|
@ -83,6 +83,9 @@
|
||||
@import "modules/socials/fab-socials";
|
||||
@import "modules/subscriptions/free-extend-modal";
|
||||
@import "modules/subscriptions/renew-modal";
|
||||
@import "modules/supporting-documents/supporting-documents-files";
|
||||
@import "modules/supporting-documents/supporting-documents-refusal-form";
|
||||
@import "modules/supporting-documents/supporting-documents-validation";
|
||||
@import "modules/user/avatar";
|
||||
@import "modules/user/avatar-input";
|
||||
@import "modules/user/gender-input";
|
||||
|
@ -0,0 +1,120 @@
|
||||
.supporting-documents-files {
|
||||
background-color: #f4f3f3;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
margin: 30px;
|
||||
min-height: 1px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
position: relative;
|
||||
display: block;
|
||||
border-spacing: 0;
|
||||
|
||||
.info-area {
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.files-list {
|
||||
font-size: rem-calc(14);
|
||||
background: unset;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
&.has-error {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: 15px;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-bottom: 5px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.fileinput {
|
||||
display: table;
|
||||
border-collapse: separate;
|
||||
position: relative;
|
||||
margin-bottom: 9px;
|
||||
|
||||
.filename-container {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
|
||||
.fileinput-filename {
|
||||
vertical-align: bottom;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.file-download {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
|
||||
i {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
.fileinput-button {
|
||||
z-index: 1;
|
||||
border: 1px solid #c4c4c4;
|
||||
border-left: 0;
|
||||
border-radius: 0 4px 4px 0;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
background-color: #eee;
|
||||
color: #555;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
padding: 6px 12px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
width: 1%;
|
||||
display: table-cell;
|
||||
background-image: none;
|
||||
touch-action: manipulation;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.errors-area {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
color: lighten($text-color, 25%);
|
||||
}
|
||||
}
|
||||
.save-btn {
|
||||
background-color: var(--secondary-dark);
|
||||
border-color: var(--secondary-dark);
|
||||
color: var(--secondary-text-color);
|
||||
margin-bottom: 15px;
|
||||
margin-top: 15px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.supporting-documents-files {
|
||||
width: 58.3333333333%;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.supporting-documents-files {
|
||||
width: 100%;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.supporting-documents-files {
|
||||
width: 100%;
|
||||
float: left;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
.supporting-documents-refusal-form {
|
||||
input {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.refusal-comment {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
.supporting-documents-validation {
|
||||
section {
|
||||
background-color: #f4f3f3;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
margin: 30px;
|
||||
min-height: 1px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
position: relative;
|
||||
display: block;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.info-area {
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.document-type {
|
||||
margin-bottom: 15px;
|
||||
|
||||
.type-name {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.filename {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.missing-file {
|
||||
color: var(--error);
|
||||
}
|
||||
}
|
||||
|
||||
.refuse-btn {
|
||||
background-color: var(--secondary);
|
||||
border-color: var(--secondary);
|
||||
color: var(--secondary-text-color);
|
||||
margin-bottom: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.supporting-documents-validation section {
|
||||
width: 58.3333333333%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.supporting-documents-validation section.refusal {
|
||||
width: 33.33333333333333%;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.supporting-documents-validation section {
|
||||
width: 100%;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.supporting-documents-validation section {
|
||||
width: 100%;
|
||||
float: left;
|
||||
}
|
||||
}
|
@ -53,7 +53,7 @@
|
||||
on-error="onError"
|
||||
on-success="onUserSuccess"
|
||||
show-group-input="true"
|
||||
show-tags-input="true"
|
||||
show-tags-input="true"
|
||||
show-trainings-input="true" />
|
||||
</div>
|
||||
</section>
|
||||
@ -61,7 +61,7 @@
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab heading="{{ 'app.admin.members_edit.proof_of_identity_files' | translate }}" ng-show="hasProofOfIdentityTypes">
|
||||
<proof-of-identity-validation
|
||||
<supporting-documents-validation
|
||||
operator="currentUser"
|
||||
member="user"
|
||||
on-error="onError"
|
||||
|
@ -8,6 +8,6 @@
|
||||
|
||||
|
||||
<div class="row no-gutter">
|
||||
<proof-of-identity-files current-user="currentUser" on-success="onSuccess" on-error="onError" />
|
||||
<supporting-documents-files current-user="currentUser" on-success="onSuccess" on-error="onError" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -955,6 +955,20 @@ en:
|
||||
validate_member_error: "An unexpected error occurred: unable to validate this member."
|
||||
invalidate_member_error: "An unexpected error occurred: unable to invalidate this member."
|
||||
validate_account: "Validate the account"
|
||||
supporting_documents_refusal_form:
|
||||
refusal_comment: "Comment"
|
||||
comment_placeholder: "Please type a comment here"
|
||||
supporting_documents_refusal_modal:
|
||||
title: "Refuse some supporting documents"
|
||||
refusal_successfully_sent: "The refusal has been successfully sent."
|
||||
unable_to_send: "Unable to refuse the supporting documents: "
|
||||
confirm: "Confirm"
|
||||
supporting_documents_validation:
|
||||
title: "Supporting documents"
|
||||
find_below_documents_files: "You will find below the supporting documents submitted by the member."
|
||||
to_complete: "To complete"
|
||||
refuse_documents: "Refusing to proof of identity"
|
||||
refuse_documents_info: "After verification, you may notify the member that the evidence submitted is not acceptable. You can specify the reasons for your refusal and indicate the actions to be taken. The member will be notified by e-mail."
|
||||
#edit a member
|
||||
members_edit:
|
||||
change_role: "Change role"
|
||||
@ -1004,29 +1018,14 @@ en:
|
||||
cannot_extend_own_subscription: "You cannot extend your own subscription. Please ask another manager or an administrator to extend your subscription."
|
||||
update_success: "Member's profile successfully updated"
|
||||
my_documents: "My documents"
|
||||
my_documents_info: "Following the declaration of your profile, you must declare the proof of identity documents. Once submitted, these documents will be verified by the administrator."
|
||||
proof_of_identity_file_size_error: "The weight limit is {SIZE} MB max"
|
||||
my_documents_alert: "Attention!</br>You can submit your documents in pdf or in image (jpg) under a weight limit of {SIZE} Mb max"
|
||||
proof_of_identity_files: "Proof of identity"
|
||||
find_below_the_proof_of_identity_files: "You will find below the proof of identity documents submitted by the member."
|
||||
save: "Save"
|
||||
to_complete: "To complete"
|
||||
proof_of_identity_refusal: "Refusal of the proof of identity"
|
||||
refuse_proof_of_identity_files: "Refusing to proof of identity"
|
||||
refuse_proof_of_identity_files_info: "After verification, you may notify the member that the evidence submitted is not acceptable. You can specify the reasons for your refusal and indicate the actions to be taken. The member will be notified by e-mail."
|
||||
confirm: "Confirm"
|
||||
cancel: "Cancel"
|
||||
proof_of_identity_refusal_comment: "Comment"
|
||||
proof_of_identity_refuse_input_message: "Type your text"
|
||||
validate_account: "Validate the account"
|
||||
validate_member_success: "The member is validated"
|
||||
invalidate_member_success: "The member is invalidated"
|
||||
validate_member_error: "An error occurred: impossible to validate from this member."
|
||||
invalidate_member_error: "An error occurred: impossible to invalidate from this member."
|
||||
proof_of_identity_refusal_successfully_sent: "Refusal of the proof of identity has been sent."
|
||||
proof_of_identity_refusal_unable_to_send: "Impossible to refuse proof of identity : "
|
||||
proof_of_identity_files_successfully_uploaded: "The proof of identity has been sent."
|
||||
proof_of_identity_files_unable_to_upload: "Impossible to send proof of identity : "
|
||||
# extend a subscription for free
|
||||
free_extend_modal:
|
||||
extend_subscription: "Extend the subscription"
|
||||
@ -1556,16 +1555,19 @@ en:
|
||||
edit: "Edit"
|
||||
confirm: "Confirm"
|
||||
confirmation_required: "Confirmation required"
|
||||
do_you_really_want_to_delete_this_proof_of_identity_type: "Do you really want to remove this proof of identity ?"
|
||||
proof_of_identity_type_successfully_created: "The new proof of identity has been created."
|
||||
proof_of_identity_type_unable_to_create: "Unable to delete the proof of identity : "
|
||||
proof_of_identity_type_successfully_updated: "The proof of identity has been updated."
|
||||
proof_of_identity_type_unable_to_update: "Unable to modify the proof of identity : "
|
||||
proof_of_identity_type_deleted: "The proof of identity has been deleted."
|
||||
proof_of_identity_type_unable_to_delete: "Unable to delete the proof of identity : "
|
||||
organization: "Organization"
|
||||
organization_profile_custom_fields_info: "You can display additional fields for users who declare themselves to be an organization. You can also choose to make them mandatory at account creation."
|
||||
organization_profile_custom_fields_alert: "Attention: the activated fields will be automatically displayed on the issued invoices. Once configured, please do not modify them."
|
||||
delete_supporting_documents_type_modal:
|
||||
confirmation_required: "Confirmation required"
|
||||
confirm: "Confirm"
|
||||
deleted: "The credential has been deleted."
|
||||
proof_of_identity_type_unable_to_delete: "Unable to delete the proof of identity: "
|
||||
confirm_delete_supporting_documents_type: "Do you really want to remove this requested type of credential?"
|
||||
profile_custom_fields_list:
|
||||
field_successfully_updated: "The organization field has been updated."
|
||||
unable_to_update: "Impossible to modify the field : "
|
||||
|
@ -132,6 +132,16 @@ en:
|
||||
no_payment_schedules: "No payment schedules to display"
|
||||
load_more: "Load more"
|
||||
card_updated_success: "Your card was successfully updated"
|
||||
supporting_documents_files:
|
||||
file_successfully_uploaded: "The supporting documents were sent."
|
||||
unable_to_upload: "Unable to send the supporting documents: "
|
||||
supporting_documents_files: "Supporting documents"
|
||||
my_documents_info: "Due to your group declaration, some supporting documents are required. Once submitted, these documents will be verified by the administrator."
|
||||
upload_limits_alert_html: "Warning!</br>You can submit your documents as PDF or images (JPEG, PNG). Maximum allowed size: {SIZE} Mb"
|
||||
file_size_error: "The file size exceeds the limit ({SIZE} MB)"
|
||||
save: "Save"
|
||||
browse: "Browse"
|
||||
edit: "Edit"
|
||||
#public profil of a member
|
||||
members_show:
|
||||
members_list: "Members list"
|
||||
|
Loading…
x
Reference in New Issue
Block a user