diff --git a/.eslintrc b/.eslintrc index 58edfc126..1dd62ba79 100644 --- a/.eslintrc +++ b/.eslintrc @@ -50,11 +50,11 @@ "files": ["app/frontend/src/javascript/components/**/*.tsx"], "rules": { "import/no-default-export": "error", - "import/no-unused-modules": ["error", { "missingExports": true }], + "import/no-unused-modules": "error", "fabmanager/component-class-named-as-component": ["error", { "ignoreAbstractKeyword": true }], "fabmanager/component-named-like-file": "error", - "fabmanager/component-documentation": "error", - "fabmanager/component-methods-documentation": "error", + "fabmanager/component-documentation": "warn", + "fabmanager/component-methods-documentation": "warn", "fabmanager/no-bootstrap": "error", "fabmanager/no-utilities": "error", "fabmanager/scoped-translation": ["error", { "ignoreAbstractKeyword": false }] diff --git a/app/frontend/src/javascript/components/base/fab-panel.tsx b/app/frontend/src/javascript/components/base/fab-panel.tsx new file mode 100644 index 000000000..c586b7f81 --- /dev/null +++ b/app/frontend/src/javascript/components/base/fab-panel.tsx @@ -0,0 +1,26 @@ +import React, { ReactNode } from 'react'; + +interface FabPanelProps { + className?: string, + header?: ReactNode, + size?: 'small' | 'normal' +} + +/** + * Simple styled panel component + */ +export const FabPanel: React.FC = ({ className, header, size, children }) => { + return ( +
+ {header &&
+
+ {header} +
+
+ {children} +
+
} + {!header && children} +
+ ); +}; diff --git a/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx b/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx index c127a3ec5..c7878043f 100644 --- a/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx +++ b/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx @@ -132,7 +132,7 @@ export const LocalPaymentForm: React.FC = ({ onSubmit, onSucce value={methodToOption(method)} /> {method === 'card' &&

{t('app.admin.local_payment_form.card_collection_info')}

} {method === 'check' &&

{t('app.admin.local_payment_form.check_collection_info', { DEADLINES: paymentSchedule.items.length })}

} - {method === 'transfer' && } + {method === 'transfer' && }
    diff --git a/app/frontend/src/javascript/components/payment/payzen/payzen-settings.tsx b/app/frontend/src/javascript/components/payment/payzen/payzen-settings.tsx index cd2e3eb8d..706f8fab4 100644 --- a/app/frontend/src/javascript/components/payment/payzen/payzen-settings.tsx +++ b/app/frontend/src/javascript/components/payment/payzen/payzen-settings.tsx @@ -83,7 +83,7 @@ export const PayzenSettings: React.FC = ({ onEditKeys, onCu setError(''); updateSettings(draft => draft.set(SettingName.PayZenCurrency, value)); } else { - setError(t('app.admin.invoices.payment.payzen.currency_error')); + setError(t('app.admin.invoices.payment.payzen_settings.currency_error')); } }; @@ -97,18 +97,18 @@ export const PayzenSettings: React.FC = ({ onEditKeys, onCu updateSettings(draft => draft.set(SettingName.PayZenCurrency, result.value)); onCurrencyUpdateSuccess(result.value); }, reason => { - setError(t('app.admin.invoices.payment.payzen.error_while_saving') + reason); + setError(t('app.admin.invoices.payment.payzen_settings.error_while_saving') + reason); }); }; return (
    -

    {t('app.admin.invoices.payment.payzen.payzen_keys')}

    +

    {t('app.admin.invoices.payment.payzen_settings.payzen_keys')}

    {payZenPublicSettings.concat(payZenPrivateSettings).map(setting => { return (
    - + -1 ? 'password' : 'text'} @@ -119,17 +119,17 @@ export const PayzenSettings: React.FC = ({ onEditKeys, onCu ); })}
    - {t('app.admin.invoices.payment.edit_keys')} + {t('app.admin.invoices.payment.payzen_settings.edit_keys')}
    -

    {t('app.admin.invoices.payment.payzen.currency')}

    +

    {t('app.admin.invoices.payment.payzen_settings.currency')}

    - +

    - + } @@ -138,7 +138,7 @@ export const PayzenSettings: React.FC = ({ onEditKeys, onCu pattern="[A-Z]{3}" error={error} />
    - {t('app.admin.invoices.payment.payzen.save')} + {t('app.admin.invoices.payment.payzen_settings.save')}
    diff --git a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-type-modal.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-type-modal.tsx deleted file mode 100644 index ff7b5c95f..000000000 --- a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-type-modal.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { FabModal } from '../base/fab-modal'; -import { ProofOfIdentityType } from '../../models/proof-of-identity-type'; -import { Group } from '../../models/group'; -import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type'; -import { ProofOfIdentityTypeForm } from './proof-of-identity-type-form'; - -interface ProofOfIdentityTypeModalProps { - isOpen: boolean, - toggleModal: () => void, - onSuccess: (message: string) => void, - onError: (message: string) => void, - groups: Array, - proofOfIdentityType?: ProofOfIdentityType, -} - -export const ProofOfIdentityTypeModal: React.FC = ({ isOpen, toggleModal, onSuccess, onError, proofOfIdentityType, groups }) => { - const { t } = useTranslation('admin'); - - const [data, setData] = useState({ id: proofOfIdentityType?.id, group_ids: proofOfIdentityType?.group_ids || [], name: proofOfIdentityType?.name || '' }); - - useEffect(() => { - setData({ id: proofOfIdentityType?.id, group_ids: proofOfIdentityType?.group_ids || [], name: proofOfIdentityType?.name || '' }); - }, [proofOfIdentityType]); - - const handleProofOfIdentityTypeChanged = (field: string, value: string | Array) => { - setData({ - ...data, - [field]: value - }); - }; - - const handleSaveProofOfIdentityType = async (): Promise => { - try { - if (proofOfIdentityType?.id) { - await ProofOfIdentityTypeAPI.update(data); - onSuccess(t('app.admin.settings.account.proof_of_identity_type_successfully_updated')); - } else { - await ProofOfIdentityTypeAPI.create(data); - onSuccess(t('app.admin.settings.account.proof_of_identity_type_successfully_created')); - } - } catch (e) { - if (proofOfIdentityType?.id) { - onError(t('app.admin.settings.account.proof_of_identity_type_unable_to_update') + e); - } else { - onError(t('app.admin.settings.account.proof_of_identity_type_unable_to_create') + e); - } - } - }; - - const isPreventSaveProofOfIdentityType = (): boolean => { - return !data.name || data.group_ids.length === 0; - }; - - return ( - - - - ); -}; diff --git a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-types-list.tsx b/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-types-list.tsx deleted file mode 100644 index 372a89e0f..000000000 --- a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-types-list.tsx +++ /dev/null @@ -1,214 +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 { 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 { DeleteSupportingDocumentsTypeModal } from './delete-supporting-documents-type-modal'; -import GroupAPI from '../../api/group'; -import ProofOfIdentityTypeAPI from '../../api/proof-of-identity-type'; - -declare const Application: IApplication; - -interface ProofOfIdentityTypesListProps { - onSuccess: (message: string) => void, - onError: (message: string) => void, -} - -/** - * This component shows a list of all payment schedules with their associated deadlines (aka. PaymentScheduleItem) and invoices - */ -const ProofOfIdentityTypesList: React.FC = ({ onSuccess, onError }) => { - const { t } = useTranslation('admin'); - - // list of displayed supporting documents type - const [proofOfIdentityTypes, setProofOfIdentityTypes] = useState>([]); - const [proofOfIdentityType, setProofOfIdentityType] = useState(null); - const [proofOfIdentityTypeOrder, setProofOfIdentityTypeOrder] = useState(null); - const [modalIsOpen, setModalIsOpen] = useState(false); - const [groups, setGroups] = useState>([]); - const [destroyModalIsOpen, setDestroyModalIsOpen] = useState(false); - const [proofOfIdentityTypeId, setProofOfIdentityTypeId] = useState(null); - - // get groups - useEffect(() => { - GroupAPI.index({ disabled: false, admins: false }).then(data => { - setGroups(data); - ProofOfIdentityTypeAPI.index().then(pData => { - setProofOfIdentityTypes(pData); - }); - }); - }, []); - - /** - * Check if the current collection of supporting documents types is empty or not. - */ - const hasProofOfIdentityTypes = (): boolean => { - return proofOfIdentityTypes.length > 0; - }; - - const addProofOfIdentityType = (): void => { - setProofOfIdentityType(null); - setModalIsOpen(true); - }; - - const editProofOfIdentityType = (poit: ProofOfIdentityType): () => void => { - return (): void => { - setProofOfIdentityType(poit); - setModalIsOpen(true); - }; - }; - - const toggleCreateAndEditModal = (): void => { - setModalIsOpen(false); - }; - - const saveProofOfIdentityTypeOnSuccess = (message: string): void => { - setModalIsOpen(false); - ProofOfIdentityTypeAPI.index().then(pData => { - setProofOfIdentityTypes(orderProofOfIdentityTypes(pData, proofOfIdentityTypeOrder)); - onSuccess(message); - }).catch((error) => { - onError('Unable to load proof of identity types' + error); - }); - }; - - const destroyProofOfIdentityType = (id: number): () => void => { - return (): void => { - setProofOfIdentityTypeId(id); - setDestroyModalIsOpen(true); - }; - }; - - const toggleDestroyModal = (): void => { - setDestroyModalIsOpen(false); - }; - - const destroyProofOfIdentityTypeOnSuccess = (message: string): void => { - setDestroyModalIsOpen(false); - ProofOfIdentityTypeAPI.index().then(pData => { - setProofOfIdentityTypes(pData); - setProofOfIdentityTypes(orderProofOfIdentityTypes(pData, proofOfIdentityTypeOrder)); - onSuccess(message); - }).catch((error) => { - onError('Unable to load proof of identity types' + error); - }); - }; - - const setOrderProofOfIdentityType = (orderBy: string): () => void => { - return () => { - let order = orderBy; - if (proofOfIdentityTypeOrder === orderBy) { - order = `-${orderBy}`; - } - setProofOfIdentityTypeOrder(order); - setProofOfIdentityTypes(orderProofOfIdentityTypes(proofOfIdentityTypes, order)); - }; - }; - - const orderProofOfIdentityTypes = (poits: Array, orderBy?: string): Array => { - if (!orderBy) { - return poits; - } - const order = orderBy[0] === '-' ? 'desc' : 'asc'; - if (orderBy.search('group_name') !== -1) { - return _.orderBy(poits, (poit: ProofOfIdentityType) => getGroupName(poit.group_ids), order); - } else { - return _.orderBy(poits, 'name', order); - } - }; - - const orderClassName = (orderBy: string): string => { - if (proofOfIdentityTypeOrder) { - const order = proofOfIdentityTypeOrder[0] === '-' ? proofOfIdentityTypeOrder.substr(1) : proofOfIdentityTypeOrder; - if (order === orderBy) { - return `fa fa-arrows-v ${proofOfIdentityTypeOrder[0] === '-' ? 'fa-sort-alpha-desc' : 'fa-sort-alpha-asc'}`; - } - } - return 'fa fa-arrows-v'; - }; - - const getGroupName = (groupIds: Array): string => { - if (groupIds.length === groups.length && groupIds.length > 0) { - return t('app.admin.settings.account.all_groups'); - } - const _groups = _.filter(groups, (g: Group) => { return groupIds.includes(g.id); }); - return _groups.map((g: Group) => g.name).join(', '); - }; - - return ( -
    -
    - {t('app.admin.settings.account.add_proof_of_identity_types')} -
    -
    -
    -

    {t('app.admin.settings.account.proof_of_identity_type_info')}

    - -
    - -
    -

    {t('app.admin.settings.account.proof_of_identity_type_title')}

    - -
    - - - - - - - - - - - - - - {proofOfIdentityTypes.map(poit => { - return ( - - - - - - ); - })} - -
    {t('app.admin.settings.account.proof_of_identity_type.group_name')} {t('app.admin.settings.account.proof_of_identity_type.name')}
    {getGroupName(poit.group_ids)}{poit.name} -
    - - -
    -
    - {!hasProofOfIdentityTypes() && ( -

    - -

    - )} -
    -
    - ); -}; - -const ProofOfIdentityTypesListWrapper: React.FC = ({ onSuccess, onError }) => { - return ( - - - - ); -}; - -Application.Components.component('proofOfIdentityTypesList', react2angular(ProofOfIdentityTypesListWrapper, ['onSuccess', 'onError'])); diff --git a/app/frontend/src/javascript/components/supporting-documents/supporting-documents-files.tsx b/app/frontend/src/javascript/components/supporting-documents/supporting-documents-files.tsx index a59725738..2ab9f7d94 100644 --- a/app/frontend/src/javascript/components/supporting-documents/supporting-documents-files.tsx +++ b/app/frontend/src/javascript/components/supporting-documents/supporting-documents-files.tsx @@ -12,6 +12,7 @@ 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'; +import { FabPanel } from '../base/fab-panel'; declare let Fablab: IFablab; @@ -133,7 +134,7 @@ export const SupportingDocumentsFiles: React.FC = }; return ( -
    +

    {t('app.logged.dashboard.supporting_documents_files.supporting_documents_files')}

    {t('app.logged.dashboard.supporting_documents_files.my_documents_info')}

    @@ -189,7 +190,7 @@ export const SupportingDocumentsFiles: React.FC = {t('app.logged.dashboard.supporting_documents_files.save')} )} -
    + ); }; diff --git a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-type-form.tsx b/app/frontend/src/javascript/components/supporting-documents/supporting-documents-type-form.tsx similarity index 70% rename from app/frontend/src/javascript/components/supporting-documents/proof-of-identity-type-form.tsx rename to app/frontend/src/javascript/components/supporting-documents/supporting-documents-type-form.tsx index 6b3309dd3..da5538c2c 100644 --- a/app/frontend/src/javascript/components/supporting-documents/proof-of-identity-type-form.tsx +++ b/app/frontend/src/javascript/components/supporting-documents/supporting-documents-type-form.tsx @@ -5,7 +5,7 @@ import { FabInput } from '../base/fab-input'; import { ProofOfIdentityType } from '../../models/proof-of-identity-type'; import { Group } from '../../models/group'; -interface ProofOfIdentityTypeFormProps { +interface SupportingDocumentsTypeFormProps { groups: Array, proofOfIdentityType?: ProofOfIdentityType, onChange: (field: string, value: string | Array) => void, @@ -18,13 +18,13 @@ interface ProofOfIdentityTypeFormProps { type selectOption = { value: number, label: string }; /** - * Form to set the stripe's public and private keys + * Form to set create/edit supporting documents type */ -export const ProofOfIdentityTypeForm: React.FC = ({ groups, proofOfIdentityType, onChange }) => { +export const SupportingDocumentsTypeForm: React.FC = ({ groups, proofOfIdentityType, onChange }) => { const { t } = useTranslation('admin'); /** - * Convert all themes to the react-select format + * Convert all groups to the react-select format */ const buildOptions = (): Array => { return groups.map(t => { @@ -33,7 +33,7 @@ export const ProofOfIdentityTypeForm: React.FC = ( }; /** - * Return the current groups(s), formatted to match the react-select format + * Return the group(s) associated with the current type, formatted to match the react-select format */ const groupsValues = (): Array => { const res = []; @@ -63,23 +63,23 @@ export const ProofOfIdentityTypeForm: React.FC = ( }; return ( -
    -
    - {t('app.admin.settings.account.proof_of_identity_type_form_info')} +
    +
    + {t('app.admin.settings.account.supporting_documents_type_form.type_form_info')}
    -
    +