diff --git a/package-lock.json b/package-lock.json index 7f652af0c..14d2036cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "easyqrcodejs": "^4.4.13", "file-saver": "^2.0.5", "i18n-iso-countries": "^7.13.0", - "i18next": "^24.0.2", + "i18next": "^25.0.2", "jquery": "^3.6.3", "jszip": "^3.10.1", "libphonenumber-js": "^1.10.41", @@ -8534,9 +8534,9 @@ } }, "node_modules/i18next": { - "version": "24.0.2", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.0.2.tgz", - "integrity": "sha512-D88xyIGcWAKwBTAs4RSqASi8NXR/NhCVSTM4LDbdoU8qb/5dcEZjNCLDhtQBB7Epw/Cp1w2vH/3ujoTbqLSs5g==", + "version": "25.0.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.0.2.tgz", + "integrity": "sha512-xWxgK8GAaPYkV9ia2tdgbtdM+qiC+ysVTBPvXhpCORU/+QkeQe3BSI7Crr+c4ZXULN1PfnXG/HY2n7HGx4KKBg==", "funding": [ { "type": "individual", @@ -8553,7 +8553,7 @@ ], "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.2" + "@babel/runtime": "^7.26.10" }, "peerDependencies": { "typescript": "^5" diff --git a/package.json b/package.json index 0327cab8a..aff18acdc 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "easyqrcodejs": "^4.4.13", "file-saver": "^2.0.5", "i18n-iso-countries": "^7.13.0", - "i18next": "^24.0.2", + "i18next": "^25.0.2", "jquery": "^3.6.3", "jszip": "^3.10.1", "libphonenumber-js": "^1.10.41", diff --git a/react/assets/forus-platform/scss/_common/blocks/block-inline-copy.scss b/react/assets/forus-platform/scss/_common/blocks/block-inline-copy.scss new file mode 100644 index 000000000..da740ebe4 --- /dev/null +++ b/react/assets/forus-platform/scss/_common/blocks/block-inline-copy.scss @@ -0,0 +1,22 @@ +.block.block-inline-copy { + display: inline-flex; + flex-direction: row; + align-items: center; + gap: 4px; + cursor: pointer; + + .inline-copy-icon { + display: inline-flex; + font-size: 16px; + line-height: 18px; + height: 18px; + transition: 0.4s; + color: #646f79; + } + + &:hover { + .inline-copy-icon { + color: var(--color-primary); + } + } +} diff --git a/react/assets/forus-platform/scss/_common/blocks/block-translation-stats.scss b/react/assets/forus-platform/scss/_common/blocks/block-translation-stats.scss new file mode 100644 index 000000000..467e88c9d --- /dev/null +++ b/react/assets/forus-platform/scss/_common/blocks/block-translation-stats.scss @@ -0,0 +1,34 @@ +.block.block-translation-stats { + display: flex; + flex-direction: row; + margin-bottom: 20px; + gap: 20px; + + .translation-stats-col { + display: flex; + flex-direction: column; + flex: 0 0 calc(50% - 10px); + background: #ffffff; + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + padding: 15px; + gap: 15px; + + .translation-stats-col-label { + display: inline-flex; + font: 500 14px/20px var(--base-font); + + .translation-stats-col-label-period { + color: #646F79; + display: flex; + flex: 1 1 auto; + justify-content: flex-end; + } + } + + .translation-stats-col-value { + font: 600 18px/26px var(--base-font); + } + } +} diff --git a/react/assets/forus-platform/scss/_common/components/modals.scss b/react/assets/forus-platform/scss/_common/components/modals.scss index c2cec8692..1b189181f 100644 --- a/react/assets/forus-platform/scss/_common/components/modals.scss +++ b/react/assets/forus-platform/scss/_common/components/modals.scss @@ -317,7 +317,6 @@ .modal-text { font: 500 14px/22px var(--base-font); color: #646f79; - text-align: center; margin: 0 0 20px; small { @@ -582,104 +581,6 @@ } } - &.modal-voucher-export-type { - .modal-window { - width: 940px; - - .modal-body { - .voucher-export-type { - text-align: center; - cursor: default; - padding: 10px 0; - - .voucher-export-type-row { - display: flex; - justify-content: center; - justify-items: center; - } - - .voucher-export-type-item { - margin-right: 30px; - flex-grow: 1; - padding: 25px 20px; - font: 500 14px var(--base-font); - color: #282b39; - border: 1px solid #d4d9dd; - border-radius: calc(var(--border-radius)); - position: relative; - transition: 0.4s; - background: transparent; - cursor: pointer; - flex-basis: 25%; - - .voucher-export-type-item-check { - position: absolute; - top: 10px; - right: 10px; - border-radius: 22px; - width: 22px; - height: 22px; - line-height: 22px; - text-align: center; - background: #2cc678; - color: #fff; - opacity: 0; - transition: 0.4s; - } - - .voucher-export-type-item-icon { - margin-bottom: 15px; - width: 100%; - - .voucher-export-type-item-icon-img { - display: block; - margin: 0 auto 0; - width: 100%; - } - - .mdi { - font-size: 64px; - text-align: center; - line-height: 60px; - padding: 10px 0 0 0; - display: block; - color: var(--color-primary); - } - } - - &:hover { - color: #009ef4; - background: #fff; - border-color: #fff; - box-shadow: 2px 10px 15px rgba($color: #000000, $alpha: 0.075); - } - - &.active { - background: #fff; - color: #282b39; - border: 1px solid #d4d9dd; - box-shadow: 1px 5px 7.5px rgba($color: #000000, $alpha: 0.075); - - .voucher-export-type-item-check { - opacity: 1; - } - } - - &:last-child { - margin-right: 0; - } - } - } - } - } - - &.modal-voucher-export-narrow { - .modal-window { - width: 640px; - } - } - } - &.modal-lg { .modal-window { width: 960px; diff --git a/react/assets/forus-platform/scss/_common/components/tables.scss b/react/assets/forus-platform/scss/_common/components/tables.scss index 18e18b5c1..6b32e1c91 100644 --- a/react/assets/forus-platform/scss/_common/components/tables.scss +++ b/react/assets/forus-platform/scss/_common/components/tables.scss @@ -547,6 +547,14 @@ &.selected { background: #f8fbf7; } + + &.tr-totals { + border-top: 2px solid var(--color-primary); + + td { + background-color: #fafbfc; + } + } } & > tbody:last-child > tr { diff --git a/react/assets/forus-platform/scss/_common/dashboard.scss b/react/assets/forus-platform/scss/_common/dashboard.scss index 27e8d5084..154fad7f3 100644 --- a/react/assets/forus-platform/scss/_common/dashboard.scss +++ b/react/assets/forus-platform/scss/_common/dashboard.scss @@ -95,10 +95,12 @@ @import 'blocks/block-approve-fund-request.scss'; @import 'blocks/block-provider-products-required.scss'; @import 'blocks/block-push-notifications.scss'; +@import 'blocks/block-inline-copy.scss'; @import 'blocks/block-collapsable.scss'; @import 'blocks/block-fund-request-assigned.scss'; @import 'blocks/block-product.scss'; @import 'blocks/block-label-tabs.scss'; +@import 'blocks/block-translation-stats.scss'; // Select controls @import 'select-controls/select-control-funds'; @@ -1345,7 +1347,7 @@ body { } &.card-outline { - border: 1px solid #8ca3a6; + border: 1px solid var(--border-color); } &.card-collapsable { @@ -5171,7 +5173,7 @@ body { .inline-filters-dropdown-content { width: 260px; - min-height: 100px; + min-height: 75px; position: absolute; top: 100%; margin-top: 15px; diff --git a/react/assets/forus-platform/scss/_common/includes/common.scss b/react/assets/forus-platform/scss/_common/includes/common.scss index 9e3dc5738..e004c7345 100644 --- a/react/assets/forus-platform/scss/_common/includes/common.scss +++ b/react/assets/forus-platform/scss/_common/includes/common.scss @@ -120,6 +120,10 @@ img { transition: color 0.3s; } +.text-default { + font: 400 13px/20px var(--base-font); +} + .text-muted { color: #a1a1a1 !important; transition: color 0.3s; @@ -211,6 +215,14 @@ img { font-weight: 600 !important; } +.text-decoration-link { + text-decoration: underline; + + &:hover { + text-decoration: none; + } +} + .text-medium, .text-strong-half { font-weight: 500 !important; diff --git a/react/src/dashboard/Dashboard.tsx b/react/src/dashboard/Dashboard.tsx index c7c0cc1b8..88ec3f8b1 100644 --- a/react/src/dashboard/Dashboard.tsx +++ b/react/src/dashboard/Dashboard.tsx @@ -21,6 +21,10 @@ import i18nEN from './i18n/i18n-en'; import i18nNL from './i18n/i18n-nl'; import { FrameDirectorProvider } from './modules/frame_director/context/FrameDirectorContext'; import ProviderNotificationProductRequired from './modules/provider_notification_product_required/ProviderNotificationProductRequired'; +import { setDefaultOptions } from 'date-fns'; +import { nl } from 'date-fns/locale'; + +setDefaultOptions({ weekStartsOn: 1, locale: nl }); i18n.use(initReactI18next) .init({ diff --git a/react/src/dashboard/components/elements/block-card-emails/BlockCardEmails.tsx b/react/src/dashboard/components/elements/block-card-emails/BlockCardEmails.tsx index d2aea914c..78c9718ba 100644 --- a/react/src/dashboard/components/elements/block-card-emails/BlockCardEmails.tsx +++ b/react/src/dashboard/components/elements/block-card-emails/BlockCardEmails.tsx @@ -18,22 +18,28 @@ import Paginator from '../../../modules/paginator/components/Paginator'; import { trimStart } from 'lodash'; import { extractText } from '../../../helpers/utils'; import useFilterNext from '../../../modules/filter_next/useFilterNext'; +import useEmailLogService from '../../../services/EmailLogService'; +import { useFileService } from '../../../services/FileService'; +import Organization from '../../../props/models/Organization'; export default function BlockCardEmails({ + organization, fetchLogEmails, fetchEmailsRef, - onExportEmail, }: { + organization: Organization; fetchLogEmails: (value: FilterModel) => Promise>; fetchEmailsRef?: React.MutableRefObject<() => void>; - onExportEmail?: (emailLog: EmailLog) => void; }) { const openModal = useOpenModal(); const pushApiError = usePushApiError(); const setProgress = useSetProgress(); + const fileService = useFileService(); const paginatorService = usePaginatorService(); + const emailLogService = useEmailLogService(); + const [emailLogs, setEmailLogs] = useState>(null); const [paginatorKey] = useState('fund_request_email_logs'); @@ -47,13 +53,26 @@ export default function BlockCardEmails({ per_page: paginatorService.getPerPage(paginatorKey), }); + const exportEmailLog = useCallback( + (emailLog: EmailLog) => { + emailLogService + .export(organization.id, emailLog.id) + .then((res) => fileService.downloadFile(`email-log-${emailLog.id}.pdf`, res.data)) + .catch(pushApiError) + .finally(() => setProgress(100)); + + setProgress(0); + }, + [organization.id, fileService, emailLogService, pushApiError, setProgress], + ); + const openEmail = useCallback( (logEmail: EmailLog) => { openModal((modal) => { - return ; + return ; }); }, - [openModal, onExportEmail], + [openModal, exportEmailLog], ); const fetchEmails = useCallback(() => { @@ -76,7 +95,9 @@ export default function BlockCardEmails({ }, [emailLogs, openEmail]); useEffect(() => { - fetchEmailsRef.current = fetchEmails; + if (fetchEmailsRef) { + fetchEmailsRef.current = fetchEmails; + } }, [fetchEmailsRef, fetchEmails]); if (!emailLogs) { @@ -84,7 +105,7 @@ export default function BlockCardEmails({ } return ( -
+
Berichten  @@ -120,7 +141,7 @@ export default function BlockCardEmails({ Actions {emailLogs?.data.map((emailLog) => ( - + @@ -150,10 +171,12 @@ export default function BlockCardEmails({ ( )} /> diff --git a/react/src/dashboard/components/elements/block-inline-copy/BlockInlineCopy.tsx b/react/src/dashboard/components/elements/block-inline-copy/BlockInlineCopy.tsx new file mode 100644 index 000000000..096d5da29 --- /dev/null +++ b/react/src/dashboard/components/elements/block-inline-copy/BlockInlineCopy.tsx @@ -0,0 +1,29 @@ +import React, { ReactNode } from 'react'; +import useCopyToClipboard from '../../../hooks/useCopyToClipboard'; +import classNames from 'classnames'; + +export default function BlockInlineCopy({ + children, + className, + value, +}: { + children: ReactNode | ReactNode[]; + className?: string; + value?: string; +}) { + const copyToClipboard = useCopyToClipboard(); + + return ( +
{ + e?.preventDefault(); + e?.stopPropagation(); + + copyToClipboard(value); + }}> + {children} + +
+ ); +} diff --git a/react/src/dashboard/components/elements/card/Card.tsx b/react/src/dashboard/components/elements/card/Card.tsx index 6fedce4a7..5c510bdb9 100644 --- a/react/src/dashboard/components/elements/card/Card.tsx +++ b/react/src/dashboard/components/elements/card/Card.tsx @@ -8,6 +8,7 @@ export default function Card({ children, footer, footerHidden = false, + dusk = null, }: { title: string; section?: boolean; @@ -15,9 +16,10 @@ export default function Card({ children: ReactNode | ReactNode[]; footer?: ReactNode | ReactNode[]; footerHidden?: boolean; + dusk?: string; }) { return ( -
+
{title}
diff --git a/react/src/dashboard/components/elements/empty-value/EmptyValue.tsx b/react/src/dashboard/components/elements/empty-value/EmptyValue.tsx new file mode 100644 index 000000000..4c6057ee7 --- /dev/null +++ b/react/src/dashboard/components/elements/empty-value/EmptyValue.tsx @@ -0,0 +1,5 @@ +import React, { ReactNode } from 'react'; + +export default function EmptyValue({ children = '---' }: { children?: ReactNode | ReactNode[] }) { + return
{children}
; +} diff --git a/react/src/dashboard/components/elements/forms/controls/CheckboxControl.tsx b/react/src/dashboard/components/elements/forms/controls/CheckboxControl.tsx index 443686c5e..d6486264a 100644 --- a/react/src/dashboard/components/elements/forms/controls/CheckboxControl.tsx +++ b/react/src/dashboard/components/elements/forms/controls/CheckboxControl.tsx @@ -14,6 +14,7 @@ export default function CheckboxControl({ onChange, className, children, + dusk = null, }: { id?: string; title?: string; @@ -26,6 +27,7 @@ export default function CheckboxControl({ className?: string; customElement?: React.ReactElement; children?: string | React.ReactNode | Array; + dusk?: string; }) { const formId = useMemo(() => (id ? id : `checkbox_control_${uniqueId()}`), [id]); @@ -33,6 +35,7 @@ export default function CheckboxControl({
; + return ; } diff --git a/react/src/dashboard/components/elements/tables/elements/CardHeaderFilter.tsx b/react/src/dashboard/components/elements/tables/elements/CardHeaderFilter.tsx index c2d6e95f3..bdf5bcddc 100644 --- a/react/src/dashboard/components/elements/tables/elements/CardHeaderFilter.tsx +++ b/react/src/dashboard/components/elements/tables/elements/CardHeaderFilter.tsx @@ -24,7 +24,7 @@ export default function CardHeaderFilter({ )} -
diff --git a/react/src/dashboard/components/modals/ModalExportTypeLegacy.tsx b/react/src/dashboard/components/modals/ModalExportTypeLegacy.tsx deleted file mode 100644 index 4b36dce7d..000000000 --- a/react/src/dashboard/components/modals/ModalExportTypeLegacy.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React, { useState } from 'react'; -import { ModalState } from '../../modules/modals/context/ModalContext'; -import { ModalButton } from './elements/ModalButton'; -import useTranslate from '../../hooks/useTranslate'; -import classNames from 'classnames'; - -export default function ModalExportTypeLegacy({ - modal, - onSubmit, - buttonCancel, - buttonSubmit, -}: { - modal: ModalState; - onSubmit?: (exportType: string) => void; - buttonCancel?: ModalButton; - buttonSubmit?: ModalButton; -}) { - const translate = useTranslate(); - const [exportTypes] = useState(['xls', 'csv']); - const [exportType, setExportType] = useState('xls'); - - return ( -
-
-
-
-
{translate('modals.modal_voucher_export.title')}
- -
-
-
-
- {exportTypes.map((type) => ( -
setExportType(type)}> -
- -
-
- -
- {translate(`modals.modal_voucher_export.modal_section.export_type_${type}`)} -
- ))} -
-
-
-
- -
- {} - { - { - modal.close(); - onSubmit(exportType); - }, - ...buttonSubmit, - }} - submit={true} - text="Bevestigen" - type="primary" - /> - } -
-
-
- ); -} diff --git a/react/src/dashboard/components/modals/ModalLogEmailShow.tsx b/react/src/dashboard/components/modals/ModalLogEmailShow.tsx index bbd8916fa..beabf0e88 100644 --- a/react/src/dashboard/components/modals/ModalLogEmailShow.tsx +++ b/react/src/dashboard/components/modals/ModalLogEmailShow.tsx @@ -15,7 +15,9 @@ export default function ModalLogEmailShow({ exportEmailLog: (emailLog: EmailLog) => void; }) { return ( -
+
@@ -87,7 +89,7 @@ export default function ModalLogEmailShow({
-
+
Sluiten
setFund(fund)} optionsComponent={SelectControlOptionsFund} + dusk="prevalidationSelectFund" />
diff --git a/react/src/dashboard/components/pages/csv_validations/elements/PrevalidatedTable.tsx b/react/src/dashboard/components/pages/csv_validations/elements/PrevalidatedTable.tsx index 3a3b71d54..cffff506d 100644 --- a/react/src/dashboard/components/pages/csv_validations/elements/PrevalidatedTable.tsx +++ b/react/src/dashboard/components/pages/csv_validations/elements/PrevalidatedTable.tsx @@ -8,7 +8,6 @@ import FilterItemToggle from '../../../elements/tables/elements/FilterItemToggle import SelectControl from '../../../elements/select-control/SelectControl'; import { dateFormat, dateParse } from '../../../../helpers/dates'; import DatePickerControl from '../../../elements/forms/controls/DatePickerControl'; -import ModalExportTypeLegacy from '../../../modals/ModalExportTypeLegacy'; import useOpenModal from '../../../../hooks/useOpenModal'; import { PaginationData } from '../../../../props/ApiResponses'; import Prevalidation from '../../../../props/models/Prevalidation'; @@ -19,17 +18,15 @@ import useActiveOrganization from '../../../../hooks/useActiveOrganization'; import ModalNotification from '../../../modals/ModalNotification'; import usePushSuccess from '../../../../hooks/usePushSuccess'; import LoadingCard from '../../../elements/loading-card/LoadingCard'; -import { format } from 'date-fns'; -import { useFileService } from '../../../../services/FileService'; import EmptyCard from '../../../elements/empty-card/EmptyCard'; import useTranslate from '../../../../hooks/useTranslate'; import useSetProgress from '../../../../hooks/useSetProgress'; import Employee from '../../../../props/models/Employee'; -import usePushApiError from '../../../../hooks/usePushApiError'; import useConfigurableTable from '../../vouchers/hooks/useConfigurableTable'; import TableTopScroller from '../../../elements/tables/TableTopScroller'; import TableRowActions from '../../../elements/tables/TableRowActions'; import TableEmptyValue from '../../../elements/table-empty-value/TableEmptyValue'; +import usePrevalidationExporter from '../../../../services/exporters/usePrevalidationExporter'; export default function PrevalidatedTable({ fund, @@ -43,11 +40,11 @@ export default function PrevalidatedTable({ const translate = useTranslate(); const openModal = useOpenModal(); const pushSuccess = usePushSuccess(); - const pushApiError = usePushApiError(); const setProgress = useSetProgress(); + const activeOrganization = useActiveOrganization(); + const prevalidationExporter = usePrevalidationExporter(); - const fileService = useFileService(); const employeeService = useEmployeeService(); const paginatorService = usePaginatorService(); const prevalidationService = usePrevalidationService(); @@ -121,31 +118,13 @@ export default function PrevalidatedTable({ per_page: paginatorService.getPerPage(paginatorKey), }); - const doExport = useCallback( - (exportType: string) => { - prevalidationService - .export({ ...externalFilters, ...filter.activeValues, fund_id: fund.id, export_type: exportType }) - .then((res) => { - const dateTime = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); - const fileType = res.headers['content-type'] + ';charset=utf-8;'; - const fileName = `${fund.key || 'fund'}_${dateTime}.${exportType}`; - - fileService.downloadFile(fileName, res.data, fileType); - }, pushApiError); - }, - [fileService, externalFilters, filter.activeValues, fund.key, fund.id, prevalidationService, pushApiError], - ); - const exportData = useCallback(() => { - openModal((modal) => ( - { - doExport(exportType); - }} - /> - )); - }, [doExport, openModal]); + prevalidationExporter.exportData(activeOrganization.id, { + ...externalFilters, + ...filter.activeValues, + fund_id: fund.id, + }); + }, [activeOrganization.id, externalFilters, filter.activeValues, fund.id, prevalidationExporter]); const fetchPrevalidations = useCallback(() => { setProgress(0); @@ -206,7 +185,7 @@ export default function PrevalidatedTable({ } return ( -
+
{translate('prevalidated_table.header.title')}
@@ -229,6 +208,7 @@ export default function PrevalidatedTable({ filter.update({ q: e.target.value })} @@ -300,6 +280,7 @@ export default function PrevalidatedTable({ @@ -333,7 +315,7 @@ export default function PrevalidatedTable({ {rows?.map((row) => ( - + {row.uid} {employeesByAddress?.[row?.identity_address] || 'Unknown'} diff --git a/react/src/dashboard/components/pages/employees/Employees.tsx b/react/src/dashboard/components/pages/employees/Employees.tsx index a0769a20d..6e300bde7 100644 --- a/react/src/dashboard/components/pages/employees/Employees.tsx +++ b/react/src/dashboard/components/pages/employees/Employees.tsx @@ -11,16 +11,12 @@ import Paginator from '../../../modules/paginator/components/Paginator'; import ModalEmployeeEdit from '../../modals/ModalEmployeeEdit'; import ModalDangerZone from '../../modals/ModalDangerZone'; import ModalTransferOrganizationOwnership from '../../modals/ModalTransferOrganizationOwnership'; -import ModalExportTypeLegacy from '../../modals/ModalExportTypeLegacy'; -import { format } from 'date-fns'; -import { useFileService } from '../../../services/FileService'; import { PaginationData } from '../../../props/ApiResponses'; import LoadingCard from '../../elements/loading-card/LoadingCard'; import useAuthIdentity from '../../../hooks/useAuthIdentity'; import usePushSuccess from '../../../hooks/usePushSuccess'; import useOpenModal from '../../../hooks/useOpenModal'; import useActiveOrganization from '../../../hooks/useActiveOrganization'; -import useEnvData from '../../../hooks/useEnvData'; import usePaginatorService from '../../../modules/paginator/services/usePaginatorService'; import useTranslate from '../../../hooks/useTranslate'; import LoaderTableCard from '../../elements/loader-table-card/LoaderTableCard'; @@ -31,9 +27,9 @@ import TableEmptyValue from '../../elements/table-empty-value/TableEmptyValue'; import useConfigurableTable from '../vouchers/hooks/useConfigurableTable'; import TableTopScroller from '../../elements/tables/TableTopScroller'; import TableRowActions from '../../elements/tables/TableRowActions'; +import useEmployeeExporter from '../../../services/exporters/useEmployeeExporter'; export default function Employees() { - const envData = useEnvData(); const isProviderPanel = useIsProviderPanel(); const { setActiveOrganization } = useContext(mainContext); @@ -43,10 +39,11 @@ export default function Employees() { const pushSuccess = usePushSuccess(); const setProgress = useSetProgress(); const pushApiError = usePushApiError(); + const authIdentity = useAuthIdentity(); const activeOrganization = useActiveOrganization(); + const employeeExporter = useEmployeeExporter(); - const fileService = useFileService(); const employeeService = useEmployeeService(); const paginatorService = usePaginatorService(); @@ -127,34 +124,14 @@ export default function Employees() { ], ); - const doExport = useCallback( - (exportType: string) => { - employeeService - .export(activeOrganization.id, { ...filter.activeValues, export_type: exportType }) - .then((res) => { - const dateTime = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); - const fileName = `${envData.client_type}_${activeOrganization.name}_employees_${dateTime}.${exportType}`; - - fileService.downloadFile(fileName, res.data, res.headers['content-type']); - }) - .catch(pushApiError); - }, - [pushApiError, fileService, filter.activeValues, activeOrganization, employeeService, envData.client_type], - ); - const exportEmployees = useCallback(() => { - openModal((modal) => ( - { - doExport(exportType); - }} - /> - )); - }, [doExport, openModal]); + employeeExporter.exportData(activeOrganization.id, { + ...filter.activeValues, + }); + }, [activeOrganization.id, employeeExporter, filter.activeValues]); const transferOwnership = useCallback( - function (adminEmployees) { + function (adminEmployees: Array) { openModal((modal) => ( ( +
Medewerkers ({employees?.meta.total})
@@ -242,6 +219,7 @@ export default function Employees() { )}
diff --git a/react/src/dashboard/components/pages/financial-dashboard-overview/elements/FinancialOverviewFundsBudgetTable.tsx b/react/src/dashboard/components/pages/financial-dashboard-overview/elements/FinancialOverviewFundsBudgetTable.tsx index cde7fbc08..14ab923f2 100644 --- a/react/src/dashboard/components/pages/financial-dashboard-overview/elements/FinancialOverviewFundsBudgetTable.tsx +++ b/react/src/dashboard/components/pages/financial-dashboard-overview/elements/FinancialOverviewFundsBudgetTable.tsx @@ -1,8 +1,7 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { currencyFormat } from '../../../../helpers/string'; import Tooltip from '../../../elements/tooltip/Tooltip'; import FinancialOverviewFundsBudgetTableItem from './FinancialOverviewFundsBudgetTableItem'; -import useExportFunds from '../hooks/useExportFunds'; import Fund from '../../../../props/models/Fund'; import Organization from '../../../../props/models/Organization'; import { FinancialOverview } from '../../financial-dashboard/types/FinancialStatisticTypes'; @@ -14,6 +13,7 @@ import { useFundService } from '../../../../services/FundService'; import useConfigurableTable from '../../vouchers/hooks/useConfigurableTable'; import TableTopScroller from '../../../elements/tables/TableTopScroller'; import TableEmptyValue from '../../../elements/table-empty-value/TableEmptyValue'; +import useFundExporter from '../../../../services/exporters/useFundExporter'; export default function FinancialOverviewFundsBudgetTable({ years, @@ -33,10 +33,10 @@ export default function FinancialOverviewFundsBudgetTable({ loaded: boolean; }) { const translate = useTranslate(); - const exportFunds = useExportFunds(organization); - const setProgress = useSetProgress(); + const fundExporter = useFundExporter(); + const [funds, setFunds] = useState>(null); const [financialOverview, setFinancialOverview] = useState(null); @@ -48,6 +48,10 @@ export default function FinancialOverviewFundsBudgetTable({ const { headElement, configsElement } = useConfigurableTable(fundService.getColumnsBudget()); + const exportFunds = useCallback(() => { + fundExporter.exportData(organization.id, true, year); + }, [fundExporter, organization.id, year]); + useEffect(() => { if (!loaded) return; @@ -84,7 +88,10 @@ export default function FinancialOverviewFundsBudgetTable({ />
- diff --git a/react/src/dashboard/components/pages/financial-dashboard-overview/elements/FinancialOverviewFundsTable.tsx b/react/src/dashboard/components/pages/financial-dashboard-overview/elements/FinancialOverviewFundsTable.tsx index 96f1c03df..b1e40bb5d 100644 --- a/react/src/dashboard/components/pages/financial-dashboard-overview/elements/FinancialOverviewFundsTable.tsx +++ b/react/src/dashboard/components/pages/financial-dashboard-overview/elements/FinancialOverviewFundsTable.tsx @@ -1,7 +1,6 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import Tooltip from '../../../elements/tooltip/Tooltip'; import Fund from '../../../../props/models/Fund'; -import useExportFunds from '../hooks/useExportFunds'; import Organization from '../../../../props/models/Organization'; import { FinancialOverview } from '../../financial-dashboard/types/FinancialStatisticTypes'; import useTranslate from '../../../../hooks/useTranslate'; @@ -12,6 +11,7 @@ import useSetProgress from '../../../../hooks/useSetProgress'; import { useFundService } from '../../../../services/FundService'; import TableTopScroller from '../../../elements/tables/TableTopScroller'; import useConfigurableTable from '../../vouchers/hooks/useConfigurableTable'; +import useFundExporter from '../../../../services/exporters/useFundExporter'; export default function FinancialOverviewFundsTable({ years, @@ -31,10 +31,10 @@ export default function FinancialOverviewFundsTable({ setLoaded?: () => void; }) { const translate = useTranslate(); - const exportFunds = useExportFunds(organization); - const setProgress = useSetProgress(); + const fundExporter = useFundExporter(); + const fundService = useFundService(); const [funds, setFunds] = useState>(null); @@ -42,6 +42,10 @@ export default function FinancialOverviewFundsTable({ const { headElement, configsElement } = useConfigurableTable(fundService.getColumnsBalance()); + const exportFunds = useCallback(() => { + fundExporter.exportData(organization.id, false, year); + }, [fundExporter, organization.id, year]); + useEffect(() => { setProgress(0); @@ -82,7 +86,10 @@ export default function FinancialOverviewFundsTable({ />
- diff --git a/react/src/dashboard/components/pages/financial-dashboard-overview/hooks/useExportFunds.tsx b/react/src/dashboard/components/pages/financial-dashboard-overview/hooks/useExportFunds.tsx deleted file mode 100644 index da9d1a85c..000000000 --- a/react/src/dashboard/components/pages/financial-dashboard-overview/hooks/useExportFunds.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { useCallback } from 'react'; -import ModalExportTypeLegacy from '../../../modals/ModalExportTypeLegacy'; -import { format } from 'date-fns'; -import useOpenModal from '../../../../hooks/useOpenModal'; -import { useFileService } from '../../../../services/FileService'; -import { useFundService } from '../../../../services/FundService'; -import Organization from '../../../../props/models/Organization'; -import useEnvData from '../../../../hooks/useEnvData'; -import usePushApiError from '../../../../hooks/usePushApiError'; - -export default function useExportFunds(organization: Organization) { - const envData = useEnvData(); - - const openModal = useOpenModal(); - const pushApiError = usePushApiError(); - - const fileService = useFileService(); - const fundService = useFundService(); - - const doExport = useCallback( - (exportType: string, detailed: boolean, year: number) => { - fundService - .financialOverviewExport(organization.id, { - export_type: exportType, - detailed: detailed ? 1 : 0, - year: year, - }) - .then((res) => { - const dateTime = - year != new Date().getFullYear() ? year : format(new Date(), 'yyyy-MM-dd HH:mm:ss'); - const fileName = `${envData.client_type}_${organization.name}_${dateTime}.${exportType}`; - const fileType = res.headers['content-type'] + ';charset=utf-8;'; - - fileService.downloadFile(fileName, res.data, fileType); - }) - .catch(pushApiError); - }, - [fundService, organization.id, organization.name, envData.client_type, fileService, pushApiError], - ); - - return useCallback( - (detailed: boolean, year?: number) => { - openModal((modal) => ( - doExport(exportType, detailed, year)} /> - )); - }, - [doExport, openModal], - ); -} diff --git a/react/src/dashboard/components/pages/financial-dashboard/elements/ProviderFinancialTable.tsx b/react/src/dashboard/components/pages/financial-dashboard/elements/ProviderFinancialTable.tsx index de4dff124..192d2ae56 100644 --- a/react/src/dashboard/components/pages/financial-dashboard/elements/ProviderFinancialTable.tsx +++ b/react/src/dashboard/components/pages/financial-dashboard/elements/ProviderFinancialTable.tsx @@ -9,21 +9,21 @@ import { useOrganizationService } from '../../../../services/OrganizationService import LoadingCard from '../../../elements/loading-card/LoadingCard'; import { uniqueId } from 'lodash'; import { PaginationData } from '../../../../props/ApiResponses'; -import { format } from 'date-fns'; -import { useFileService } from '../../../../services/FileService'; import { FinancialFiltersQuery } from './FinancialFilters'; import { ProviderFinancial } from '../types/FinancialStatisticTypes'; import EmptyCard from '../../../elements/empty-card/EmptyCard'; import TableTopScroller from '../../../elements/tables/TableTopScroller'; import usePushApiError from '../../../../hooks/usePushApiError'; +import useProviderFinancialExporter from '../../../../services/exporters/useProviderFinancialExporter'; type ProviderFinancialLocal = ProviderFinancial & { id: string }; export default function ProviderFinancialTable({ externalFilters }: { externalFilters?: FinancialFiltersQuery }) { const pushApiError = usePushApiError(); + const activeOrganization = useActiveOrganization(); + const providerFinancialExporter = useProviderFinancialExporter(); - const fileService = useFileService(); const paginatorService = usePaginatorService(); const organizationService = useOrganizationService(); @@ -37,25 +37,11 @@ export default function ProviderFinancialTable({ externalFilters }: { externalFi }); const financeProvidersExport = useCallback(() => { - organizationService - .financeProvidersExport(activeOrganization.id, { ...externalFilters, ...filter.activeValues }) - .then((res) => { - const dateTime = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); - const fileName = `financial-dashboard_${activeOrganization.name}_${dateTime}.xls`; - const fileType = res.headers['content-type'] + ';charset=utf-8;'; - - fileService.downloadFile(fileName, res.data, fileType); - }) - .catch(pushApiError); - }, [ - organizationService, - activeOrganization.id, - activeOrganization.name, - externalFilters, - filter?.activeValues, - fileService, - pushApiError, - ]); + providerFinancialExporter.exportData(activeOrganization.id, { + ...externalFilters, + ...filter.activeValues, + }); + }, [activeOrganization.id, externalFilters, filter?.activeValues, providerFinancialExporter]); const toggleTransactionsTable = useCallback((id: string) => { setShowTransactions((list) => { @@ -98,7 +84,10 @@ export default function ProviderFinancialTable({ externalFilters }: { externalFi
- diff --git a/react/src/dashboard/components/pages/fund-requests-view/FundRequestsView.tsx b/react/src/dashboard/components/pages/fund-requests-view/FundRequestsView.tsx index 2f71f1ac2..8c811f501 100644 --- a/react/src/dashboard/components/pages/fund-requests-view/FundRequestsView.tsx +++ b/react/src/dashboard/components/pages/fund-requests-view/FundRequestsView.tsx @@ -25,8 +25,6 @@ import useEnvData from '../../../hooks/useEnvData'; import FundRequestRecordTabs from './elements/FundRequestRecordTabs'; import FundRequestPerson from './elements/FundRequestPerson'; import useTranslate from '../../../hooks/useTranslate'; -import EmailLog from '../../../props/models/EmailLog'; -import { useFileService } from '../../../services/FileService'; import usePushApiError from '../../../hooks/usePushApiError'; import classNames from 'classnames'; import ModalApproveFundRequest from '../../modals/ModalApproveFundRequest'; @@ -36,6 +34,7 @@ import KeyValueItem from '../../elements/key-value/KeyValueItem'; import TableRowActions from '../../elements/tables/TableRowActions'; import Icon from '../../../../../assets/forus-platform/resources/_platform-common/assets/img/fund-request-icon.svg'; import TableEmptyValue from '../../elements/table-empty-value/TableEmptyValue'; +import useEmailLogService from '../../../services/EmailLogService'; export default function FundRequestsView() { const authIdentity = useAuthIdentity(); @@ -49,7 +48,7 @@ export default function FundRequestsView() { const pushApiError = usePushApiError(); const setProgress = useSetProgress(); - const fileService = useFileService(); + const emailLogService = useEmailLogService(); const activeOrganization = useActiveOrganization(); const fundRequestService = useFundRequestValidatorService(); @@ -434,21 +433,12 @@ export default function FundRequestsView() { ); const fetchEmailLogs = useCallback( - (query = {}) => fundRequestService.emailLogs(activeOrganization.id, fundRequestMeta.id, query), - [activeOrganization?.id, fundRequestMeta?.id, fundRequestService], - ); - - const exportEmailLog = useCallback( - (emailLog: EmailLog) => { - fundRequestService - .emailLogExport(activeOrganization.id, fundRequestMeta.id, emailLog.id) - .then((res) => fileService.downloadFile(`email-log-${emailLog.id}.pdf`, res.data)) - .catch(pushApiError) - .finally(() => setProgress(100)); - - setProgress(0); - }, - [activeOrganization?.id, fileService, fundRequestMeta?.id, fundRequestService, pushApiError, setProgress], + (query = {}) => + emailLogService.list(activeOrganization.id, { + fund_request_id: fundRequestMeta.id, + ...query, + }), + [activeOrganization?.id, fundRequestMeta?.id, emailLogService], ); const deleteNote = useCallback( @@ -820,7 +810,7 @@ export default function FundRequestsView() { diff --git a/react/src/dashboard/components/pages/fund-requests/FundRequests.tsx b/react/src/dashboard/components/pages/fund-requests/FundRequests.tsx index a3e98f905..987520575 100644 --- a/react/src/dashboard/components/pages/fund-requests/FundRequests.tsx +++ b/react/src/dashboard/components/pages/fund-requests/FundRequests.tsx @@ -7,16 +7,11 @@ import FilterItemToggle from '../../elements/tables/elements/FilterItemToggle'; import SelectControl from '../../elements/select-control/SelectControl'; import { useEmployeeService } from '../../../services/EmployeeService'; import CardHeaderFilter from '../../elements/tables/elements/CardHeaderFilter'; -import { format } from 'date-fns'; import LoadingCard from '../../elements/loading-card/LoadingCard'; import 'react-datepicker/dist/react-datepicker.css'; import DatePickerControl from '../../elements/forms/controls/DatePickerControl'; import { PaginationData } from '../../../props/ApiResponses'; -import ModalExportTypeLegacy from '../../modals/ModalExportTypeLegacy'; -import { useFileService } from '../../../services/FileService'; -import useEnvData from '../../../hooks/useEnvData'; import useAppConfigs from '../../../hooks/useAppConfigs'; -import useOpenModal from '../../../hooks/useOpenModal'; import useActiveOrganization from '../../../hooks/useActiveOrganization'; import useSetProgress from '../../../hooks/useSetProgress'; import { dateFormat, dateParse } from '../../../helpers/dates'; @@ -26,24 +21,22 @@ import usePushApiError from '../../../hooks/usePushApiError'; import useFilterNext from '../../../modules/filter_next/useFilterNext'; import FundRequestsTable from './elements/FundRequestsTable'; import { NumberParam, StringParam } from 'use-query-params'; +import useFundRequestExporter from '../../../services/exporters/useFundRequestExporter'; export default function FundRequests() { - const envData = useEnvData(); - - const openModal = useOpenModal(); - const translate = useTranslate(); const appConfigs = useAppConfigs(); const activeOrganization = useActiveOrganization(); + const fundRequestExporter = useFundRequestExporter(); + const navigate = useNavigate(); + const translate = useTranslate(); const setProgress = useSetProgress(); const pushApiError = usePushApiError(); - const navigate = useNavigate(); const [loading, setLoading] = useState(false); const [employees, setEmployees] = useState(null); const [fundRequests, setFundRequests] = useState>(null); - const fileService = useFileService(); const employeeService = useEmployeeService(); const paginatorService = usePaginatorService(); const fundRequestService = useFundRequestValidatorService(); @@ -151,25 +144,11 @@ export default function FundRequests() { .finally(() => setProgress(100)); }, [setProgress, activeOrganization.id, employeeService, allEmployeesOption, pushApiError]); - const doExport = useCallback( - (exportType: string) => { - fundRequestService - .export(activeOrganization.id, { ...filterActiveValues, export_type: exportType }) - .then((res) => { - const dateTime = format(new Date(), 'yyyy-MM-dd HH:mm:ss'); - const fileType = res.headers['content-type'] + ';charset=utf-8;'; - const fileName = `${envData.client_type}_${activeOrganization.name}_fund-requests_${dateTime}.${exportType}`; - - fileService.downloadFile(fileName, res.data, fileType); - }) - .catch(pushApiError); - }, - [fundRequestService, activeOrganization, filterActiveValues, envData.client_type, fileService, pushApiError], - ); - const exportRequests = useCallback(() => { - openModal((modal) => doExport(exportType)} />); - }, [doExport, openModal]); + fundRequestExporter.exportData(activeOrganization.id, { + ...filterActiveValues, + }); + }, [activeOrganization.id, filterActiveValues, fundRequestExporter]); useEffect(() => { if (!appConfigs.organizations?.funds?.fund_requests) { @@ -188,7 +167,7 @@ export default function FundRequests() { } return ( -
+
{translate('validation_requests.header.title')} ({fundRequests.meta.total}) @@ -229,6 +208,7 @@ export default function FundRequests() { filterUpdate({ q: e.target.value })} placeholder={translate('validation_requests.labels.search')} @@ -297,6 +277,7 @@ export default function FundRequests() {