@epcare/esm-report-builder 0.1.1-pre.76 → 0.1.1-pre.78
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/__mocks__/react-i18next.js +12 -0
- package/dist/729.js +1 -1
- package/dist/760.js +1 -0
- package/dist/760.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/openmrs-esm-report-builder.js.buildmanifest.json +26 -26
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/routes.json +3 -2
- package/src/components/admin/admin-page.component.tsx +53 -24
- package/src/components/admin/etl-browser/etl-browser.component.scss +10 -3
- package/src/components/admin/etl-browser/preview-grid.component.tsx +1 -1
- package/src/components/admin/etl-sources-page.component.tsx +30 -19
- package/src/components/admin/report-categories-page.component.tsx +32 -21
- package/src/components/admin/report-library-page.component.tsx +30 -19
- package/src/components/app-shell/report-builder-shell.component.tsx +194 -162
- package/src/components/app-shell/report-builder-shell.scss +4 -1
- package/src/components/dashboards/dashboards-page.component.tsx +13 -6
- package/src/components/dashboards/dashboards-table.component.tsx +24 -17
- package/src/components/data-themes/data-themes-page.component.tsx +11 -4
- package/src/components/data-themes/data-themes-table.component.tsx +10 -3
- package/src/components/data-visualizer/data-visualizer.resource.ts +49 -37
- package/src/components/etl-monitor/builder/etl-monitor-builder-page.component.tsx +6 -1
- package/src/components/etl-monitor/etl-monitors-page.component.tsx +35 -24
- package/src/components/import-export/import-export-page.component.tsx +38 -22
- package/src/components/indicators/indicators-page.component.tsx +41 -6
- package/src/components/indicators/indicators-table.component.tsx +24 -4
- package/src/components/landing/report-builder-landing-page.component.tsx +34 -1
- package/src/components/linelist/definition/linelist-definition-editor.component.tsx +14 -2
- package/src/components/linelist/linelist-dashboard.page.component.tsx +20 -13
- package/src/components/report/report-dashboard.page.component.tsx +14 -8
- package/src/components/report/report-editor.page.component.tsx +6 -1
- package/src/components/report-sections/report-sections-page.component.tsx +17 -5
- package/src/components/security/access-denied-page.component.tsx +69 -0
- package/src/components/security/access-denied-page.scss +39 -0
- package/src/components/security/privilege-route.component.test.tsx +79 -0
- package/src/components/security/privilege-route.component.tsx +39 -0
- package/src/constants/privileges.test.ts +53 -0
- package/src/constants/privileges.ts +118 -0
- package/src/hooks/use-report-builder-privileges.test.tsx +98 -0
- package/src/hooks/use-report-builder-privileges.ts +58 -0
- package/src/resources/etl-monitor/etl-monitor-test.api.ts +5 -2
- package/src/resources/etl-task/etl-task.api.ts +21 -13
- package/src/resources/legacy-reports/legacy-reports.api.ts +10 -22
- package/src/resources/openmrs-api.ts +30 -14
- package/src/resources/report-section/section-preview.api.ts +10 -6
- package/src/routes/report-builder.routes.tsx +93 -28
- package/src/routes.json +3 -2
- package/src/utils/api-error.utils.test.ts +151 -0
- package/src/utils/api-error.utils.ts +131 -0
- package/dist/359.js +0 -1
- package/dist/359.js.map +0 -1
package/dist/main.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var e,r,t,n,o={7729(e,r,t){"use strict";var n=t(2897),o={featureName:"report-builder",moduleName:(n.Type.String,n.Type.String,n.Type.String,n.Type.Array,n.Type.String,n.Type.Array,n.Type.String,"@epcare/esm-report-builder")};(0,n.getAsyncLifecycle)(function(){return Promise.all([t.e(469),t.e(656),t.e(705),t.e(710)]).then(t.bind(t,2710))},o),t(6738),(0,n.getAsyncLifecycle)(function(){return Promise.all([t.e(469),t.e(242),t.e(838),t.e(656),t.e(705),t.e(
|
|
1
|
+
(()=>{var e,r,t,n,o={7729(e,r,t){"use strict";var n=t(2897),o={featureName:"report-builder",moduleName:(n.Type.String,n.Type.String,n.Type.String,n.Type.Array,n.Type.String,n.Type.Array,n.Type.String,"@epcare/esm-report-builder")};(0,n.getAsyncLifecycle)(function(){return Promise.all([t.e(469),t.e(656),t.e(705),t.e(710)]).then(t.bind(t,2710))},o),t(6738),(0,n.getAsyncLifecycle)(function(){return Promise.all([t.e(469),t.e(242),t.e(838),t.e(656),t.e(705),t.e(760)]).then(t.bind(t,2760))},o),(0,n.getAsyncLifecycle)(function(){return Promise.all([t.e(469),t.e(656),t.e(705),t.e(669)]).then(t.bind(t,8669))},o)},6738(e,r,t){var n={"./en.json":[4300,[300]]};function o(e){try{if(!t.o(n,e))return Promise.resolve().then(()=>{var r=new Error("Cannot find module '"+e+"'");throw r.code="MODULE_NOT_FOUND",r})}catch(e){return Promise.reject(e)}var r=n[e],o=r[0];return t.e(r[1][0]).then(()=>t(o))}o.keys=()=>Object.keys(n),o.id=6738,e.exports=o}},a={};function i(e){var r=a[e];if(void 0!==r)return r.exports;var t=a[e]={id:e,exports:{}};return o[e].call(t.exports,t,t.exports,i),t.exports}i.m=o,i.c=a,i.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return i.d(r,{a:r}),r},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,i.t=function(t,n){if(1&n&&(t=this(t)),8&n)return t;if("object"==typeof t&&t){if(4&n&&t.__esModule)return t;if(16&n&&"function"==typeof t.then)return t}var o=Object.create(null);i.r(o);var a={};e=e||[null,r({}),r([]),r(r)];for(var u=2&n&&t;("object"==typeof u||"function"==typeof u)&&!~e.indexOf(u);u=r(u))Object.getOwnPropertyNames(u).forEach(e=>a[e]=()=>t[e]);return a.default=()=>t,i.d(o,a),o},i.d=(e,r)=>{for(var t in r)i.o(r,t)&&!i.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},i.f={},i.e=e=>Promise.all(Object.keys(i.f).reduce((r,t)=>(i.f[t](e,r),r),[])),i.u=e=>e+".js",i.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),i.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t={},n="@epcare/esm-report-builder:",i.l=(e,r,o,a)=>{if(t[e])t[e].push(r);else{var u,l;if(void 0!==o)for(var s=document.getElementsByTagName("script"),f=0;f<s.length;f++){var c=s[f];if(c.getAttribute("src")==e||c.getAttribute("data-webpack")==n+o){u=c;break}}u||(l=!0,(u=document.createElement("script")).charset="utf-8",i.nc&&u.setAttribute("nonce",i.nc),u.setAttribute("data-webpack",n+o),u.src=e),t[e]=[r];var p=(r,n)=>{u.onerror=u.onload=null,clearTimeout(d);var o=t[e];if(delete t[e],u.parentNode&&u.parentNode.removeChild(u),o&&o.forEach(e=>e(n)),r)return r(n)},d=setTimeout(p.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=p.bind(null,u.onerror),u.onload=p.bind(null,u.onload),l&&document.head.appendChild(u)}},i.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{i.S={};var e={},r={};i.I=(t,n)=>{n||(n=[]);var o=r[t];if(o||(o=r[t]={}),!(n.indexOf(o)>=0)){if(n.push(o),e[t])return e[t];i.o(i.S,t)||(i.S[t]={});var a=i.S[t],u="@epcare/esm-report-builder",l=(e,r,t,n)=>{var o=a[e]=a[e]||{},i=o[r];(!i||!i.loaded&&(!n!=!i.eager?n:u>i.from))&&(o[r]={get:t,from:u,eager:!!n})},s=[];return"default"===t&&(l("@openmrs/esm-framework","9.0.3-pre.4342",()=>Promise.all([i.e(469),i.e(478),i.e(242),i.e(162),i.e(656),i.e(705),i.e(446)]).then(()=>()=>i(2162))),l("react-dom","18.3.1",()=>Promise.all([i.e(961),i.e(656)]).then(()=>()=>i(961))),l("react-i18next","16.6.6",()=>Promise.all([i.e(478),i.e(311),i.e(656)]).then(()=>()=>i(3311))),l("react","18.3.1",()=>i.e(540).then(()=>()=>i(6540))),l("rxjs","6.6.7",()=>i.e(184).then(()=>()=>i(3184))),l("single-spa","5.9.5",()=>i.e(917).then(()=>()=>i(7917)))),e[t]=s.length?Promise.all(s).then(()=>e[t]=1):1}}})(),(()=>{var e;i.g.importScripts&&(e=i.g.location+"");var r=i.g.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var n=t.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=t[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),i.p=e})(),(()=>{var e=e=>{var r=e=>e.split(".").map(e=>+e==e?+e:e),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},r=e=>{var t=e[0],n="";if(1===e.length)return"*";if(t+.5){n+=0==t?">=":-1==t?"<":1==t?"^":2==t?"~":t>0?"=":"!=";for(var o=1,a=1;a<e.length;a++)o--,n+="u"==(typeof(u=e[a]))[0]?"-":(o>0?".":"")+(o=2,u);return n}var i=[];for(a=1;a<e.length;a++){var u=e[a];i.push(0===u?"not("+l()+")":1===u?"("+l()+" || "+l()+")":2===u?i.pop()+" "+i.pop():r(u))}return l();function l(){return i.pop().replace(/^\((.+)\)$/,"$1")}},t=(r,n)=>{if(0 in r){n=e(n);var o=r[0],a=o<0;a&&(o=-o-1);for(var i=0,u=1,l=!0;;u++,i++){var s,f,c=u<r.length?(typeof r[u])[0]:"";if(i>=n.length||"o"==(f=(typeof(s=n[i]))[0]))return!l||("u"==c?u>o&&!a:""==c!=a);if("u"==f){if(!l||"u"!=c)return!1}else if(l)if(c==f)if(u<=o){if(s!=r[u])return!1}else{if(a?s>r[u]:s<r[u])return!1;s!=r[u]&&(l=!1)}else if("s"!=c&&"n"!=c){if(a||u<=o)return!1;l=!1,u--}else{if(u<=o||f<c!=a)return!1;l=!1}else"s"!=c&&"n"!=c&&(l=!1,u--)}}var p=[],d=p.pop.bind(p);for(i=1;i<r.length;i++){var h=r[i];p.push(1==h?d()|d():2==h?d()&d():h?t(h,n):!d())}return!!d()},n=(r,t,n)=>{var o=n?(e=>Object.keys(e).reduce((r,t)=>(e[t].eager&&(r[t]=e[t]),r),{}))(r[t]):r[t];return Object.keys(o).reduce((r,t)=>!r||!o[r].loaded&&((r,t)=>{r=e(r),t=e(t);for(var n=0;;){if(n>=r.length)return n<t.length&&"u"!=(typeof t[n])[0];var o=r[n],a=(typeof o)[0];if(n>=t.length)return"u"==a;var i=t[n],u=(typeof i)[0];if(a!=u)return"o"==a&&"n"==u||"s"==u||"u"==a;if("o"!=a&&"u"!=a&&o!=i)return o<i;n++}})(r,t)?t:r,0)},o=(e,r,t)=>t?t():((e,r)=>(e=>{throw new Error(e)})("Shared module "+r+" doesn't exist in shared scope "+e))(e,r),a=(e=>function(r,t,n,o,a){var u=i.I(r);return u&&u.then&&!n?u.then(e.bind(e,r,i.S[r],t,!1,o,a)):e(r,i.S[r],t,n,o,a)})((e,a,u,l,s,f)=>{if(!((e,r)=>e&&i.o(e,r))(a,u))return o(e,u,f);var c,p,d=n(a,u,l);return t(s,d)||(p=((e,t,n,o)=>"Unsatisfied version "+n+" from "+(n&&e[t][n].from)+" of shared singleton module "+t+" (required "+r(o)+")")(a,u,d,s),"undefined"!=typeof console&&console.warn&&console.warn(p)),(c=a[u][d]).loaded=1,c.get()}),u={},l={2897:()=>a("default","@openmrs/esm-framework",!1,[1,8,0,0],()=>Promise.all([i.e(469),i.e(478),i.e(242),i.e(162),i.e(656),i.e(705),i.e(446)]).then(()=>()=>i(2162))),7656:()=>a("default","react",!1,[1,18,0,0],()=>i.e(540).then(()=>()=>i(6540))),1180:()=>a("default","react-i18next",!1,[1,16,0,0],()=>Promise.all([i.e(478),i.e(311)]).then(()=>()=>i(3311))),5686:()=>a("default","react-dom",!1,[1,18,0,0],()=>i.e(961).then(()=>()=>i(961))),7858:()=>a("default","rxjs",!1,[1,6,6,7],()=>i.e(184).then(()=>()=>i(3184))),8443:()=>a("default","single-spa",!1,[1,5,0,0],()=>i.e(917).then(()=>()=>i(7917)))};[2897].forEach(e=>{i.m[e]=r=>{u[e]=0,delete i.c[e];var t=l[e]();if("function"!=typeof t)throw new Error("Shared module is not available for eager consumption: "+e);r.exports=t()}});var s={446:[7858,8443],656:[7656],705:[1180,5686],792:[2897]},f={};i.f.consumes=(e,r)=>{i.o(s,e)&&s[e].forEach(e=>{if(i.o(u,e))return r.push(u[e]);if(!f[e]){var t=r=>{u[e]=0,i.m[e]=t=>{delete i.c[e],t.exports=r()}};f[e]=!0;var n=r=>{delete u[e],i.m[e]=t=>{throw delete i.c[e],r}};try{var o=l[e]();o.then?r.push(u[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}})}})(),(()=>{var e={729:0,792:0};i.f.j=(r,t)=>{var n=i.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(446|656|705)$/.test(r))e[r]=0;else{var o=new Promise((t,o)=>n=e[r]=[t,o]);t.push(n[2]=o);var a=i.p+i.u(r),u=new Error;i.l(a,t=>{if(i.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),a=t&&t.target&&t.target.src;u.message="Loading chunk "+r+" failed.\n("+o+": "+a+")",u.name="ChunkLoadError",u.type=o,u.request=a,n[1](u)}},"chunk-"+r,r)}};var r=(r,t)=>{var n,o,[a,u,l]=t,s=0;if(a.some(r=>0!==e[r])){for(n in u)i.o(u,n)&&(i.m[n]=u[n]);l&&l(i)}for(r&&r(t);s<a.length;s++)o=a[s],i.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=self.webpackChunk_epcare_esm_report_builder=self.webpackChunk_epcare_esm_report_builder||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),i.nc=void 0,i(7729)})();
|
|
@@ -130,30 +130,6 @@
|
|
|
130
130
|
"hash": "0e359dbd7feccb45",
|
|
131
131
|
"childrenByOrder": {}
|
|
132
132
|
},
|
|
133
|
-
{
|
|
134
|
-
"rendered": true,
|
|
135
|
-
"initial": false,
|
|
136
|
-
"entry": false,
|
|
137
|
-
"recorded": false,
|
|
138
|
-
"size": 3894038,
|
|
139
|
-
"sizes": {
|
|
140
|
-
"javascript": 3894038
|
|
141
|
-
},
|
|
142
|
-
"names": [],
|
|
143
|
-
"idHints": [],
|
|
144
|
-
"runtime": [
|
|
145
|
-
"@epcare/esm-report-builder",
|
|
146
|
-
"main"
|
|
147
|
-
],
|
|
148
|
-
"files": [
|
|
149
|
-
"359.js"
|
|
150
|
-
],
|
|
151
|
-
"auxiliaryFiles": [
|
|
152
|
-
"359.js.map"
|
|
153
|
-
],
|
|
154
|
-
"hash": "4349902a4919b40d",
|
|
155
|
-
"childrenByOrder": {}
|
|
156
|
-
},
|
|
157
133
|
{
|
|
158
134
|
"rendered": false,
|
|
159
135
|
"initial": false,
|
|
@@ -414,7 +390,31 @@
|
|
|
414
390
|
"auxiliaryFiles": [
|
|
415
391
|
"729.js.map"
|
|
416
392
|
],
|
|
417
|
-
"hash": "
|
|
393
|
+
"hash": "806243addf9e6fbd",
|
|
394
|
+
"childrenByOrder": {}
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
"rendered": true,
|
|
398
|
+
"initial": false,
|
|
399
|
+
"entry": false,
|
|
400
|
+
"recorded": false,
|
|
401
|
+
"size": 3975691,
|
|
402
|
+
"sizes": {
|
|
403
|
+
"javascript": 3975691
|
|
404
|
+
},
|
|
405
|
+
"names": [],
|
|
406
|
+
"idHints": [],
|
|
407
|
+
"runtime": [
|
|
408
|
+
"@epcare/esm-report-builder",
|
|
409
|
+
"main"
|
|
410
|
+
],
|
|
411
|
+
"files": [
|
|
412
|
+
"760.js"
|
|
413
|
+
],
|
|
414
|
+
"auxiliaryFiles": [
|
|
415
|
+
"760.js.map"
|
|
416
|
+
],
|
|
417
|
+
"hash": "91c8580ce54e48d8",
|
|
418
418
|
"childrenByOrder": {}
|
|
419
419
|
},
|
|
420
420
|
{
|
|
@@ -442,7 +442,7 @@
|
|
|
442
442
|
"auxiliaryFiles": [
|
|
443
443
|
"main.js.map"
|
|
444
444
|
],
|
|
445
|
-
"hash": "
|
|
445
|
+
"hash": "dd19761663af2d04",
|
|
446
446
|
"childrenByOrder": {}
|
|
447
447
|
},
|
|
448
448
|
{
|
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","pages":[{"component":"root","route":"report-builder","online":true,"offline":false,"order":1}],"extensions":[{"name":"system-administration-report-builder-card-link","slot":"system-admin-page-card-link-slot","component":"reportBuilderAdminLink","online":true,"offline":false},{"component":"confirmModal","name":"confirm-modal","online":true,"offline":true}],"version":"0.1.1-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","pages":[{"component":"root","route":"report-builder","online":true,"offline":false,"order":1}],"extensions":[{"name":"system-administration-report-builder-card-link","slot":"system-admin-page-card-link-slot","component":"reportBuilderAdminLink","online":true,"offline":false,"privileges":"Task: reportbuilder.report.view"},{"component":"confirmModal","name":"confirm-modal","online":true,"offline":true}],"version":"0.1.1-pre.78"}
|
package/package.json
CHANGED
package/routes.json
CHANGED
|
@@ -1,12 +1,49 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ClickableTile, Stack } from '@carbon/react';
|
|
2
|
+
import { ClickableTile, InlineNotification, Stack } from '@carbon/react';
|
|
3
3
|
import { useNavigate } from 'react-router-dom';
|
|
4
4
|
import Header from '../shared/header/header.component';
|
|
5
|
+
import { RB } from '../../constants/privileges';
|
|
6
|
+
import { useReportBuilderPrivileges } from '../../hooks/use-report-builder-privileges';
|
|
5
7
|
|
|
6
8
|
const tileStyle: React.CSSProperties = { minHeight: '10rem' };
|
|
7
9
|
|
|
8
10
|
export default function AdminPage() {
|
|
9
11
|
const navigate = useNavigate();
|
|
12
|
+
const { has } = useReportBuilderPrivileges();
|
|
13
|
+
|
|
14
|
+
const tiles = [
|
|
15
|
+
{
|
|
16
|
+
path: '/admin/report-library',
|
|
17
|
+
title: 'Report Library',
|
|
18
|
+
body: 'Catalog completed and legacy reports before full migration into the builder.',
|
|
19
|
+
required: [RB.LIBRARY_VIEW],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
path: '/admin/themes',
|
|
23
|
+
title: 'Data Themes',
|
|
24
|
+
body: 'Manage reusable source configurations and table mappings.',
|
|
25
|
+
required: [RB.THEME_VIEW],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
path: '/admin/etl-tasks',
|
|
29
|
+
title: 'ETL Tasks',
|
|
30
|
+
body: 'Configure and run scheduled ETL processes for data extraction and transformation.',
|
|
31
|
+
required: [RB.PACKAGE_IMPORT],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
path: '/admin/etl-browser',
|
|
35
|
+
title: 'ETL Data Browser',
|
|
36
|
+
body: 'Explore ETL schemas, tables, columns, and sample records.',
|
|
37
|
+
required: [RB.ETLSOURCE_VIEW],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
path: '/admin/dashboards',
|
|
41
|
+
title: 'Dashboards',
|
|
42
|
+
body: 'Configure dashboards composed of sections and widgets (ETL monitors, reports).',
|
|
43
|
+
required: [RB.DASHBOARD_VIEW],
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
const visibleTiles = tiles.filter((tile) => has(...tile.required));
|
|
10
47
|
|
|
11
48
|
return (
|
|
12
49
|
<Stack gap={5}>
|
|
@@ -16,30 +53,22 @@ export default function AdminPage() {
|
|
|
16
53
|
/>
|
|
17
54
|
|
|
18
55
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))', gap: '1rem' }}>
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<h4>Data Themes</h4>
|
|
26
|
-
<p>Manage reusable source configurations and table mappings.</p>
|
|
27
|
-
</ClickableTile>
|
|
28
|
-
|
|
29
|
-
<ClickableTile style={tileStyle} onClick={() => navigate('/admin/etl-tasks')}>
|
|
30
|
-
<h4>ETL Tasks</h4>
|
|
31
|
-
<p>Configure and run scheduled ETL processes for data extraction and transformation.</p>
|
|
32
|
-
</ClickableTile>
|
|
33
|
-
|
|
34
|
-
<ClickableTile style={tileStyle} onClick={() => navigate('/admin/etl-browser')}>
|
|
35
|
-
<h4>ETL Data Browser</h4>
|
|
36
|
-
<p>Explore ETL schemas, tables, columns, and sample records.</p>
|
|
37
|
-
</ClickableTile>
|
|
56
|
+
{visibleTiles.map((tile) => (
|
|
57
|
+
<ClickableTile key={tile.path} style={tileStyle} onClick={() => navigate(tile.path)}>
|
|
58
|
+
<h4>{tile.title}</h4>
|
|
59
|
+
<p>{tile.body}</p>
|
|
60
|
+
</ClickableTile>
|
|
61
|
+
))}
|
|
38
62
|
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
{visibleTiles.length === 0 && (
|
|
64
|
+
<InlineNotification
|
|
65
|
+
lowContrast
|
|
66
|
+
kind="info"
|
|
67
|
+
title="No access"
|
|
68
|
+
subtitle="You do not have access to any Report Builder administration features. Contact your administrator."
|
|
69
|
+
hideCloseButton
|
|
70
|
+
/>
|
|
71
|
+
)}
|
|
43
72
|
</div>
|
|
44
73
|
</Stack>
|
|
45
74
|
);
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
/* Bounded height so the side panes scroll internally instead of stretching the page */
|
|
8
8
|
height: calc(100vh - 16rem);
|
|
9
9
|
min-height: 30rem;
|
|
10
|
+
/* Flex item of the page Stack: without an explicit floor reset, min-content width
|
|
11
|
+
(driven by wide preview tables) would inflate this row and the shell with it */
|
|
12
|
+
min-width: 0;
|
|
13
|
+
max-width: 100%;
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
/* Width-controlled wrapper for the draggable/collapsible panes */
|
|
@@ -267,8 +271,9 @@
|
|
|
267
271
|
flex: 1;
|
|
268
272
|
min-height: 0;
|
|
269
273
|
padding: 0.75rem 1rem 1rem;
|
|
270
|
-
/*
|
|
271
|
-
overflow: auto;
|
|
274
|
+
/* Vertical scroll only — horizontal overflow belongs to .gridScroll below */
|
|
275
|
+
overflow-y: auto;
|
|
276
|
+
overflow-x: hidden;
|
|
272
277
|
}
|
|
273
278
|
|
|
274
279
|
/* Toolbar (preview + tabs) */
|
|
@@ -297,9 +302,11 @@
|
|
|
297
302
|
|
|
298
303
|
/* Preview grid — the ONLY element that scrolls horizontally */
|
|
299
304
|
.gridScroll {
|
|
305
|
+
width: 100%;
|
|
300
306
|
max-width: 100%;
|
|
301
307
|
max-height: min(60vh, 34rem);
|
|
302
|
-
overflow: auto;
|
|
308
|
+
overflow-x: auto;
|
|
309
|
+
overflow-y: auto;
|
|
303
310
|
border: 1px solid var(--cds-border-subtle, #dcdcdc);
|
|
304
311
|
|
|
305
312
|
/* Keep columns at natural width so wide tables scroll inside this container */
|
|
@@ -89,7 +89,7 @@ export default function PreviewGrid({ table, data, loading, error, onRefresh }:
|
|
|
89
89
|
</Button>
|
|
90
90
|
</div>
|
|
91
91
|
<p className={styles.truncateNote}>
|
|
92
|
-
Preview needs the "
|
|
92
|
+
Preview needs the "sql.execute" privilege and a valid table name.
|
|
93
93
|
</p>
|
|
94
94
|
</div>
|
|
95
95
|
);
|
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
} from '@carbon/react';
|
|
23
23
|
import { Add, Edit, TrashCan } from '@carbon/icons-react';
|
|
24
24
|
import Header from '../shared/header/header.component';
|
|
25
|
+
import { RB } from '../../constants/privileges';
|
|
26
|
+
import { useReportBuilderPrivileges } from '../../hooks/use-report-builder-privileges';
|
|
25
27
|
import {
|
|
26
28
|
createETLSource,
|
|
27
29
|
deleteETLSource,
|
|
@@ -61,6 +63,9 @@ const emptyForm: FormState = {
|
|
|
61
63
|
};
|
|
62
64
|
|
|
63
65
|
export default function ETLSourcesPage() {
|
|
66
|
+
const { has: hasPrivilege } = useReportBuilderPrivileges();
|
|
67
|
+
const canEditSource = hasPrivilege(RB.ETLSOURCE_ADD, RB.ETLSOURCE_EDIT);
|
|
68
|
+
const canDeleteSource = hasPrivilege(RB.ETLSOURCE_PURGE);
|
|
64
69
|
const [q, setQ] = React.useState('');
|
|
65
70
|
const [rows, setRows] = React.useState<ETLSourceDto[]>([]);
|
|
66
71
|
const [loading, setLoading] = React.useState(false);
|
|
@@ -176,9 +181,11 @@ export default function ETLSourcesPage() {
|
|
|
176
181
|
title="ETL Sources"
|
|
177
182
|
subtitle="Manage ETL source definitions used by the report builder."
|
|
178
183
|
actions={
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
canEditSource ? (
|
|
185
|
+
<Button data-testid="etl-source-new" size="sm" renderIcon={Add} onClick={openCreate}>
|
|
186
|
+
New ETL Source
|
|
187
|
+
</Button>
|
|
188
|
+
) : undefined
|
|
182
189
|
}
|
|
183
190
|
/>
|
|
184
191
|
|
|
@@ -228,22 +235,26 @@ export default function ETLSourcesPage() {
|
|
|
228
235
|
</TableCell>
|
|
229
236
|
<TableCell>
|
|
230
237
|
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
238
|
+
{canEditSource && (
|
|
239
|
+
<Button
|
|
240
|
+
kind="ghost"
|
|
241
|
+
size="sm"
|
|
242
|
+
renderIcon={Edit}
|
|
243
|
+
iconDescription="Edit"
|
|
244
|
+
hasIconOnly
|
|
245
|
+
onClick={() => original && openEdit(original)}
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
248
|
+
{canDeleteSource && (
|
|
249
|
+
<Button
|
|
250
|
+
kind="ghost"
|
|
251
|
+
size="sm"
|
|
252
|
+
renderIcon={TrashCan}
|
|
253
|
+
iconDescription="Retire"
|
|
254
|
+
hasIconOnly
|
|
255
|
+
onClick={() => original && onDelete(original)}
|
|
256
|
+
/>
|
|
257
|
+
)}
|
|
247
258
|
</div>
|
|
248
259
|
</TableCell>
|
|
249
260
|
</TableRow>
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
} from '@carbon/react';
|
|
22
22
|
import { Add, Edit, TrashCan } from '@carbon/icons-react';
|
|
23
23
|
import Header from '../shared/header/header.component';
|
|
24
|
+
import { RB } from '../../constants/privileges';
|
|
25
|
+
import { useReportBuilderPrivileges } from '../../hooks/use-report-builder-privileges';
|
|
24
26
|
import {
|
|
25
27
|
createReportCategory,
|
|
26
28
|
deleteReportCategory,
|
|
@@ -44,6 +46,9 @@ const headers: DataTableHeader[] = [
|
|
|
44
46
|
const emptyForm: FormState = { name: '', description: '' };
|
|
45
47
|
|
|
46
48
|
const ReportCategoriesPage: React.FC = () => {
|
|
49
|
+
const { has: hasPrivilege } = useReportBuilderPrivileges();
|
|
50
|
+
const canAddCategory = hasPrivilege(RB.CATEGORY_ADD, RB.CATEGORY_EDIT);
|
|
51
|
+
const canDeleteCategory = hasPrivilege(RB.CATEGORY_PURGE);
|
|
47
52
|
const [q, setQ] = React.useState('');
|
|
48
53
|
const [rows, setRows] = React.useState<ReportCategoryDto[]>([]);
|
|
49
54
|
const [loading, setLoading] = React.useState(false);
|
|
@@ -142,9 +147,11 @@ const ReportCategoriesPage: React.FC = () => {
|
|
|
142
147
|
title="Report Categories"
|
|
143
148
|
subtitle="Manage shared categories used to classify reports across the builder."
|
|
144
149
|
actions={
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
150
|
+
canAddCategory ? (
|
|
151
|
+
<Button size="sm" renderIcon={Add} onClick={openCreate}>
|
|
152
|
+
New Category
|
|
153
|
+
</Button>
|
|
154
|
+
) : undefined
|
|
148
155
|
}
|
|
149
156
|
/>
|
|
150
157
|
|
|
@@ -189,24 +196,28 @@ const ReportCategoriesPage: React.FC = () => {
|
|
|
189
196
|
</TableCell>
|
|
190
197
|
<TableCell>
|
|
191
198
|
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
199
|
+
{canAddCategory && (
|
|
200
|
+
<Button
|
|
201
|
+
kind="ghost"
|
|
202
|
+
size="sm"
|
|
203
|
+
renderIcon={Edit}
|
|
204
|
+
iconDescription="Edit"
|
|
205
|
+
onClick={() => source && openEdit(source)}
|
|
206
|
+
>
|
|
207
|
+
Edit
|
|
208
|
+
</Button>
|
|
209
|
+
)}
|
|
210
|
+
{canDeleteCategory && (
|
|
211
|
+
<Button
|
|
212
|
+
kind="ghost"
|
|
213
|
+
size="sm"
|
|
214
|
+
renderIcon={TrashCan}
|
|
215
|
+
iconDescription="Retire"
|
|
216
|
+
onClick={() => source && onDelete(source)}
|
|
217
|
+
>
|
|
218
|
+
Retire
|
|
219
|
+
</Button>
|
|
220
|
+
)}
|
|
210
221
|
</div>
|
|
211
222
|
</TableCell>
|
|
212
223
|
</TableRow>
|
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
} from '@carbon/react';
|
|
25
25
|
import { Add, Edit, TrashCan } from '@carbon/icons-react';
|
|
26
26
|
import Header from '../shared/header/header.component';
|
|
27
|
+
import { RB } from '../../constants/privileges';
|
|
28
|
+
import { useReportBuilderPrivileges } from '../../hooks/use-report-builder-privileges';
|
|
27
29
|
import {
|
|
28
30
|
createReportLibrary,
|
|
29
31
|
deleteReportLibrary,
|
|
@@ -91,6 +93,9 @@ function categoryDisplayOf(value: ReportLibraryDto['category']): string {
|
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
export default function ReportLibraryPage() {
|
|
96
|
+
const { has: hasPrivilege } = useReportBuilderPrivileges();
|
|
97
|
+
const canAddLibrary = hasPrivilege(RB.LIBRARY_ADD, RB.LIBRARY_EDIT);
|
|
98
|
+
const canDeleteLibrary = hasPrivilege(RB.LIBRARY_PURGE);
|
|
94
99
|
const [q, setQ] = React.useState('');
|
|
95
100
|
const [rows, setRows] = React.useState<ReportLibraryDto[]>([]);
|
|
96
101
|
const [categories, setCategories] = React.useState<ReportCategoryDto[]>([]);
|
|
@@ -344,9 +349,11 @@ export default function ReportLibraryPage() {
|
|
|
344
349
|
Clear
|
|
345
350
|
</Button>
|
|
346
351
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
352
|
+
{canAddLibrary && (
|
|
353
|
+
<Button size="md" renderIcon={Add} onClick={openCreate}>
|
|
354
|
+
New Library Entry
|
|
355
|
+
</Button>
|
|
356
|
+
)}
|
|
350
357
|
</div>
|
|
351
358
|
|
|
352
359
|
{error ? <InlineNotification lowContrast kind="error" title="Error" subtitle={error} /> : null}
|
|
@@ -396,22 +403,26 @@ export default function ReportLibraryPage() {
|
|
|
396
403
|
</TableCell>
|
|
397
404
|
<TableCell>
|
|
398
405
|
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
406
|
+
{canAddLibrary && (
|
|
407
|
+
<Button
|
|
408
|
+
kind="ghost"
|
|
409
|
+
size="sm"
|
|
410
|
+
renderIcon={Edit}
|
|
411
|
+
onClick={() => source && openEdit(source)}
|
|
412
|
+
>
|
|
413
|
+
Edit
|
|
414
|
+
</Button>
|
|
415
|
+
)}
|
|
416
|
+
{canDeleteLibrary && (
|
|
417
|
+
<Button
|
|
418
|
+
kind="ghost"
|
|
419
|
+
size="sm"
|
|
420
|
+
renderIcon={TrashCan}
|
|
421
|
+
onClick={() => source && onDelete(source)}
|
|
422
|
+
>
|
|
423
|
+
Retire
|
|
424
|
+
</Button>
|
|
425
|
+
)}
|
|
415
426
|
</div>
|
|
416
427
|
</TableCell>
|
|
417
428
|
</TableRow>
|