ui-soxo-bootstrap-core 2.6.59 → 2.6.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +8 -8
- package/.github/workflows/npm-publish.yml +41 -41
- package/.husky/pre-commit +11 -11
- package/.prettierrc.json +10 -10
- package/DEVELOPER_GUIDE.md +294 -294
- package/PUBLISHING.md +333 -333
- package/babel.config.js +2 -2
- package/core/components/component-loader/component-loader.js +125 -125
- package/core/components/component-wrapper/component-wrapper.js +121 -121
- package/core/components/external-window/DEVELOPER_GUIDE.md +705 -705
- package/core/components/external-window/external-window.js +236 -236
- package/core/components/external-window/external-window.test.js +80 -80
- package/core/components/extra-info/extra-info-details.js +155 -155
- package/core/components/extra-info/extra-info-details.scss +26 -26
- package/core/components/extra-info/extra-info.js +134 -134
- package/core/components/index.js +14 -14
- package/core/components/landing-api/landing-api.js +733 -733
- package/core/components/landing-api/landing-api.scss +41 -41
- package/core/components/license-management/license-alert.js +97 -97
- package/core/components/menu-template-api/menu-template-api.js +321 -321
- package/core/components/root-application-api/root-application-api.js +174 -174
- package/core/index.js +13 -13
- package/core/lib/Store.js +375 -375
- package/core/lib/components/application-bootstrap/application-bootstrap.js +115 -115
- package/core/lib/components/approval-form/approval-form.js +280 -280
- package/core/lib/components/approval-form/approval-form.scss +183 -183
- package/core/lib/components/approval-list/approval-list.js +143 -143
- package/core/lib/components/approval-list/approval-list.scss +2 -2
- package/core/lib/components/approval-list/components/request-card/request-card.js +42 -42
- package/core/lib/components/approval-list/components/request-card/request-card.scss +30 -30
- package/core/lib/components/camera/camera.js +230 -230
- package/core/lib/components/camera/camera.scss +86 -86
- package/core/lib/components/comment-block/comment-block.js +138 -138
- package/core/lib/components/comment-block/comment-block.scss +3 -3
- package/core/lib/components/confirm-modal/confirm-modal.js +82 -82
- package/core/lib/components/confirm-modal/confirm-modal.scss +2 -2
- package/core/lib/components/consent/consent.js +67 -67
- package/core/lib/components/consent/pdf-signature.js +299 -299
- package/core/lib/components/consent/signature-pad.js +227 -227
- package/core/lib/components/consent/signature-pad.scss +23 -23
- package/core/lib/components/file-upload/file-upload.js +133 -133
- package/core/lib/components/finger-print-reader/finger-print-reader.js +295 -295
- package/core/lib/components/finger-print-reader/finger-print-reader.scss +47 -47
- package/core/lib/components/finger-print-search/finger-print-search.js +200 -200
- package/core/lib/components/finger-print-search/finger-print-search.scss +47 -47
- package/core/lib/components/global-header/animations.js +18 -18
- package/core/lib/components/global-header/global-header.js +293 -293
- package/core/lib/components/global-header/global-header.scss +399 -399
- package/core/lib/components/header/generic-header.js +76 -76
- package/core/lib/components/header/generic-header.scss +99 -99
- package/core/lib/components/image-preview/image-preview.js +33 -33
- package/core/lib/components/image-wrapper/image-wrapper.js +108 -108
- package/core/lib/components/image-wrapper/image-wrapper.scss +12 -12
- package/core/lib/components/index.js +206 -206
- package/core/lib/components/landing/landing.js +403 -403
- package/core/lib/components/language-switcher/language-switcher.js +49 -49
- package/core/lib/components/menu-context/menu-context.js +69 -69
- package/core/lib/components/menu-template/menu-template.js +249 -249
- package/core/lib/components/menu-template/menu-template.scss +9 -9
- package/core/lib/components/modal-search/modal-search.js +153 -153
- package/core/lib/components/modal-search/modal-search.scss +78 -78
- package/core/lib/components/modal-wrapper/modal-manager.js +15 -15
- package/core/lib/components/modal-wrapper/modal-wrapper.js +108 -108
- package/core/lib/components/modal-wrapper/modal-wrapper.scss +13 -13
- package/core/lib/components/notice-board/notice-board.js +132 -132
- package/core/lib/components/notice-board/notice-board.scss +65 -65
- package/core/lib/components/page-container/page-container.js +55 -55
- package/core/lib/components/page-container/page-container.scss +8 -8
- package/core/lib/components/page-header/page-header.js +23 -23
- package/core/lib/components/page-header/page-header.scss +17 -17
- package/core/lib/components/pdf-viewer/pdf-viewer.js +56 -56
- package/core/lib/components/portlet-table/components/table-actions/table-actions.js +58 -58
- package/core/lib/components/portlet-table/components/table-actions/table-actions.scss +1 -1
- package/core/lib/components/portlet-table/components/table-data/table-data.js +106 -106
- package/core/lib/components/portlet-table/portlet-table.js +63 -63
- package/core/lib/components/portlet-table/portlet-table.scss +90 -90
- package/core/lib/components/progress-bar/progress-bar.js +58 -58
- package/core/lib/components/progress-bar/progress-bar.scss +15 -15
- package/core/lib/components/request-form/request-form.js +110 -110
- package/core/lib/components/root-application/root-application.js +70 -70
- package/core/lib/components/rupee/rupee.js +14 -14
- package/core/lib/components/script-input/script-input.js +169 -169
- package/core/lib/components/script-input/script-input.scss +8 -8
- package/core/lib/components/sidemenu/animations.js +51 -51
- package/core/lib/components/sidemenu/sidemenu.js +632 -632
- package/core/lib/components/sidemenu/sidemenu.scss +314 -314
- package/core/lib/components/spotlight-search/spotlight-search.component.js +635 -635
- package/core/lib/components/spotlight-search/spotlight-search.component.scss +78 -78
- package/core/lib/components/table-wrapper/table-wrapper.js +135 -135
- package/core/lib/components/table-wrapper/table-wrapper.scss +72 -72
- package/core/lib/components/ui_elements/Loader.js +12 -12
- package/core/lib/components/ui_elements/Notify.js +12 -12
- package/core/lib/components/ui_elements/PlaceHolder.js +33 -33
- package/core/lib/components/web-camera/web-camera.js +161 -161
- package/core/lib/components/web-camera/web-camera.scss +28 -28
- package/core/lib/core.md +9 -9
- package/core/lib/elements/Elements.md +2 -2
- package/core/lib/elements/basic/LoggedUserRedirect.js +21 -21
- package/core/lib/elements/basic/PrivateRoute.js +16 -16
- package/core/lib/elements/basic/button/Button.md +43 -43
- package/core/lib/elements/basic/button/button.js +170 -170
- package/core/lib/elements/basic/card/Card.md +15 -15
- package/core/lib/elements/basic/card/card.js +40 -40
- package/core/lib/elements/basic/card/card.scss +13 -13
- package/core/lib/elements/basic/checkbox/checkbox.js +23 -23
- package/core/lib/elements/basic/col/col.js +15 -15
- package/core/lib/elements/basic/copy-to-clipboard/Readme.md +40 -40
- package/core/lib/elements/basic/copy-to-clipboard/copy-to-clipboard.js +61 -61
- package/core/lib/elements/basic/country-phone-input/Readme.md +98 -98
- package/core/lib/elements/basic/country-phone-input/country-phone-input.js +81 -81
- package/core/lib/elements/basic/country-phone-input/phone-input.scss +75 -75
- package/core/lib/elements/basic/datepicker/datepicker.js +33 -33
- package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +203 -203
- package/core/lib/elements/basic/empty/empty.js +14 -14
- package/core/lib/elements/basic/fingerprint-protrected/fingerprint-protected.js +118 -118
- package/core/lib/elements/basic/fingerprint-protrected/fingerprint-protected.scss +10 -10
- package/core/lib/elements/basic/form/form.js +70 -70
- package/core/lib/elements/basic/form/form.scss +3 -3
- package/core/lib/elements/basic/image/image.js +45 -45
- package/core/lib/elements/basic/image/image.scss +17 -17
- package/core/lib/elements/basic/image/readme.md +26 -26
- package/core/lib/elements/basic/image-viewer/image-viewer.js +108 -108
- package/core/lib/elements/basic/image-viewer/image-viewer.scss +7 -7
- package/core/lib/elements/basic/input/input.js +81 -81
- package/core/lib/elements/basic/input/readme.md +77 -77
- package/core/lib/elements/basic/json-input/json-input.js +51 -51
- package/core/lib/elements/basic/menu-dashboard/menu-dashboard.js +216 -216
- package/core/lib/elements/basic/menu-dashboard/menu-dashboard.scss +28 -28
- package/core/lib/elements/basic/menu-tree/menu-tree.js +127 -127
- package/core/lib/elements/basic/modal/modal.js +64 -64
- package/core/lib/elements/basic/modal/readme.md +62 -62
- package/core/lib/elements/basic/popconfirm/popconfirm.js +17 -17
- package/core/lib/elements/basic/popover/popover.js +12 -12
- package/core/lib/elements/basic/radio/radio.js +18 -18
- package/core/lib/elements/basic/rangepicker/rangepicker.js +141 -141
- package/core/lib/elements/basic/rangepicker/rangepicker.scss +24 -24
- package/core/lib/elements/basic/rangepicker/readme.md +81 -81
- package/core/lib/elements/basic/reference-select/readme.md +18 -18
- package/core/lib/elements/basic/reference-select/reference-select.js +341 -341
- package/core/lib/elements/basic/row/row.js +15 -15
- package/core/lib/elements/basic/select/select.js +46 -46
- package/core/lib/elements/basic/select-box/readme.md +52 -52
- package/core/lib/elements/basic/select-box/select-box.js +63 -63
- package/core/lib/elements/basic/skeleton/readme.md +35 -35
- package/core/lib/elements/basic/skeleton/skeleton.js +35 -35
- package/core/lib/elements/basic/skeleton/skeleton.scss +53 -53
- package/core/lib/elements/basic/space/space.js +12 -12
- package/core/lib/elements/basic/switch/readme.md +29 -29
- package/core/lib/elements/basic/switch/switch.js +67 -67
- package/core/lib/elements/basic/tab/tab.js +14 -14
- package/core/lib/elements/basic/table/readme.md +8 -8
- package/core/lib/elements/basic/table/table.js +95 -95
- package/core/lib/elements/basic/tag/tag.js +63 -63
- package/core/lib/elements/basic/tag/tag.scss +2 -2
- package/core/lib/elements/basic/timeline/timeline.js +13 -13
- package/core/lib/elements/basic/title/readme.md +20 -20
- package/core/lib/elements/basic/title/title.js +37 -37
- package/core/lib/elements/basic/user-search/user-search.js +192 -192
- package/core/lib/elements/complex/barcode/barcode.js +27 -27
- package/core/lib/elements/complex/bargraph/bar-graph.js +262 -262
- package/core/lib/elements/complex/basic-table/basic-table.js +110 -110
- package/core/lib/elements/complex/basic-table/basic-table.scss +4 -4
- package/core/lib/elements/complex/date-display/date-display.js +37 -37
- package/core/lib/elements/complex/error-boundary/error-boundary.js +29 -29
- package/core/lib/elements/complex/google-location-input/map-container-library-load.js +92 -92
- package/core/lib/elements/complex/google-map/google-map.js +230 -230
- package/core/lib/elements/complex/google-map/google-map.scss +13 -13
- package/core/lib/elements/complex/line-graph/line-graph.js +108 -108
- package/core/lib/elements/complex/location-search-input/location-search-input.js +100 -100
- package/core/lib/elements/complex/pie-chart/pie-chart.js +202 -202
- package/core/lib/elements/complex/qr-code/qr-code.js +29 -29
- package/core/lib/elements/complex/qrscanner/qrscanner.js +57 -57
- package/core/lib/elements/complex/search-debounce/search-debounce.js +37 -37
- package/core/lib/elements/complex/statistic-card/dashboard-statistic-card.js +75 -75
- package/core/lib/elements/complex/statistic-card/statistic-card.js +28 -28
- package/core/lib/elements/index.js +226 -226
- package/core/lib/hooks/device-detect.js +25 -25
- package/core/lib/hooks/index.js +9 -9
- package/core/lib/hooks/use-location.js +33 -33
- package/core/lib/hooks/use-otp-timer.js +80 -80
- package/core/lib/hooks/use-window-size.js +34 -34
- package/core/lib/i18n.js +69 -69
- package/core/lib/index.js +106 -106
- package/core/lib/introduction.md +73 -73
- package/core/lib/js-styleguide.md +4112 -4112
- package/core/lib/models/actions/actions.js +127 -127
- package/core/lib/models/actions/components/action-detail/action-detail.js +190 -190
- package/core/lib/models/actions/components/custom-actions/custom-actions.js +185 -185
- package/core/lib/models/attachments/attachments.js +231 -231
- package/core/lib/models/base-loader.js +99 -99
- package/core/lib/models/base.js +716 -716
- package/core/lib/models/branches/branches.js +125 -125
- package/core/lib/models/checklists/checklists.js +114 -114
- package/core/lib/models/columns/columns.js +169 -169
- package/core/lib/models/columns/components/columns-add/columns-add.js +171 -171
- package/core/lib/models/comments/comments.js +213 -213
- package/core/lib/models/departments/departments.js +107 -107
- package/core/lib/models/financial-years/financial_years.js +127 -127
- package/core/lib/models/forms/components/form-creator/form-creator.js +667 -667
- package/core/lib/models/forms/components/form-creator/form-creator.scss +39 -39
- package/core/lib/models/forms/components/form-detail/form-detail.js +224 -224
- package/core/lib/models/forms/forms.js +121 -121
- package/core/lib/models/index.js +203 -203
- package/core/lib/models/invoice-numbers/invoice_numbers.js +204 -204
- package/core/lib/models/lookup-types/components/lookup-detail/lookup-detail.js +145 -145
- package/core/lib/models/lookup-types/lookup-types.js +113 -113
- package/core/lib/models/lookup-values/components/lookup-values-add/lookup-values-add.js +126 -126
- package/core/lib/models/lookup-values/lookup-values.js +107 -107
- package/core/lib/models/menu-roles/menu-roles.js +127 -127
- package/core/lib/models/menus/components/menu-add/menu-add.js +228 -228
- package/core/lib/models/menus/components/menu-detail/menu-detail.js +170 -170
- package/core/lib/models/menus/components/menu-list/menu-list.js +550 -550
- package/core/lib/models/menus/components/menu-list/menu-list.scss +5 -5
- package/core/lib/models/menus/components/menu-roles-add/menu-roles-add.js +183 -183
- package/core/lib/models/menus/menus.js +499 -499
- package/core/lib/models/models/components/model-detail/model-detail.js +137 -137
- package/core/lib/models/models/components/models.js +128 -128
- package/core/lib/models/modules/modules.js +204 -204
- package/core/lib/models/outbox/outbox.js +73 -73
- package/core/lib/models/pages/pages.js +107 -107
- package/core/lib/models/permissions/permissions.js +71 -71
- package/core/lib/models/process/components/process-add/process-add.js +181 -181
- package/core/lib/models/process/components/process-dashboard/process-dashboard.js +1068 -1068
- package/core/lib/models/process/components/process-dashboard/process-dashboard.scss +66 -66
- package/core/lib/models/process/components/process-detail/process-detail.js +140 -140
- package/core/lib/models/process/components/process-timeline/process-timeline.js +139 -139
- package/core/lib/models/process/components/task-detail/task-detail.js +240 -240
- package/core/lib/models/process/components/task-detail/task-detail.scss +27 -27
- package/core/lib/models/process/components/task-form/task-form.js +528 -528
- package/core/lib/models/process/components/task-form/task-form.scss +7 -7
- package/core/lib/models/process/components/task-list/task-list.js +221 -221
- package/core/lib/models/process/components/task-list/task-list.scss +14 -14
- package/core/lib/models/process/components/task-overview/task-overview.js +299 -299
- package/core/lib/models/process/components/task-overview-legacy/task-overview-legacy.js +192 -192
- package/core/lib/models/process/components/task-routes/task-routes.js +45 -45
- package/core/lib/models/process/components/task-status/task-status.js +175 -175
- package/core/lib/models/process/components/task-status/task-status.scss +11 -11
- package/core/lib/models/process/process.js +780 -780
- package/core/lib/models/process-transactions/process-transactions.js +123 -123
- package/core/lib/models/roles/roles.js +106 -106
- package/core/lib/models/scripts/scripts.js +111 -111
- package/core/lib/models/step-transactions/step-transcations.js +147 -147
- package/core/lib/models/steps/components/step-add/step-add.js +261 -261
- package/core/lib/models/steps/components/step-detail/step-detail.js +157 -157
- package/core/lib/models/steps/steps.js +356 -356
- package/core/lib/models/user-preferences/user-preferences.js +83 -83
- package/core/lib/models/users/components/user-add/user-add.js +226 -226
- package/core/lib/models/users/users.js +119 -119
- package/core/lib/modules/business/launch-page/launch-page.js +29 -29
- package/core/lib/modules/business/launch-page/launch-page.scss +5 -5
- package/core/lib/modules/business/slots/slots.js +231 -231
- package/core/lib/modules/business/slots/slots.scss +108 -108
- package/core/lib/modules/forms/components/field-customizer/field-customizer.js +138 -138
- package/core/lib/modules/forms/components/field-selector/field-selector.js +157 -157
- package/core/lib/modules/forms/components/field-selector/field-selector.scss +25 -25
- package/core/lib/modules/forms/components/form-display/form-display.js +203 -203
- package/core/lib/modules/forms/components/form-display/form-display.scss +9 -9
- package/core/lib/modules/forms/components/tab-customizer/tab-customizer.js +124 -124
- package/core/lib/modules/generic/generic-add/generic-add.js +213 -213
- package/core/lib/modules/generic/generic-detail/generic-detail.js +199 -199
- package/core/lib/modules/generic/generic-edit/generic-edit.js +120 -120
- package/core/lib/modules/generic/generic-list/ExportReactCSV.js +508 -508
- package/core/lib/modules/generic/generic-list/fonts/NotoSansBold.js +8 -8
- package/core/lib/modules/generic/generic-list/fonts/NotoSansRegular.js +15 -15
- package/core/lib/modules/generic/generic-list/generic-list.js +705 -705
- package/core/lib/modules/generic/generic-list/generic-list.scss +68 -68
- package/core/lib/modules/generic/generic-upload/generic-upload.js +483 -483
- package/core/lib/modules/generic/table-settings/table-settings.js +226 -226
- package/core/lib/modules/generic/table-settings/table-settings.scss +37 -37
- package/core/lib/modules/index.js +52 -52
- package/core/lib/modules/modules-routes/module-routes.js +35 -35
- package/core/lib/pages/change-password/change-password.js +204 -204
- package/core/lib/pages/change-password/change-password.scss +73 -73
- package/core/lib/pages/homepage/homepage.js +53 -53
- package/core/lib/pages/index.js +19 -19
- package/core/lib/pages/login/commnication-mode-selection.js +46 -46
- package/core/lib/pages/login/communication-mode-selection.scss +60 -60
- package/core/lib/pages/login/login.js +872 -872
- package/core/lib/pages/login/login.scss +353 -353
- package/core/lib/pages/login/reset-password.js +124 -124
- package/core/lib/pages/login/reset-password.scss +31 -31
- package/core/lib/pages/manage-users/manage-users.js +429 -429
- package/core/lib/pages/manage-users/manage-users.scss +25 -25
- package/core/lib/pages/profile/profile.js +247 -247
- package/core/lib/pages/profile/profile.scss +107 -107
- package/core/lib/pages/profile/theme-config.js +18 -18
- package/core/lib/pages/profile/themes-backup.json +310 -310
- package/core/lib/pages/profile/themes.json +254 -254
- package/core/lib/pages/register/register.js +176 -176
- package/core/lib/pages/register/register.scss +128 -128
- package/core/lib/react-styleguide.md +756 -756
- package/core/lib/utils/api/api.utils.js +207 -207
- package/core/lib/utils/api/readme.md +426 -426
- package/core/lib/utils/async.js +35 -35
- package/core/lib/utils/common/common.utils.js +237 -237
- package/core/lib/utils/common/readme.md +30 -30
- package/core/lib/utils/date/date.utils.js +295 -295
- package/core/lib/utils/date/readme.md +2 -2
- package/core/lib/utils/firebase.support.utils.js +98 -98
- package/core/lib/utils/firebase.utils.js +808 -808
- package/core/lib/utils/font-awesome.utils.js +168 -168
- package/core/lib/utils/form/form.utils.js +255 -255
- package/core/lib/utils/generic/generic.utils.js +70 -70
- package/core/lib/utils/http/auth.helper.js +95 -95
- package/core/lib/utils/http/http.utils.js +190 -190
- package/core/lib/utils/http/readme.md +14 -14
- package/core/lib/utils/index.js +43 -43
- package/core/lib/utils/location/location.utils.js +137 -137
- package/core/lib/utils/location/readme.md +18 -18
- package/core/lib/utils/modal.utils.js +15 -15
- package/core/lib/utils/notification.utils.js +34 -34
- package/core/lib/utils/pwa/pwa.utils.js +88 -88
- package/core/lib/utils/script.utils.js +235 -235
- package/core/lib/utils/setting.utils.js +68 -68
- package/core/lib/utils/upload.utils.js +29 -29
- package/core/models/Preference/Preferences.js +46 -46
- package/core/models/base/base.js +403 -403
- package/core/models/base-clone-loader.js +107 -107
- package/core/models/base-clone.js +187 -187
- package/core/models/base-loader.js +97 -97
- package/core/models/core-scripts/core-scripts.js +179 -179
- package/core/models/dashboard/dashboard.js +201 -201
- package/core/models/detail-loader.js +88 -88
- package/core/models/doctor/components/doctor-add/doctor-add.js +432 -432
- package/core/models/doctor/components/doctor-add/doctor-add.scss +32 -32
- package/core/models/groups.js +82 -82
- package/core/models/index.js +100 -100
- package/core/models/lookup-types/components/lookup-detail/lookup-detail.js +129 -129
- package/core/models/lookup-types/lookup-types.js +96 -96
- package/core/models/lookup-values/components/lookup-values-modal/lookup-values-modal.js +95 -95
- package/core/models/lookup-values/lookup-values.js +92 -92
- package/core/models/menu-roles/components/menu-roles-add/menu-roles-add.js +153 -153
- package/core/models/menu-roles/menu-roles.js +158 -158
- package/core/models/menus/components/menu-add/menu-add.js +315 -315
- package/core/models/menus/components/menu-add/menu-add.scss +31 -31
- package/core/models/menus/components/menu-detail/menu-detail.js +263 -263
- package/core/models/menus/components/menu-list/menu-list.js +392 -392
- package/core/models/menus/components/menu-lists/menu-lists.js +647 -647
- package/core/models/menus/components/menu-lists/menu-lists.scss +73 -73
- package/core/models/menus/menus.js +338 -338
- package/core/models/model-columns.js +121 -121
- package/core/models/models/components/model-detail/model-add.js +120 -120
- package/core/models/models/components/model-detail/model-detail.js +133 -133
- package/core/models/models/models.js +154 -154
- package/core/models/pages/components/page-add/page-add.js +163 -163
- package/core/models/pages/components/page-add/page-add.scss +30 -30
- package/core/models/pages/components/page-details/page-details.js +209 -209
- package/core/models/pages/components/page-list/page-list.js +248 -248
- package/core/models/pages/pages.js +142 -142
- package/core/models/pages.js +142 -142
- package/core/models/roles/components/role-add/menu-label.js +14 -14
- package/core/models/roles/components/role-add/menu-tree.js +127 -127
- package/core/models/roles/components/role-add/role-add.js +230 -230
- package/core/models/roles/components/role-add/role-add.scss +4 -4
- package/core/models/roles/components/role-list/role-list.js +406 -406
- package/core/models/roles/roles.js +199 -199
- package/core/models/staff/components/staff-add/staff-add.js +455 -455
- package/core/models/user-roles/components/user-roles-add/user-roles-add.js +149 -149
- package/core/models/user-roles/user-roles.js +113 -113
- package/core/models/users/components/assign-role/assign-role.js +428 -428
- package/core/models/users/components/assign-role/assign-role.scss +281 -281
- package/core/models/users/components/assign-role/avatar-props.js +45 -45
- package/core/models/users/components/user-add/user-add.js +847 -847
- package/core/models/users/components/user-add/user-edit.js +110 -110
- package/core/models/users/components/user-detail/user-detail.js +236 -236
- package/core/models/users/components/user-list/user-list.js +397 -397
- package/core/models/users/users.js +379 -379
- package/core/modules/Informations/change-info/change-info.js +618 -618
- package/core/modules/Informations/change-info/change-info.scss +134 -134
- package/core/modules/dashboard/components/dashboard-card/animations.js +64 -64
- package/core/modules/dashboard/components/dashboard-card/dashboard-card.js +197 -197
- package/core/modules/dashboard/components/dashboard-card/menu-dashboard-card.js +430 -430
- package/core/modules/dashboard/components/dashboard-card/menu-dashboard-card.scss +59 -59
- package/core/modules/dashboard/components/pop-query-dashboard/pop-query-dashboard.js +66 -66
- package/core/modules/generic/components/generic-add/generic-add.js +121 -121
- package/core/modules/generic/components/generic-add/generic-add.scss +13 -13
- package/core/modules/generic/components/generic-add-modal/generic-add-modal.js +125 -125
- package/core/modules/generic/components/generic-add-modal/generic-add-modal.scss +13 -13
- package/core/modules/generic/components/generic-detail/generic-detail.js +184 -184
- package/core/modules/generic/components/generic-detail/generic-detail.scss +25 -25
- package/core/modules/generic/components/generic-edit/generic-edit.js +123 -123
- package/core/modules/generic/components/generic-list/generic-list.js +335 -335
- package/core/modules/generic/components/generic-list/generic-list.scss +35 -35
- package/core/modules/index.js +42 -42
- package/core/modules/module-routes/module-routes.js +37 -37
- package/core/modules/reporting/components/index.js +6 -6
- package/core/modules/reporting/components/reporting-dashboard/README.md +316 -316
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +271 -271
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss +76 -76
- package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +90 -90
- package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.test.js +74 -74
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +404 -404
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +179 -179
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +652 -652
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +215 -215
- package/core/modules/reporting/components/reporting-dashboard/reporting-table.js +537 -537
- package/core/modules/steps/action-buttons.js +92 -92
- package/core/modules/steps/action-buttons.scss +62 -62
- package/core/modules/steps/chat-assistant.js +141 -141
- package/core/modules/steps/narration.js +192 -192
- package/core/modules/steps/openai-realtime.js +275 -275
- package/core/modules/steps/progress-storage.js +140 -140
- package/core/modules/steps/readme.md +167 -167
- package/core/modules/steps/steps.js +1716 -1597
- package/core/modules/steps/steps.scss +1068 -1051
- package/core/modules/steps/timeline.js +56 -56
- package/core/modules/steps/voice-navigation.js +709 -709
- package/core/pages/homepage-api/homepage-api.js +106 -106
- package/core/pages/homepage-api/homepage-api.scss +233 -233
- package/core/pages/homepage-api/menu-dashboard.js +169 -169
- package/core/pages/homepage-api/menu-dashboard.scss +11 -11
- package/core/translation.json +53 -53
- package/core/translations.json +19 -19
- package/core/utils/script.utils.js +129 -129
- package/core/utils/settings.utils.js +25 -25
- package/eslint.config.mjs +79 -79
- package/index.js +35 -35
- package/jest.config.js +7 -7
- package/jest.setup.js +1 -1
- package/package.json +125 -125
- package/tsconfig.json +26 -26
- package/webpack.config.js +173 -173
|
@@ -1,508 +1,508 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exports the provided data as an `.xlsx` Excel file using the `xlsx` and `file-saver` libraries.
|
|
3
|
-
*
|
|
4
|
-
* Props:
|
|
5
|
-
* @param {Array<Object>} csvData - The data array to export.
|
|
6
|
-
* @param {Array<{ label: string, key: string }>} [headers] - Optional column headers and field mappings.
|
|
7
|
-
* @param {string} [fileName='Report.xlsx'] - Optional name for the downloaded Excel file.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import React, { useState } from 'react';
|
|
11
|
-
// Import XLSX library to handle Excel file creation
|
|
12
|
-
import * as XLSX from 'xlsx';
|
|
13
|
-
import { PDFDocument, rgb } from 'pdf-lib';
|
|
14
|
-
import fontkit from '@pdf-lib/fontkit';
|
|
15
|
-
// import * as XLSX from 'xlsx-js-style';
|
|
16
|
-
// import * as XLSX from 'xlsx-js-style';
|
|
17
|
-
// Import saveAs from file-saver to trigger file download in browser
|
|
18
|
-
import { saveAs } from 'file-saver';
|
|
19
|
-
import { Dropdown, Form, Input, Menu, Modal, Radio, message } from 'antd';
|
|
20
|
-
import { DownOutlined, FileExcelFilled, FilePdfFilled, MailFilled, DownloadOutlined } from '@ant-design/icons';
|
|
21
|
-
import { Trans } from 'react-i18next';
|
|
22
|
-
import { CoreScripts } from '../../../../models';
|
|
23
|
-
import './generic-list.scss';
|
|
24
|
-
import { Button } from '../../../elements';
|
|
25
|
-
import { NotoSansRegularBase64 } from './fonts/NotoSansRegular';
|
|
26
|
-
import { NotoSansBoldBase64 } from './fonts/NotoSansBold';
|
|
27
|
-
|
|
28
|
-
// pdf-lib's built-in StandardFonts (Helvetica) only support WinAnsi encoding,
|
|
29
|
-
// which cannot render Vietnamese diacritics (e.g. "à", "ầ") and throws
|
|
30
|
-
// "WinAnsi cannot encode ..." when a report contains them. Noto Sans is
|
|
31
|
-
// embedded instead via fontkit so PDF export supports Vietnamese text.
|
|
32
|
-
// The base64 strings are decoded once and cached, since every PDF export
|
|
33
|
-
// would otherwise re-decode the same ~120KB font twice.
|
|
34
|
-
let cachedRegularFontBytes;
|
|
35
|
-
let cachedBoldFontBytes;
|
|
36
|
-
|
|
37
|
-
const base64ToUint8Array = (base64) => {
|
|
38
|
-
const binaryString = atob(base64);
|
|
39
|
-
const bytes = new Uint8Array(binaryString.length);
|
|
40
|
-
for (let i = 0; i < binaryString.length; i += 1) {
|
|
41
|
-
bytes[i] = binaryString.charCodeAt(i);
|
|
42
|
-
}
|
|
43
|
-
return bytes;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const getRegularFontBytes = () => {
|
|
47
|
-
if (!cachedRegularFontBytes) cachedRegularFontBytes = base64ToUint8Array(NotoSansRegularBase64);
|
|
48
|
-
return cachedRegularFontBytes;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const getBoldFontBytes = () => {
|
|
52
|
-
if (!cachedBoldFontBytes) cachedBoldFontBytes = base64ToUint8Array(NotoSansBoldBase64);
|
|
53
|
-
return cachedBoldFontBytes;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const normalizeScriptId = (value) => {
|
|
57
|
-
if (value === undefined || value === null || value === '') return value;
|
|
58
|
-
|
|
59
|
-
const numberValue = Number(value);
|
|
60
|
-
return Number.isNaN(numberValue) ? value : numberValue;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const getRecipients = (value) => {
|
|
64
|
-
if (Array.isArray(value)) return value.filter(Boolean);
|
|
65
|
-
|
|
66
|
-
return String(value || '')
|
|
67
|
-
.split(/[\n,;]+/)
|
|
68
|
-
.map((email) => email.trim())
|
|
69
|
-
.filter(Boolean);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
export const ExportReactCSV = ({
|
|
74
|
-
csvData,
|
|
75
|
-
headers,
|
|
76
|
-
fileName,
|
|
77
|
-
pdfFileName,
|
|
78
|
-
title,
|
|
79
|
-
format = 'excel',
|
|
80
|
-
buttonText,
|
|
81
|
-
dropdown = false,
|
|
82
|
-
onSendReportToMail,
|
|
83
|
-
scriptId,
|
|
84
|
-
inputParameters,
|
|
85
|
-
}) => {
|
|
86
|
-
const [mailModalVisible, setMailModalVisible] = useState(false);
|
|
87
|
-
const [sendingMail, setSendingMail] = useState(false);
|
|
88
|
-
const [selectedFormat, setSelectedFormat] = useState('excel');
|
|
89
|
-
const [mailForm] = Form.useForm();
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* exportToExcel function
|
|
93
|
-
* Triggered on button click, generates and downloads an Excel file
|
|
94
|
-
*/
|
|
95
|
-
|
|
96
|
-
const exportToExcel = (downloadFileName) => {
|
|
97
|
-
// if (!csvData?.length) return;
|
|
98
|
-
|
|
99
|
-
let worksheet;
|
|
100
|
-
// If headers are provided, use them to create the sheet with custom columns
|
|
101
|
-
if (headers?.length) {
|
|
102
|
-
// Extract label names for the header row
|
|
103
|
-
const headerLabels = headers.map((h) => h.label);
|
|
104
|
-
// Map data rows according to the headers' key order
|
|
105
|
-
const rows = csvData.map((row) =>
|
|
106
|
-
headers.map((h, hIndex) => {
|
|
107
|
-
let value = row[h.key];
|
|
108
|
-
// If it's a summary row, return a cell object with bold styling
|
|
109
|
-
if (row.isSummaryRow) {
|
|
110
|
-
// Remove the index value (typically the first column) for the summary row
|
|
111
|
-
if (hIndex === 0) value = "";
|
|
112
|
-
|
|
113
|
-
return {
|
|
114
|
-
v: value,
|
|
115
|
-
s: {
|
|
116
|
-
font: { bold: true },
|
|
117
|
-
// "Thick" visual indicator via border
|
|
118
|
-
// Visual indicator via border
|
|
119
|
-
border: { bottom: { style: "thick", color: { rgb: "000000" } } }
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
return value;
|
|
124
|
-
})
|
|
125
|
-
);
|
|
126
|
-
// Create a worksheet from an array of arrays (header + rows)
|
|
127
|
-
const data = [[title || "Report"], [], headerLabels, ...rows];
|
|
128
|
-
// worksheet = XLSX.utils.aoa_to_sheet([headerLabels, ...rows]);
|
|
129
|
-
worksheet = XLSX.utils.aoa_to_sheet(data);
|
|
130
|
-
} else {
|
|
131
|
-
// If no headers, convert JSON data directly to a worksheet
|
|
132
|
-
worksheet = XLSX.utils.json_to_sheet(csvData);
|
|
133
|
-
}
|
|
134
|
-
// Create a new workbook
|
|
135
|
-
const workbook = XLSX.utils.book_new();
|
|
136
|
-
// Append the worksheet to the workbook with the name 'Sheet1'
|
|
137
|
-
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
|
138
|
-
// Write workbook to a buffer as an array
|
|
139
|
-
const excelBuffer = XLSX.write(workbook, {
|
|
140
|
-
bookType: 'xlsx',
|
|
141
|
-
type: 'array',
|
|
142
|
-
cellStyles: true, // Required to export styles
|
|
143
|
-
});
|
|
144
|
-
// Create a Blob object from the buffer with correct MIME type
|
|
145
|
-
const blob = new Blob([excelBuffer], {
|
|
146
|
-
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
147
|
-
});
|
|
148
|
-
const flName = downloadFileName || fileName || 'Report.xlsx';
|
|
149
|
-
// Trigger download using file-saver
|
|
150
|
-
saveAs(blob, flName);
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const exportToPdf = async (downloadFileName) => {
|
|
154
|
-
const pdfDoc = await PDFDocument.create();
|
|
155
|
-
pdfDoc.registerFontkit(fontkit);
|
|
156
|
-
const font = await pdfDoc.embedFont(getRegularFontBytes());
|
|
157
|
-
const boldFont = await pdfDoc.embedFont(getBoldFontBytes());
|
|
158
|
-
const headersToUse = headers?.length
|
|
159
|
-
? headers
|
|
160
|
-
: Object.keys(csvData?.[0] || {}).map((key) => ({ label: key, key }));
|
|
161
|
-
const pageSize = headersToUse.length > 12 ? [1190, 842] : [842, 595];
|
|
162
|
-
const margin = 30;
|
|
163
|
-
const minRowHeight = 20;
|
|
164
|
-
const titleHeight = 38;
|
|
165
|
-
const fontSize = headersToUse.length > 12 ? 7 : 8;
|
|
166
|
-
const headerFontSize = headersToUse.length > 12 ? 7 : 8;
|
|
167
|
-
const lineHeight = fontSize + 2;
|
|
168
|
-
const headerLineHeight = headerFontSize + 2;
|
|
169
|
-
const cellPaddingX = 4;
|
|
170
|
-
const cellPaddingY = 5;
|
|
171
|
-
const availableWidth = pageSize[0] - margin * 2;
|
|
172
|
-
let page;
|
|
173
|
-
let y;
|
|
174
|
-
|
|
175
|
-
const sanitizeValue = (value) => {
|
|
176
|
-
if (value === undefined || value === null) return '';
|
|
177
|
-
if (typeof value === 'object') return JSON.stringify(value);
|
|
178
|
-
// Normalize all line-ending variants (CRLF/CR) to LF so a lone "\r"
|
|
179
|
-
// never reaches pdf-lib's WinAnsi encoder, which cannot encode it.
|
|
180
|
-
return String(value).replace(/\r\n|\r/g, '\n');
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const getTextWidth = (text, textFont, size) => {
|
|
184
|
-
const sanitized = sanitizeValue(text);
|
|
185
|
-
// widthOfTextAtSize measures a single line; strip any remaining
|
|
186
|
-
// newlines defensively so multi-line values never crash the encoder.
|
|
187
|
-
return textFont.widthOfTextAtSize(sanitized.replace(/\n/g, ' '), size);
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
const getMaxLineWidth = (value, textFont, size) =>
|
|
191
|
-
sanitizeValue(value)
|
|
192
|
-
.split('\n')
|
|
193
|
-
.reduce((maxWidth, line) => Math.max(maxWidth, textFont.widthOfTextAtSize(line, size)), 0);
|
|
194
|
-
|
|
195
|
-
const splitLongWord = (word, maxWidth, textFont, size) => {
|
|
196
|
-
const characters = Array.from(word);
|
|
197
|
-
const chunks = [];
|
|
198
|
-
let chunk = '';
|
|
199
|
-
|
|
200
|
-
characters.forEach((character) => {
|
|
201
|
-
const nextChunk = `${chunk}${character}`;
|
|
202
|
-
|
|
203
|
-
if (chunk && getTextWidth(nextChunk, textFont, size) > maxWidth) {
|
|
204
|
-
chunks.push(chunk);
|
|
205
|
-
chunk = character;
|
|
206
|
-
} else {
|
|
207
|
-
chunk = nextChunk;
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
if (chunk) chunks.push(chunk);
|
|
212
|
-
return chunks;
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
const wrapText = (value, maxWidth, textFont, size) => {
|
|
216
|
-
const text = sanitizeValue(value);
|
|
217
|
-
const usableWidth = Math.max(maxWidth, 1);
|
|
218
|
-
const paragraphs = text.split(/\r?\n/);
|
|
219
|
-
|
|
220
|
-
return paragraphs.flatMap((paragraph) => {
|
|
221
|
-
const words = paragraph.split(/\s+/).filter(Boolean);
|
|
222
|
-
const lines = [];
|
|
223
|
-
let line = '';
|
|
224
|
-
|
|
225
|
-
if (!words.length) return [''];
|
|
226
|
-
|
|
227
|
-
words.forEach((word) => {
|
|
228
|
-
if (getTextWidth(word, textFont, size) > usableWidth) {
|
|
229
|
-
if (line) {
|
|
230
|
-
lines.push(line);
|
|
231
|
-
line = '';
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const chunks = splitLongWord(word, usableWidth, textFont, size);
|
|
235
|
-
chunks.forEach((chunk, chunkIndex) => {
|
|
236
|
-
if (chunkIndex === chunks.length - 1) {
|
|
237
|
-
line = chunk;
|
|
238
|
-
} else {
|
|
239
|
-
lines.push(chunk);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const nextLine = line ? `${line} ${word}` : word;
|
|
247
|
-
if (getTextWidth(nextLine, textFont, size) <= usableWidth) {
|
|
248
|
-
line = nextLine;
|
|
249
|
-
} else {
|
|
250
|
-
if (line) lines.push(line);
|
|
251
|
-
line = word;
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
if (line) lines.push(line);
|
|
256
|
-
return lines;
|
|
257
|
-
});
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const getColumnWidths = () => {
|
|
261
|
-
if (!headersToUse.length) return [availableWidth];
|
|
262
|
-
|
|
263
|
-
const sampleRows = (csvData || []).slice(0, 100);
|
|
264
|
-
const minWidths = [];
|
|
265
|
-
const preferredWidths = headersToUse.map((header, index) => {
|
|
266
|
-
const label = sanitizeValue(header.label);
|
|
267
|
-
const compactColumn = label === '#' || ['slno', 'sl no'].includes(label.toLowerCase());
|
|
268
|
-
const minWidth = compactColumn ? 28 : 36;
|
|
269
|
-
const values = [label, ...sampleRows.map((row) => sanitizeValue(row[header.key]))];
|
|
270
|
-
const longestValueWidth = values.reduce((maxWidth, value) => Math.max(maxWidth, getMaxLineWidth(value, font, fontSize)), 0);
|
|
271
|
-
const longestWordWidth = values.reduce((maxWidth, value) => {
|
|
272
|
-
const words = value.split(/\s+/).filter(Boolean);
|
|
273
|
-
return words.reduce((wordMaxWidth, word) => Math.max(wordMaxWidth, getTextWidth(word, font, fontSize)), maxWidth);
|
|
274
|
-
}, 0);
|
|
275
|
-
const headerWidth = getTextWidth(label, boldFont, headerFontSize);
|
|
276
|
-
const preferredWidth = Math.max(
|
|
277
|
-
minWidth,
|
|
278
|
-
Math.min(longestValueWidth + cellPaddingX * 2, compactColumn ? 48 : 130),
|
|
279
|
-
Math.min(longestWordWidth + cellPaddingX * 2, compactColumn ? 48 : 95),
|
|
280
|
-
headerWidth + cellPaddingX * 2
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
minWidths[index] = minWidth;
|
|
284
|
-
return preferredWidth;
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
const minTotal = minWidths.reduce((total, width) => total + width, 0);
|
|
288
|
-
const preferredTotal = preferredWidths.reduce((total, width) => total + width, 0);
|
|
289
|
-
|
|
290
|
-
if (preferredTotal <= availableWidth) {
|
|
291
|
-
const extraWidth = availableWidth - preferredTotal;
|
|
292
|
-
return preferredWidths.map((width) => width + (extraWidth * width) / preferredTotal);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (minTotal >= availableWidth) {
|
|
296
|
-
return minWidths.map((width) => (width * availableWidth) / minTotal);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const flexibleTotal = preferredWidths.reduce((total, width, index) => total + (width - minWidths[index]), 0);
|
|
300
|
-
const extraWidth = availableWidth - minTotal;
|
|
301
|
-
|
|
302
|
-
return preferredWidths.map((width, index) => {
|
|
303
|
-
const flexibleWidth = width - minWidths[index];
|
|
304
|
-
return minWidths[index] + (extraWidth * flexibleWidth) / flexibleTotal;
|
|
305
|
-
});
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
const columnWidths = getColumnWidths();
|
|
309
|
-
|
|
310
|
-
const getMaxLineCount = (cellLines) => Math.max(1, ...cellLines.map((lines) => lines.length));
|
|
311
|
-
|
|
312
|
-
const getRowHeight = (cellLines, rowLineHeight) =>
|
|
313
|
-
Math.max(minRowHeight, getMaxLineCount(cellLines) * rowLineHeight + cellPaddingY * 2);
|
|
314
|
-
|
|
315
|
-
const drawTableRow = (cellLines, rowHeight, options) => {
|
|
316
|
-
let x = margin;
|
|
317
|
-
|
|
318
|
-
cellLines.forEach((lines, index) => {
|
|
319
|
-
const width = columnWidths[index] || availableWidth;
|
|
320
|
-
page.drawRectangle({
|
|
321
|
-
x,
|
|
322
|
-
y: y - rowHeight,
|
|
323
|
-
width,
|
|
324
|
-
height: rowHeight,
|
|
325
|
-
color: options.backgroundColor,
|
|
326
|
-
borderColor: options.borderColor,
|
|
327
|
-
borderWidth: 0.5,
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
lines.forEach((line, lineIndex) => {
|
|
331
|
-
page.drawText(line, {
|
|
332
|
-
x: x + cellPaddingX,
|
|
333
|
-
y: y - cellPaddingY - options.fontSize - lineIndex * options.lineHeight,
|
|
334
|
-
size: options.fontSize,
|
|
335
|
-
font: options.font,
|
|
336
|
-
color: options.textColor,
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
x += width;
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
y -= rowHeight;
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
const headerCellLines = headersToUse.map((header, index) =>
|
|
347
|
-
wrapText(header.label, columnWidths[index] - cellPaddingX * 2, boldFont, headerFontSize)
|
|
348
|
-
);
|
|
349
|
-
const headerRowHeight = getRowHeight(headerCellLines, headerLineHeight);
|
|
350
|
-
|
|
351
|
-
const drawHeader = () => {
|
|
352
|
-
drawTableRow(headerCellLines, headerRowHeight, {
|
|
353
|
-
backgroundColor: rgb(0.92, 0.94, 0.96),
|
|
354
|
-
borderColor: rgb(0.72, 0.75, 0.78),
|
|
355
|
-
font: boldFont,
|
|
356
|
-
fontSize: headerFontSize,
|
|
357
|
-
lineHeight: headerLineHeight,
|
|
358
|
-
textColor: rgb(0.1, 0.1, 0.1),
|
|
359
|
-
});
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
const addPage = () => {
|
|
363
|
-
page = pdfDoc.addPage(pageSize);
|
|
364
|
-
y = pageSize[1] - margin;
|
|
365
|
-
|
|
366
|
-
page.drawText(title || 'Report', {
|
|
367
|
-
x: margin,
|
|
368
|
-
y,
|
|
369
|
-
size: 14,
|
|
370
|
-
font: boldFont,
|
|
371
|
-
color: rgb(0.1, 0.1, 0.1),
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
y -= titleHeight;
|
|
375
|
-
drawHeader();
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
const drawDataRow = (row) => {
|
|
379
|
-
const rowFont = row.isSummaryRow ? boldFont : font;
|
|
380
|
-
const cellLines = headersToUse.map((header, index) =>
|
|
381
|
-
wrapText(row[header.key], columnWidths[index] - cellPaddingX * 2, rowFont, fontSize)
|
|
382
|
-
);
|
|
383
|
-
let remainingCellLines = cellLines.map((lines) => (lines.length ? lines : ['']));
|
|
384
|
-
const fullPageDataHeight = pageSize[1] - margin * 2 - titleHeight - headerRowHeight;
|
|
385
|
-
|
|
386
|
-
while (remainingCellLines.some((lines) => lines.length)) {
|
|
387
|
-
const rowHeight = getRowHeight(remainingCellLines, lineHeight);
|
|
388
|
-
const availableHeight = y - margin;
|
|
389
|
-
|
|
390
|
-
if (rowHeight > availableHeight && rowHeight <= fullPageDataHeight) {
|
|
391
|
-
addPage();
|
|
392
|
-
continue;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
const maxLinesOnPage =
|
|
396
|
-
rowHeight > availableHeight
|
|
397
|
-
? Math.max(1, Math.floor((availableHeight - cellPaddingY * 2) / lineHeight))
|
|
398
|
-
: getMaxLineCount(remainingCellLines);
|
|
399
|
-
const segmentCellLines = remainingCellLines.map((lines) => lines.slice(0, maxLinesOnPage));
|
|
400
|
-
const segmentHeight = getRowHeight(segmentCellLines, lineHeight);
|
|
401
|
-
|
|
402
|
-
if (segmentHeight > availableHeight && availableHeight < fullPageDataHeight) {
|
|
403
|
-
addPage();
|
|
404
|
-
continue;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
drawTableRow(segmentCellLines, segmentHeight, {
|
|
408
|
-
backgroundColor: row.isSummaryRow ? rgb(0.97, 0.97, 0.97) : rgb(1, 1, 1),
|
|
409
|
-
borderColor: rgb(0.82, 0.84, 0.86),
|
|
410
|
-
font: rowFont,
|
|
411
|
-
fontSize,
|
|
412
|
-
lineHeight,
|
|
413
|
-
textColor: rgb(0.12, 0.12, 0.12),
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
remainingCellLines = remainingCellLines.map((lines) => lines.slice(maxLinesOnPage));
|
|
417
|
-
if (remainingCellLines.some((lines) => lines.length)) addPage();
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
addPage();
|
|
422
|
-
|
|
423
|
-
(csvData || []).forEach((row) => {
|
|
424
|
-
drawDataRow(row);
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
const pdfBytes = await pdfDoc.save();
|
|
428
|
-
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
|
429
|
-
const flName = downloadFileName || pdfFileName || fileName || 'Report.pdf';
|
|
430
|
-
saveAs(blob, flName);
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
const handleDownloadMenuClick = ({ key }) => {
|
|
434
|
-
if (key === 'excel') {
|
|
435
|
-
exportToExcel(fileName);
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if (key === 'pdf') {
|
|
440
|
-
exportToPdf(pdfFileName);
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (key === 'mail') {
|
|
445
|
-
setMailModalVisible(true);
|
|
446
|
-
}
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
const handleMailModalClose = () => {
|
|
450
|
-
setMailModalVisible(false);
|
|
451
|
-
mailForm.resetFields();
|
|
452
|
-
setSelectedFormat('excel');
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
const handleFormatToggle = (value) => {
|
|
456
|
-
const nextValue = selectedFormat === value ? null : value;
|
|
457
|
-
setSelectedFormat(nextValue);
|
|
458
|
-
mailForm.setFieldsValue({ format: nextValue });
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const downloadMenu = (
|
|
463
|
-
<Menu className="export-download-menu" onClick={handleDownloadMenuClick}>
|
|
464
|
-
<Menu.Item
|
|
465
|
-
key="excel"
|
|
466
|
-
icon={<FileExcelFilled className="export-download-menu__icon export-download-menu__icon--excel" />}
|
|
467
|
-
>
|
|
468
|
-
Download Excel Sheet
|
|
469
|
-
</Menu.Item>
|
|
470
|
-
<Menu.Item
|
|
471
|
-
key="pdf"
|
|
472
|
-
icon={<FilePdfFilled className="export-download-menu__icon export-download-menu__icon--pdf" />}
|
|
473
|
-
>
|
|
474
|
-
Download PDF
|
|
475
|
-
</Menu.Item>
|
|
476
|
-
</Menu>
|
|
477
|
-
);
|
|
478
|
-
|
|
479
|
-
const isPdf = format === 'pdf';
|
|
480
|
-
const handleExportButtonClick = () => {
|
|
481
|
-
if (isPdf) {
|
|
482
|
-
exportToPdf();
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
exportToExcel();
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
if (dropdown) {
|
|
490
|
-
return (
|
|
491
|
-
<>
|
|
492
|
-
<Dropdown overlay={downloadMenu} trigger={['click']} placement="bottomRight">
|
|
493
|
-
<Button type="primary" size="small" className="export-download-button">
|
|
494
|
-
<Trans i18nKey={buttonText || 'Download'} /> <DownloadOutlined className="export-download-button__icon" />
|
|
495
|
-
</Button>
|
|
496
|
-
</Dropdown>
|
|
497
|
-
</>
|
|
498
|
-
|
|
499
|
-
);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Render an Ant Design Button that triggers the Excel export
|
|
503
|
-
return (
|
|
504
|
-
<Button type="secondary" size="small" onClick={handleExportButtonClick} className="export-download-button">
|
|
505
|
-
<Trans i18nKey={buttonText || (isPdf ? 'PDF Download' : 'Download')} /> <DownloadOutlined className="export-download-button__icon" />
|
|
506
|
-
</Button>
|
|
507
|
-
);
|
|
508
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Exports the provided data as an `.xlsx` Excel file using the `xlsx` and `file-saver` libraries.
|
|
3
|
+
*
|
|
4
|
+
* Props:
|
|
5
|
+
* @param {Array<Object>} csvData - The data array to export.
|
|
6
|
+
* @param {Array<{ label: string, key: string }>} [headers] - Optional column headers and field mappings.
|
|
7
|
+
* @param {string} [fileName='Report.xlsx'] - Optional name for the downloaded Excel file.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React, { useState } from 'react';
|
|
11
|
+
// Import XLSX library to handle Excel file creation
|
|
12
|
+
import * as XLSX from 'xlsx';
|
|
13
|
+
import { PDFDocument, rgb } from 'pdf-lib';
|
|
14
|
+
import fontkit from '@pdf-lib/fontkit';
|
|
15
|
+
// import * as XLSX from 'xlsx-js-style';
|
|
16
|
+
// import * as XLSX from 'xlsx-js-style';
|
|
17
|
+
// Import saveAs from file-saver to trigger file download in browser
|
|
18
|
+
import { saveAs } from 'file-saver';
|
|
19
|
+
import { Dropdown, Form, Input, Menu, Modal, Radio, message } from 'antd';
|
|
20
|
+
import { DownOutlined, FileExcelFilled, FilePdfFilled, MailFilled, DownloadOutlined } from '@ant-design/icons';
|
|
21
|
+
import { Trans } from 'react-i18next';
|
|
22
|
+
import { CoreScripts } from '../../../../models';
|
|
23
|
+
import './generic-list.scss';
|
|
24
|
+
import { Button } from '../../../elements';
|
|
25
|
+
import { NotoSansRegularBase64 } from './fonts/NotoSansRegular';
|
|
26
|
+
import { NotoSansBoldBase64 } from './fonts/NotoSansBold';
|
|
27
|
+
|
|
28
|
+
// pdf-lib's built-in StandardFonts (Helvetica) only support WinAnsi encoding,
|
|
29
|
+
// which cannot render Vietnamese diacritics (e.g. "à", "ầ") and throws
|
|
30
|
+
// "WinAnsi cannot encode ..." when a report contains them. Noto Sans is
|
|
31
|
+
// embedded instead via fontkit so PDF export supports Vietnamese text.
|
|
32
|
+
// The base64 strings are decoded once and cached, since every PDF export
|
|
33
|
+
// would otherwise re-decode the same ~120KB font twice.
|
|
34
|
+
let cachedRegularFontBytes;
|
|
35
|
+
let cachedBoldFontBytes;
|
|
36
|
+
|
|
37
|
+
const base64ToUint8Array = (base64) => {
|
|
38
|
+
const binaryString = atob(base64);
|
|
39
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
40
|
+
for (let i = 0; i < binaryString.length; i += 1) {
|
|
41
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
42
|
+
}
|
|
43
|
+
return bytes;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const getRegularFontBytes = () => {
|
|
47
|
+
if (!cachedRegularFontBytes) cachedRegularFontBytes = base64ToUint8Array(NotoSansRegularBase64);
|
|
48
|
+
return cachedRegularFontBytes;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getBoldFontBytes = () => {
|
|
52
|
+
if (!cachedBoldFontBytes) cachedBoldFontBytes = base64ToUint8Array(NotoSansBoldBase64);
|
|
53
|
+
return cachedBoldFontBytes;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const normalizeScriptId = (value) => {
|
|
57
|
+
if (value === undefined || value === null || value === '') return value;
|
|
58
|
+
|
|
59
|
+
const numberValue = Number(value);
|
|
60
|
+
return Number.isNaN(numberValue) ? value : numberValue;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const getRecipients = (value) => {
|
|
64
|
+
if (Array.isArray(value)) return value.filter(Boolean);
|
|
65
|
+
|
|
66
|
+
return String(value || '')
|
|
67
|
+
.split(/[\n,;]+/)
|
|
68
|
+
.map((email) => email.trim())
|
|
69
|
+
.filter(Boolean);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
export const ExportReactCSV = ({
|
|
74
|
+
csvData,
|
|
75
|
+
headers,
|
|
76
|
+
fileName,
|
|
77
|
+
pdfFileName,
|
|
78
|
+
title,
|
|
79
|
+
format = 'excel',
|
|
80
|
+
buttonText,
|
|
81
|
+
dropdown = false,
|
|
82
|
+
onSendReportToMail,
|
|
83
|
+
scriptId,
|
|
84
|
+
inputParameters,
|
|
85
|
+
}) => {
|
|
86
|
+
const [mailModalVisible, setMailModalVisible] = useState(false);
|
|
87
|
+
const [sendingMail, setSendingMail] = useState(false);
|
|
88
|
+
const [selectedFormat, setSelectedFormat] = useState('excel');
|
|
89
|
+
const [mailForm] = Form.useForm();
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* exportToExcel function
|
|
93
|
+
* Triggered on button click, generates and downloads an Excel file
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
const exportToExcel = (downloadFileName) => {
|
|
97
|
+
// if (!csvData?.length) return;
|
|
98
|
+
|
|
99
|
+
let worksheet;
|
|
100
|
+
// If headers are provided, use them to create the sheet with custom columns
|
|
101
|
+
if (headers?.length) {
|
|
102
|
+
// Extract label names for the header row
|
|
103
|
+
const headerLabels = headers.map((h) => h.label);
|
|
104
|
+
// Map data rows according to the headers' key order
|
|
105
|
+
const rows = csvData.map((row) =>
|
|
106
|
+
headers.map((h, hIndex) => {
|
|
107
|
+
let value = row[h.key];
|
|
108
|
+
// If it's a summary row, return a cell object with bold styling
|
|
109
|
+
if (row.isSummaryRow) {
|
|
110
|
+
// Remove the index value (typically the first column) for the summary row
|
|
111
|
+
if (hIndex === 0) value = "";
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
v: value,
|
|
115
|
+
s: {
|
|
116
|
+
font: { bold: true },
|
|
117
|
+
// "Thick" visual indicator via border
|
|
118
|
+
// Visual indicator via border
|
|
119
|
+
border: { bottom: { style: "thick", color: { rgb: "000000" } } }
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return value;
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
// Create a worksheet from an array of arrays (header + rows)
|
|
127
|
+
const data = [[title || "Report"], [], headerLabels, ...rows];
|
|
128
|
+
// worksheet = XLSX.utils.aoa_to_sheet([headerLabels, ...rows]);
|
|
129
|
+
worksheet = XLSX.utils.aoa_to_sheet(data);
|
|
130
|
+
} else {
|
|
131
|
+
// If no headers, convert JSON data directly to a worksheet
|
|
132
|
+
worksheet = XLSX.utils.json_to_sheet(csvData);
|
|
133
|
+
}
|
|
134
|
+
// Create a new workbook
|
|
135
|
+
const workbook = XLSX.utils.book_new();
|
|
136
|
+
// Append the worksheet to the workbook with the name 'Sheet1'
|
|
137
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
|
138
|
+
// Write workbook to a buffer as an array
|
|
139
|
+
const excelBuffer = XLSX.write(workbook, {
|
|
140
|
+
bookType: 'xlsx',
|
|
141
|
+
type: 'array',
|
|
142
|
+
cellStyles: true, // Required to export styles
|
|
143
|
+
});
|
|
144
|
+
// Create a Blob object from the buffer with correct MIME type
|
|
145
|
+
const blob = new Blob([excelBuffer], {
|
|
146
|
+
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
147
|
+
});
|
|
148
|
+
const flName = downloadFileName || fileName || 'Report.xlsx';
|
|
149
|
+
// Trigger download using file-saver
|
|
150
|
+
saveAs(blob, flName);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const exportToPdf = async (downloadFileName) => {
|
|
154
|
+
const pdfDoc = await PDFDocument.create();
|
|
155
|
+
pdfDoc.registerFontkit(fontkit);
|
|
156
|
+
const font = await pdfDoc.embedFont(getRegularFontBytes());
|
|
157
|
+
const boldFont = await pdfDoc.embedFont(getBoldFontBytes());
|
|
158
|
+
const headersToUse = headers?.length
|
|
159
|
+
? headers
|
|
160
|
+
: Object.keys(csvData?.[0] || {}).map((key) => ({ label: key, key }));
|
|
161
|
+
const pageSize = headersToUse.length > 12 ? [1190, 842] : [842, 595];
|
|
162
|
+
const margin = 30;
|
|
163
|
+
const minRowHeight = 20;
|
|
164
|
+
const titleHeight = 38;
|
|
165
|
+
const fontSize = headersToUse.length > 12 ? 7 : 8;
|
|
166
|
+
const headerFontSize = headersToUse.length > 12 ? 7 : 8;
|
|
167
|
+
const lineHeight = fontSize + 2;
|
|
168
|
+
const headerLineHeight = headerFontSize + 2;
|
|
169
|
+
const cellPaddingX = 4;
|
|
170
|
+
const cellPaddingY = 5;
|
|
171
|
+
const availableWidth = pageSize[0] - margin * 2;
|
|
172
|
+
let page;
|
|
173
|
+
let y;
|
|
174
|
+
|
|
175
|
+
const sanitizeValue = (value) => {
|
|
176
|
+
if (value === undefined || value === null) return '';
|
|
177
|
+
if (typeof value === 'object') return JSON.stringify(value);
|
|
178
|
+
// Normalize all line-ending variants (CRLF/CR) to LF so a lone "\r"
|
|
179
|
+
// never reaches pdf-lib's WinAnsi encoder, which cannot encode it.
|
|
180
|
+
return String(value).replace(/\r\n|\r/g, '\n');
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const getTextWidth = (text, textFont, size) => {
|
|
184
|
+
const sanitized = sanitizeValue(text);
|
|
185
|
+
// widthOfTextAtSize measures a single line; strip any remaining
|
|
186
|
+
// newlines defensively so multi-line values never crash the encoder.
|
|
187
|
+
return textFont.widthOfTextAtSize(sanitized.replace(/\n/g, ' '), size);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const getMaxLineWidth = (value, textFont, size) =>
|
|
191
|
+
sanitizeValue(value)
|
|
192
|
+
.split('\n')
|
|
193
|
+
.reduce((maxWidth, line) => Math.max(maxWidth, textFont.widthOfTextAtSize(line, size)), 0);
|
|
194
|
+
|
|
195
|
+
const splitLongWord = (word, maxWidth, textFont, size) => {
|
|
196
|
+
const characters = Array.from(word);
|
|
197
|
+
const chunks = [];
|
|
198
|
+
let chunk = '';
|
|
199
|
+
|
|
200
|
+
characters.forEach((character) => {
|
|
201
|
+
const nextChunk = `${chunk}${character}`;
|
|
202
|
+
|
|
203
|
+
if (chunk && getTextWidth(nextChunk, textFont, size) > maxWidth) {
|
|
204
|
+
chunks.push(chunk);
|
|
205
|
+
chunk = character;
|
|
206
|
+
} else {
|
|
207
|
+
chunk = nextChunk;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (chunk) chunks.push(chunk);
|
|
212
|
+
return chunks;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const wrapText = (value, maxWidth, textFont, size) => {
|
|
216
|
+
const text = sanitizeValue(value);
|
|
217
|
+
const usableWidth = Math.max(maxWidth, 1);
|
|
218
|
+
const paragraphs = text.split(/\r?\n/);
|
|
219
|
+
|
|
220
|
+
return paragraphs.flatMap((paragraph) => {
|
|
221
|
+
const words = paragraph.split(/\s+/).filter(Boolean);
|
|
222
|
+
const lines = [];
|
|
223
|
+
let line = '';
|
|
224
|
+
|
|
225
|
+
if (!words.length) return [''];
|
|
226
|
+
|
|
227
|
+
words.forEach((word) => {
|
|
228
|
+
if (getTextWidth(word, textFont, size) > usableWidth) {
|
|
229
|
+
if (line) {
|
|
230
|
+
lines.push(line);
|
|
231
|
+
line = '';
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const chunks = splitLongWord(word, usableWidth, textFont, size);
|
|
235
|
+
chunks.forEach((chunk, chunkIndex) => {
|
|
236
|
+
if (chunkIndex === chunks.length - 1) {
|
|
237
|
+
line = chunk;
|
|
238
|
+
} else {
|
|
239
|
+
lines.push(chunk);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const nextLine = line ? `${line} ${word}` : word;
|
|
247
|
+
if (getTextWidth(nextLine, textFont, size) <= usableWidth) {
|
|
248
|
+
line = nextLine;
|
|
249
|
+
} else {
|
|
250
|
+
if (line) lines.push(line);
|
|
251
|
+
line = word;
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
if (line) lines.push(line);
|
|
256
|
+
return lines;
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const getColumnWidths = () => {
|
|
261
|
+
if (!headersToUse.length) return [availableWidth];
|
|
262
|
+
|
|
263
|
+
const sampleRows = (csvData || []).slice(0, 100);
|
|
264
|
+
const minWidths = [];
|
|
265
|
+
const preferredWidths = headersToUse.map((header, index) => {
|
|
266
|
+
const label = sanitizeValue(header.label);
|
|
267
|
+
const compactColumn = label === '#' || ['slno', 'sl no'].includes(label.toLowerCase());
|
|
268
|
+
const minWidth = compactColumn ? 28 : 36;
|
|
269
|
+
const values = [label, ...sampleRows.map((row) => sanitizeValue(row[header.key]))];
|
|
270
|
+
const longestValueWidth = values.reduce((maxWidth, value) => Math.max(maxWidth, getMaxLineWidth(value, font, fontSize)), 0);
|
|
271
|
+
const longestWordWidth = values.reduce((maxWidth, value) => {
|
|
272
|
+
const words = value.split(/\s+/).filter(Boolean);
|
|
273
|
+
return words.reduce((wordMaxWidth, word) => Math.max(wordMaxWidth, getTextWidth(word, font, fontSize)), maxWidth);
|
|
274
|
+
}, 0);
|
|
275
|
+
const headerWidth = getTextWidth(label, boldFont, headerFontSize);
|
|
276
|
+
const preferredWidth = Math.max(
|
|
277
|
+
minWidth,
|
|
278
|
+
Math.min(longestValueWidth + cellPaddingX * 2, compactColumn ? 48 : 130),
|
|
279
|
+
Math.min(longestWordWidth + cellPaddingX * 2, compactColumn ? 48 : 95),
|
|
280
|
+
headerWidth + cellPaddingX * 2
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
minWidths[index] = minWidth;
|
|
284
|
+
return preferredWidth;
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const minTotal = minWidths.reduce((total, width) => total + width, 0);
|
|
288
|
+
const preferredTotal = preferredWidths.reduce((total, width) => total + width, 0);
|
|
289
|
+
|
|
290
|
+
if (preferredTotal <= availableWidth) {
|
|
291
|
+
const extraWidth = availableWidth - preferredTotal;
|
|
292
|
+
return preferredWidths.map((width) => width + (extraWidth * width) / preferredTotal);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (minTotal >= availableWidth) {
|
|
296
|
+
return minWidths.map((width) => (width * availableWidth) / minTotal);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const flexibleTotal = preferredWidths.reduce((total, width, index) => total + (width - minWidths[index]), 0);
|
|
300
|
+
const extraWidth = availableWidth - minTotal;
|
|
301
|
+
|
|
302
|
+
return preferredWidths.map((width, index) => {
|
|
303
|
+
const flexibleWidth = width - minWidths[index];
|
|
304
|
+
return minWidths[index] + (extraWidth * flexibleWidth) / flexibleTotal;
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const columnWidths = getColumnWidths();
|
|
309
|
+
|
|
310
|
+
const getMaxLineCount = (cellLines) => Math.max(1, ...cellLines.map((lines) => lines.length));
|
|
311
|
+
|
|
312
|
+
const getRowHeight = (cellLines, rowLineHeight) =>
|
|
313
|
+
Math.max(minRowHeight, getMaxLineCount(cellLines) * rowLineHeight + cellPaddingY * 2);
|
|
314
|
+
|
|
315
|
+
const drawTableRow = (cellLines, rowHeight, options) => {
|
|
316
|
+
let x = margin;
|
|
317
|
+
|
|
318
|
+
cellLines.forEach((lines, index) => {
|
|
319
|
+
const width = columnWidths[index] || availableWidth;
|
|
320
|
+
page.drawRectangle({
|
|
321
|
+
x,
|
|
322
|
+
y: y - rowHeight,
|
|
323
|
+
width,
|
|
324
|
+
height: rowHeight,
|
|
325
|
+
color: options.backgroundColor,
|
|
326
|
+
borderColor: options.borderColor,
|
|
327
|
+
borderWidth: 0.5,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
lines.forEach((line, lineIndex) => {
|
|
331
|
+
page.drawText(line, {
|
|
332
|
+
x: x + cellPaddingX,
|
|
333
|
+
y: y - cellPaddingY - options.fontSize - lineIndex * options.lineHeight,
|
|
334
|
+
size: options.fontSize,
|
|
335
|
+
font: options.font,
|
|
336
|
+
color: options.textColor,
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
x += width;
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
y -= rowHeight;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const headerCellLines = headersToUse.map((header, index) =>
|
|
347
|
+
wrapText(header.label, columnWidths[index] - cellPaddingX * 2, boldFont, headerFontSize)
|
|
348
|
+
);
|
|
349
|
+
const headerRowHeight = getRowHeight(headerCellLines, headerLineHeight);
|
|
350
|
+
|
|
351
|
+
const drawHeader = () => {
|
|
352
|
+
drawTableRow(headerCellLines, headerRowHeight, {
|
|
353
|
+
backgroundColor: rgb(0.92, 0.94, 0.96),
|
|
354
|
+
borderColor: rgb(0.72, 0.75, 0.78),
|
|
355
|
+
font: boldFont,
|
|
356
|
+
fontSize: headerFontSize,
|
|
357
|
+
lineHeight: headerLineHeight,
|
|
358
|
+
textColor: rgb(0.1, 0.1, 0.1),
|
|
359
|
+
});
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const addPage = () => {
|
|
363
|
+
page = pdfDoc.addPage(pageSize);
|
|
364
|
+
y = pageSize[1] - margin;
|
|
365
|
+
|
|
366
|
+
page.drawText(title || 'Report', {
|
|
367
|
+
x: margin,
|
|
368
|
+
y,
|
|
369
|
+
size: 14,
|
|
370
|
+
font: boldFont,
|
|
371
|
+
color: rgb(0.1, 0.1, 0.1),
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
y -= titleHeight;
|
|
375
|
+
drawHeader();
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const drawDataRow = (row) => {
|
|
379
|
+
const rowFont = row.isSummaryRow ? boldFont : font;
|
|
380
|
+
const cellLines = headersToUse.map((header, index) =>
|
|
381
|
+
wrapText(row[header.key], columnWidths[index] - cellPaddingX * 2, rowFont, fontSize)
|
|
382
|
+
);
|
|
383
|
+
let remainingCellLines = cellLines.map((lines) => (lines.length ? lines : ['']));
|
|
384
|
+
const fullPageDataHeight = pageSize[1] - margin * 2 - titleHeight - headerRowHeight;
|
|
385
|
+
|
|
386
|
+
while (remainingCellLines.some((lines) => lines.length)) {
|
|
387
|
+
const rowHeight = getRowHeight(remainingCellLines, lineHeight);
|
|
388
|
+
const availableHeight = y - margin;
|
|
389
|
+
|
|
390
|
+
if (rowHeight > availableHeight && rowHeight <= fullPageDataHeight) {
|
|
391
|
+
addPage();
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const maxLinesOnPage =
|
|
396
|
+
rowHeight > availableHeight
|
|
397
|
+
? Math.max(1, Math.floor((availableHeight - cellPaddingY * 2) / lineHeight))
|
|
398
|
+
: getMaxLineCount(remainingCellLines);
|
|
399
|
+
const segmentCellLines = remainingCellLines.map((lines) => lines.slice(0, maxLinesOnPage));
|
|
400
|
+
const segmentHeight = getRowHeight(segmentCellLines, lineHeight);
|
|
401
|
+
|
|
402
|
+
if (segmentHeight > availableHeight && availableHeight < fullPageDataHeight) {
|
|
403
|
+
addPage();
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
drawTableRow(segmentCellLines, segmentHeight, {
|
|
408
|
+
backgroundColor: row.isSummaryRow ? rgb(0.97, 0.97, 0.97) : rgb(1, 1, 1),
|
|
409
|
+
borderColor: rgb(0.82, 0.84, 0.86),
|
|
410
|
+
font: rowFont,
|
|
411
|
+
fontSize,
|
|
412
|
+
lineHeight,
|
|
413
|
+
textColor: rgb(0.12, 0.12, 0.12),
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
remainingCellLines = remainingCellLines.map((lines) => lines.slice(maxLinesOnPage));
|
|
417
|
+
if (remainingCellLines.some((lines) => lines.length)) addPage();
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
addPage();
|
|
422
|
+
|
|
423
|
+
(csvData || []).forEach((row) => {
|
|
424
|
+
drawDataRow(row);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const pdfBytes = await pdfDoc.save();
|
|
428
|
+
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
|
429
|
+
const flName = downloadFileName || pdfFileName || fileName || 'Report.pdf';
|
|
430
|
+
saveAs(blob, flName);
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const handleDownloadMenuClick = ({ key }) => {
|
|
434
|
+
if (key === 'excel') {
|
|
435
|
+
exportToExcel(fileName);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (key === 'pdf') {
|
|
440
|
+
exportToPdf(pdfFileName);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (key === 'mail') {
|
|
445
|
+
setMailModalVisible(true);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const handleMailModalClose = () => {
|
|
450
|
+
setMailModalVisible(false);
|
|
451
|
+
mailForm.resetFields();
|
|
452
|
+
setSelectedFormat('excel');
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const handleFormatToggle = (value) => {
|
|
456
|
+
const nextValue = selectedFormat === value ? null : value;
|
|
457
|
+
setSelectedFormat(nextValue);
|
|
458
|
+
mailForm.setFieldsValue({ format: nextValue });
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
const downloadMenu = (
|
|
463
|
+
<Menu className="export-download-menu" onClick={handleDownloadMenuClick}>
|
|
464
|
+
<Menu.Item
|
|
465
|
+
key="excel"
|
|
466
|
+
icon={<FileExcelFilled className="export-download-menu__icon export-download-menu__icon--excel" />}
|
|
467
|
+
>
|
|
468
|
+
Download Excel Sheet
|
|
469
|
+
</Menu.Item>
|
|
470
|
+
<Menu.Item
|
|
471
|
+
key="pdf"
|
|
472
|
+
icon={<FilePdfFilled className="export-download-menu__icon export-download-menu__icon--pdf" />}
|
|
473
|
+
>
|
|
474
|
+
Download PDF
|
|
475
|
+
</Menu.Item>
|
|
476
|
+
</Menu>
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
const isPdf = format === 'pdf';
|
|
480
|
+
const handleExportButtonClick = () => {
|
|
481
|
+
if (isPdf) {
|
|
482
|
+
exportToPdf();
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
exportToExcel();
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
if (dropdown) {
|
|
490
|
+
return (
|
|
491
|
+
<>
|
|
492
|
+
<Dropdown overlay={downloadMenu} trigger={['click']} placement="bottomRight">
|
|
493
|
+
<Button type="primary" size="small" className="export-download-button">
|
|
494
|
+
<Trans i18nKey={buttonText || 'Download'} /> <DownloadOutlined className="export-download-button__icon" />
|
|
495
|
+
</Button>
|
|
496
|
+
</Dropdown>
|
|
497
|
+
</>
|
|
498
|
+
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Render an Ant Design Button that triggers the Excel export
|
|
503
|
+
return (
|
|
504
|
+
<Button type="secondary" size="small" onClick={handleExportButtonClick} className="export-download-button">
|
|
505
|
+
<Trans i18nKey={buttonText || (isPdf ? 'PDF Download' : 'Download')} /> <DownloadOutlined className="export-download-button__icon" />
|
|
506
|
+
</Button>
|
|
507
|
+
);
|
|
508
|
+
};
|