@bygd/nc-report-ui 0.1.40 → 0.1.42

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.
@@ -1,6 +1,6 @@
1
1
  var n,l$1,u$2,i$1,r$2,o$1,e$1,f$2,c$1,a$1,s$1,h$1,p$1,v$1,y$1,d$1={},w$2=[],_$1=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,g$2=Array.isArray;function m$1(n,l){for(var u in l)n[u]=l[u];return n}function b$1(n){n&&n.parentNode&&n.parentNode.removeChild(n);}function k$2(l,u,t){var i,r,o,e={};for(o in u)"key"==o?i=u[o]:"ref"==o?r=u[o]:e[o]=u[o];if(arguments.length>2&&(e.children=arguments.length>3?n.call(arguments,2):t),"function"==typeof l&&null!=l.defaultProps)for(o in l.defaultProps) void 0===e[o]&&(e[o]=l.defaultProps[o]);return x$2(l,e,i,r,null)}function x$2(n,t,i,r,o){var e={type:n,props:t,key:i,ref:r,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:null==o?++u$2:o,__i:-1,__u:0};return null==o&&null!=l$1.vnode&&l$1.vnode(e),e}function M$1(){return {current:null}}function S(n){return n.children}function C$2(n,l){this.props=n,this.context=l;}function $$1(n,l){if(null==l)return n.__?$$1(n.__,n.__i+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return "function"==typeof n.type?$$1(n):null}function I$1(n){if(n.__P&&n.__d){var u=n.__v,t=u.__e,i=[],r=[],o=m$1({},u);o.__v=u.__v+1,l$1.vnode&&l$1.vnode(o),q$2(n.__P,o,u,n.__n,n.__P.namespaceURI,32&u.__u?[t]:null,i,null==t?$$1(u):t,!!(32&u.__u),r),o.__v=u.__v,o.__.__k[o.__i]=o,D$2(i,o,r),u.__e=u.__=null,o.__e!=t&&P$2(o);}}function P$2(n){if(null!=(n=n.__)&&null!=n.__c)return n.__e=n.__c.base=null,n.__k.some(function(l){if(null!=l&&null!=l.__e)return n.__e=n.__c.base=l.__e}),P$2(n)}function A$2(n){(!n.__d&&(n.__d=true)&&i$1.push(n)&&!H$1.__r++||r$2!=l$1.debounceRendering)&&((r$2=l$1.debounceRendering)||o$1)(H$1);}function H$1(){try{for(var n,l=1;i$1.length;)i$1.length>l&&i$1.sort(e$1),n=i$1.shift(),l=i$1.length,I$1(n);}finally{i$1.length=H$1.__r=0;}}function L$1(n,l,u,t,i,r,o,e,f,c,a){var s,h,p,v,y,_,g,m=t&&t.__k||w$2,b=l.length;for(f=T$2(u,l,m,f,b),s=0;s<b;s++)null!=(p=u.__k[s])&&(h=-1!=p.__i&&m[p.__i]||d$1,p.__i=s,_=q$2(n,p,h,i,r,o,e,f,c,a),v=p.__e,p.ref&&h.ref!=p.ref&&(h.ref&&J$1(h.ref,null,p),a.push(p.ref,p.__c||v,p)),null==y&&null!=v&&(y=v),(g=!!(4&p.__u))||h.__k===p.__k?(f=j$2(p,f,n,g),g&&h.__e&&(h.__e=null)):"function"==typeof p.type&&void 0!==_?f=_:v&&(f=v.nextSibling),p.__u&=-7);return u.__e=y,f}function T$2(n,l,u,t,i){var r,o,e,f,c,a=u.length,s=a,h=0;for(n.__k=new Array(i),r=0;r<i;r++)null!=(o=l[r])&&"boolean"!=typeof o&&"function"!=typeof o?("string"==typeof o||"number"==typeof o||"bigint"==typeof o||o.constructor==String?o=n.__k[r]=x$2(null,o,null,null,null):g$2(o)?o=n.__k[r]=x$2(S,{children:o},null,null,null):void 0===o.constructor&&o.__b>0?o=n.__k[r]=x$2(o.type,o.props,o.key,o.ref?o.ref:null,o.__v):n.__k[r]=o,f=r+h,o.__=n,o.__b=n.__b+1,e=null,-1!=(c=o.__i=O$1(o,u,f,s))&&(s--,(e=u[c])&&(e.__u|=2)),null==e||null==e.__v?(-1==c&&(i>a?h--:i<a&&h++),"function"!=typeof o.type&&(o.__u|=4)):c!=f&&(c==f-1?h--:c==f+1?h++:(c>f?h--:h++,o.__u|=4))):n.__k[r]=null;if(s)for(r=0;r<a;r++)null!=(e=u[r])&&0==(2&e.__u)&&(e.__e==t&&(t=$$1(e)),K$1(e,e));return t}function j$2(n,l,u,t){var i,r;if("function"==typeof n.type){for(i=n.__k,r=0;i&&r<i.length;r++)i[r]&&(i[r].__=n,l=j$2(i[r],l,u,t));return l}n.__e!=l&&(t&&(l&&n.type&&!l.parentNode&&(l=$$1(n)),u.insertBefore(n.__e,l||null)),l=n.__e);do{l=l&&l.nextSibling;}while(null!=l&&8==l.nodeType);return l}function F$2(n,l){return l=l||[],null==n||"boolean"==typeof n||(g$2(n)?n.some(function(n){F$2(n,l);}):l.push(n)),l}function O$1(n,l,u,t){var i,r,o,e=n.key,f=n.type,c=l[u],a=null!=c&&0==(2&c.__u);if(null===c&&null==e||a&&e==c.key&&f==c.type)return u;if(t>(a?1:0))for(i=u-1,r=u+1;i>=0||r<l.length;)if(null!=(c=l[o=i>=0?i--:r++])&&0==(2&c.__u)&&e==c.key&&f==c.type)return o;return -1}function z$2(n,l,u){"-"==l[0]?n.setProperty(l,null==u?"":u):n[l]=null==u?"":"number"!=typeof u||_$1.test(l)?u:u+"px";}function N$1(n,l,u,t,i){var r,o;n:if("style"==l)if("string"==typeof u)n.style.cssText=u;else {if("string"==typeof t&&(n.style.cssText=t=""),t)for(l in t)u&&l in u||z$2(n.style,l,"");if(u)for(l in u)t&&u[l]==t[l]||z$2(n.style,l,u[l]);}else if("o"==l[0]&&"n"==l[1])r=l!=(l=l.replace(s$1,"$1")),o=l.toLowerCase(),l=o in n||"onFocusOut"==l||"onFocusIn"==l?o.slice(2):l.slice(2),n.l||(n.l={}),n.l[l+r]=u,u?t?u[a$1]=t[a$1]:(u[a$1]=h$1,n.addEventListener(l,r?v$1:p$1,r)):n.removeEventListener(l,r?v$1:p$1,r);else {if("http://www.w3.org/2000/svg"==i)l=l.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if("width"!=l&&"height"!=l&&"href"!=l&&"list"!=l&&"form"!=l&&"tabIndex"!=l&&"download"!=l&&"rowSpan"!=l&&"colSpan"!=l&&"role"!=l&&"popover"!=l&&l in n)try{n[l]=null==u?"":u;break n}catch(n){}"function"==typeof u||(null==u||false===u&&"-"!=l[4]?n.removeAttribute(l):n.setAttribute(l,"popover"==l&&1==u?"":u));}}function V$1(n){return function(u){if(this.l){var t=this.l[u.type+n];if(null==u[c$1])u[c$1]=h$1++;else if(u[c$1]<t[a$1])return;return t(l$1.event?l$1.event(u):u)}}}function q$2(n,u,t,i,r,o,e,f,c,a){var s,h,p,v,y,d,_,k,x,M,$,I,P,A,H,T,j=u.type;if(void 0!==u.constructor)return null;128&t.__u&&(c=!!(32&t.__u),o=[f=u.__e=t.__e]),(s=l$1.__b)&&s(u);n:if("function"==typeof j){h=e.length;try{if(x=u.props,M=j.prototype&&j.prototype.render,$=(s=j.contextType)&&i[s.__c],I=s?$?$.props.value:s.__:i,t.__c?k=(p=u.__c=t.__c).__=p.__E:(M?u.__c=p=new j(x,I):(u.__c=p=new C$2(x,I),p.constructor=j,p.render=Q$1),$&&$.sub(p),p.state||(p.state={}),p.__n=i,v=p.__d=!0,p.__h=[],p._sb=[]),M&&null==p.__s&&(p.__s=p.state),M&&null!=j.getDerivedStateFromProps&&(p.__s==p.state&&(p.__s=m$1({},p.__s)),m$1(p.__s,j.getDerivedStateFromProps(x,p.__s))),y=p.props,d=p.state,p.__v=u,v)M&&null==j.getDerivedStateFromProps&&null!=p.componentWillMount&&p.componentWillMount(),M&&null!=p.componentDidMount&&p.__h.push(p.componentDidMount);else {if(M&&null==j.getDerivedStateFromProps&&x!==y&&null!=p.componentWillReceiveProps&&p.componentWillReceiveProps(x,I),u.__v==t.__v||!p.__e&&null!=p.shouldComponentUpdate&&!1===p.shouldComponentUpdate(x,p.__s,I)){u.__v!=t.__v&&(p.props=x,p.state=p.__s,p.__d=!1),u.__e=t.__e,u.__k=t.__k,u.__k.some(function(n){n&&(n.__=u);}),w$2.push.apply(p.__h,p._sb),p._sb=[],p.__h.length&&e.push(p);break n}null!=p.componentWillUpdate&&p.componentWillUpdate(x,p.__s,I),M&&null!=p.componentDidUpdate&&p.__h.push(function(){p.componentDidUpdate(y,d,_);});}if(p.context=I,p.props=x,p.__P=n,p.__e=!1,P=l$1.__r,A=0,M)p.state=p.__s,p.__d=!1,P&&P(u),s=p.render(p.props,p.state,p.context),w$2.push.apply(p.__h,p._sb),p._sb=[];else do{p.__d=!1,P&&P(u),s=p.render(p.props,p.state,p.context),p.state=p.__s;}while(p.__d&&++A<25);p.state=p.__s,null!=p.getChildContext&&(i=m$1(m$1({},i),p.getChildContext())),M&&!v&&null!=p.getSnapshotBeforeUpdate&&(_=p.getSnapshotBeforeUpdate(y,d)),H=null!=s&&s.type===S&&null==s.key?E$1(s.props.children):s,f=L$1(n,g$2(H)?H:[H],u,t,i,r,o,e,f,c,a),p.base=u.__e,u.__u&=-161,p.__h.length&&e.push(p),k&&(p.__E=p.__=null);}catch(n){if(e.length=h,u.__v=null,c||null!=o){if(n.then){for(u.__u|=c?160:128;f&&8==f.nodeType&&f.nextSibling;)f=f.nextSibling;null!=o&&(o[o.indexOf(f)]=null),u.__e=f;}else if(null!=o)for(T=o.length;T--;)b$1(o[T]);}else u.__e=t.__e;null==u.__k&&(u.__k=t.__k||[]),n.then||B$2(u),l$1.__e(n,u,t);}}else null==o&&u.__v==t.__v?(u.__k=t.__k,u.__e=t.__e):f=u.__e=G$1(t.__e,u,t,i,r,o,e,c,a);return (s=l$1.diffed)&&s(u),128&u.__u?void 0:f}function B$2(n){n&&(n.__c&&(n.__c.__e=true),n.__k&&n.__k.some(B$2));}function D$2(n,u,t){for(var i=0;i<t.length;i++)J$1(t[i],t[++i],t[++i]);l$1.__c&&l$1.__c(u,n),n.some(function(u){try{n=u.__h,u.__h=[],n.some(function(n){n.call(u);});}catch(n){l$1.__e(n,u.__v);}});}function E$1(n){return "object"!=typeof n||null==n||n.__b>0?n:g$2(n)?n.map(E$1):void 0!==n.constructor?null:m$1({},n)}function G$1(u,t,i,r,o,e,f,c,a){var s,h,p,v,y,w,_,m=i.props||d$1,k=t.props,x=t.type;if("svg"==x?o="http://www.w3.org/2000/svg":"math"==x?o="http://www.w3.org/1998/Math/MathML":o||(o="http://www.w3.org/1999/xhtml"),null!=e)for(s=0;s<e.length;s++)if((y=e[s])&&"setAttribute"in y==!!x&&(x?y.localName==x:3==y.nodeType)){u=y,e[s]=null;break}if(null==u){if(null==x)return document.createTextNode(k);u=document.createElementNS(o,x,k.is&&k),c&&(l$1.__m&&l$1.__m(t,e),c=false),e=null;}if(null==x)m===k||c&&u.data==k||(u.data=k);else {if(e="textarea"==x&&null!=k.defaultValue?null:e&&n.call(u.childNodes),!c&&null!=e)for(m={},s=0;s<u.attributes.length;s++)m[(y=u.attributes[s]).name]=y.value;for(s in m)y=m[s],"dangerouslySetInnerHTML"==s?p=y:"children"==s||s in k||"value"==s&&"defaultValue"in k||"checked"==s&&"defaultChecked"in k||N$1(u,s,null,y,o);for(s in k)y=k[s],"children"==s?v=y:"dangerouslySetInnerHTML"==s?h=y:"value"==s?w=y:"checked"==s?_=y:c&&"function"!=typeof y||m[s]===y||N$1(u,s,y,m[s],o);if(h)c||p&&(h.__html==p.__html||h.__html==u.innerHTML)||(u.innerHTML=h.__html),t.__k=[];else if(p&&(u.innerHTML=""),L$1("template"==t.type?u.content:u,g$2(v)?v:[v],t,i,r,"foreignObject"==x?"http://www.w3.org/1999/xhtml":o,e,f,e?e[0]:i.__k&&$$1(i,0),c,a),null!=e)for(s=e.length;s--;)b$1(e[s]);c&&"textarea"!=x||(s="value","progress"==x&&null==w?u.removeAttribute("value"):null!=w&&(w!==u[s]||"progress"==x&&!w||"option"==x&&w!=m[s])&&N$1(u,s,w,m[s],o),s="checked",null!=_&&_!=u[s]&&N$1(u,s,_,m[s],o));}return u}function J$1(n,u,t){try{if("function"==typeof n){var i="function"==typeof n.__u;i&&n.__u(),i&&null==u||(n.__u=n(u));}else n.current=u;}catch(n){l$1.__e(n,t);}}function K$1(n,u,t){var i,r;if(l$1.unmount&&l$1.unmount(n),(i=n.ref)&&(i.current&&i.current!=n.__e||J$1(i,null,u)),null!=(i=n.__c)){if(i.componentWillUnmount)try{i.componentWillUnmount();}catch(n){l$1.__e(n,u);}i.base=i.__P=i.__n=null;}if(i=n.__k)for(r=0;r<i.length;r++)i[r]&&K$1(i[r],u,t||"function"!=typeof n.type);t||b$1(n.__e),n.__c=n.__=n.__e=void 0;}function Q$1(n,l,u){return this.constructor(n,u)}function R$1(u,t,i){var r,o,e,f;t==document&&(t=document.documentElement),l$1.__&&l$1.__(u,t),o=(r="function"==typeof i)?null:i&&i.__k||t.__k,e=[],f=[],q$2(t,u=(!r&&i||t).__k=k$2(S,null,[u]),o||d$1,d$1,t.namespaceURI,!r&&i?[i]:o?null:t.firstChild?n.call(t.childNodes):null,e,!r&&i?i:o?o.__e:t.firstChild,r,f),D$2(e,u,f),u.props.children=null;}function U$1(n,l){R$1(n,l,U$1);}function W$1(l,u,t){var i,r,o,e,f=m$1({},l.props);for(o in l.type&&l.type.defaultProps&&(e=l.type.defaultProps),u)"key"==o?i=u[o]:"ref"==o?r=u[o]:f[o]=void 0===u[o]&&null!=e?e[o]:u[o];return arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):t),x$2(l.type,f,i||l.key,r||l.ref,null)}function X$1(n){function l(n){var u,t;return this.getChildContext||(u=new Set,(t={})[l.__c]=this,this.getChildContext=function(){return t},this.componentWillUnmount=function(){u=null;},this.shouldComponentUpdate=function(n){this.props.value!=n.value&&u.forEach(function(n){n.__e=true,A$2(n);});},this.sub=function(n){u.add(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u&&u.delete(n),l&&l.call(n);};}),n.children}return l.__c="__cC"+y$1++,l.__=n,l.Provider=l.__l=(l.Consumer=function(n,l){return n.children(l)}).contextType=l,l}n=w$2.slice,l$1={__e:function(n,l,u,t){for(var i,r,o;l=l.__;)if((i=l.__c)&&!i.__)try{if((r=i.constructor)&&null!=r.getDerivedStateFromError&&(i.setState(r.getDerivedStateFromError(n)),o=i.__d),null!=i.componentDidCatch&&(i.componentDidCatch(n,t||{}),o=i.__d),o)return i.__E=i}catch(l){n=l;}throw n}},u$2=0,C$2.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!=this.state?this.__s:this.__s=m$1({},this.state),"function"==typeof n&&(n=n(m$1({},u),this.props)),n&&m$1(u,n),null!=n&&this.__v&&(l&&this._sb.push(l),A$2(this));},C$2.prototype.forceUpdate=function(n){this.__v&&(this.__e=true,n&&this.__h.push(n),A$2(this));},C$2.prototype.render=S,i$1=[],o$1="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,e$1=function(n,l){return n.__v.__b-l.__v.__b},H$1.__r=0,f$2=Math.random().toString(8),c$1="__d"+f$2,a$1="__a"+f$2,s$1=/(PointerCapture)$|Capture$/i,h$1=0,p$1=V$1(false),v$1=V$1(true),y$1=0;
2
2
 
3
- var t,r$1,u$1,i,o=0,f$1=[],c=l$1,e=c.__b,a=c.__r,v=c.diffed,l=c.__c,m=c.unmount,p=c.__;function s(n,t){c.__h&&c.__h(r$1,n,o||t),o=0;var u=r$1.__H||(r$1.__H={__:[],__h:[]});return n>=u.__.length&&u.__.push({}),u.__[n]}function d(n){return o=1,y(D$1,n)}function y(n,u,i){var o=s(t++,2);if(o.t=n,!o.__c&&(o.__=[i?i(u):D$1(void 0,u),function(n){var t=o.__N?o.__N[0]:o.__[0],r=o.t(t,n);t!==r&&(o.__N=[r,o.__[1]],o.__c.setState({}));}],o.__c=r$1,!r$1.__f)){var f=function(n,t,r){if(!o.__c.__H)return true;var u=false,i=o.__c.props!==n;if(o.__c.__H.__.some(function(n){if(n.__N){u=true;var t=n.__[0];n.__=n.__N,n.__N=void 0,t!==n.__[0]&&(i=true);}}),c){var f=c.call(this,n,t,r);return u?f||i:f}return !u||i};r$1.__f=true;var c=r$1.shouldComponentUpdate,e=r$1.componentWillUpdate;r$1.componentWillUpdate=function(n,t,r){if(this.__e){var u=c;c=void 0,f(n,t,r),c=u;}e&&e.call(this,n,t,r);},r$1.shouldComponentUpdate=f;}return o.__N||o.__}function h(n,u){var i=s(t++,3);!c.__s&&C$1(i.__H,u)&&(i.__=n,i.u=u,r$1.__H.__h.push(i));}function _(n,u){var i=s(t++,4);!c.__s&&C$1(i.__H,u)&&(i.__=n,i.u=u,r$1.__h.push(i));}function A$1(n){return o=5,T$1(function(){return {current:n}},[])}function F$1(n,t,r){o=6,_(function(){if("function"==typeof n){var r=n(t());return function(){n(null),r&&"function"==typeof r&&r();}}if(n)return n.current=t(),function(){return n.current=null}},null==r?r:r.concat(n));}function T$1(n,r){var u=s(t++,7);return C$1(u.__H,r)&&(u.__=n(),u.__H=r,u.__h=n),u.__}function q$1(n,t){return o=8,T$1(function(){return n},t)}function x$1(n){var u=r$1.context[n.__c],i=s(t++,9);return i.c=n,u?(null==i.__&&(i.__=true,u.sub(r$1)),u.props.value):n.__}function P$1(n,t){c.useDebugValue&&c.useDebugValue(t?t(n):n);}function b(n){var u=s(t++,10),i=d();return u.__=n,r$1.componentDidCatch||(r$1.componentDidCatch=function(n,t){u.__&&u.__(n,t),i[1](n);}),[i[0],function(){i[1](void 0);}]}function g$1(){var n=s(t++,11);if(!n.__){for(var u=r$1.__v;null!==u&&!u.__m&&null!==u.__;)u=u.__;var i=u.__m||(u.__m=[0,0]);n.__="P"+i[0]+"-"+i[1]++;}return n.__}function j$1(){for(var n;n=f$1.shift();){var t=n.__H;if(n.__P&&t)try{t.__h.some(z$1),t.__h.some(B$1),t.__h=[];}catch(r){t.__h=[],c.__e(r,n.__v);}}}c.__b=function(n){r$1=null,e&&e(n);},c.__=function(n,t){n&&t.__k&&t.__k.__m&&(n.__m=t.__k.__m),p&&p(n,t);},c.__r=function(n){a&&a(n),t=0;var i=(r$1=n.__c).__H;i&&(u$1===r$1?(i.__h=[],r$1.__h=[],i.__.some(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0;})):(i.__h.length&&j$1(),t=0)),u$1=r$1;},c.diffed=function(n){v&&v(n);var t=n.__c;t&&t.__H&&(t.__H.__h.length&&(1!==f$1.push(t)&&i===c.requestAnimationFrame||((i=c.requestAnimationFrame)||w$1)(j$1)),t.__H.__.some(function(n){n.u&&(n.__H=n.u,n.u=void 0);})),u$1=r$1=null;},c.__c=function(n,t){t.some(function(n){try{n.__h.some(z$1),n.__h=n.__h.filter(function(n){return !n.__||B$1(n)});}catch(r){t.some(function(n){n.__h&&(n.__h=[]);}),t=[],c.__e(r,n.__v);}}),l&&l(n,t);},c.unmount=function(n){m&&m(n);var t,r=n.__c;r&&r.__H&&(r.__H.__.some(function(n){try{z$1(n);}catch(n){t=n;}}),r.__H=void 0,t&&c.__e(t,r.__v));};var k$1="function"==typeof requestAnimationFrame;function w$1(n){var t,r=function(){clearTimeout(u),k$1&&cancelAnimationFrame(t),setTimeout(n);},u=setTimeout(r,35);k$1&&(t=requestAnimationFrame(r));}function z$1(n){var t=r$1,u=n.__c;"function"==typeof u&&(n.__c=void 0,u()),r$1=t;}function B$1(n){var t=r$1;n.__c=n.__(),r$1=t;}function C$1(n,t){return !n||n.length!==t.length||t.some(function(t,r){return t!==n[r]})}function D$1(n,t){return "function"==typeof t?t(n):t}
3
+ var t,r$1,u$1,i,o=0,f$1=[],c=l$1,e=c.__b,a=c.__r,v=c.diffed,l=c.__c,m=c.unmount,p=c.__;function s(n,t){c.__h&&c.__h(r$1,n,o||t),o=0;var u=r$1.__H||(r$1.__H={__:[],__h:[]});return n>=u.__.length&&u.__.push({}),u.__[n]}function d(n){return o=1,y(D$1,n)}function y(n,u,i){var o=s(t++,2);if(o.t=n,!o.__c&&(o.__=[i?i(u):D$1(void 0,u),function(n){var t=o.__N?o.__N[0]:o.__[0],r=o.t(t,n);t!==r&&(o.__N=[r,o.__[1]],o.__c.setState({}));}],o.__c=r$1,!r$1.__f)){var f=function(n,t,r){if(!o.__c.__H)return true;var u=false,i=o.__c.props!==n;if(o.__c.__H.__.some(function(n){if(n.__N){u=true;var t=n.__[0];n.__=n.__N,n.__N=void 0,t!==n.__[0]&&(i=true);}}),c){var f=c.call(this,n,t,r);return u?f||i:f}return !u||i};r$1.__f=true;var c=r$1.shouldComponentUpdate,e=r$1.componentWillUpdate;r$1.componentWillUpdate=function(n,t,r){if(this.__e){var u=c;c=void 0,f(n,t,r),c=u;}e&&e.call(this,n,t,r);},r$1.shouldComponentUpdate=f;}return o.__N||o.__}function h(n,u){var i=s(t++,3);!c.__s&&C$1(i.__H,u)&&(i.__=n,i.u=u,r$1.__H.__h.push(i));}function _(n,u){var i=s(t++,4);!c.__s&&C$1(i.__H,u)&&(i.__=n,i.u=u,r$1.__h.push(i));}function A$1(n){return o=5,T$1(function(){return {current:n}},[])}function F$1(n,t,r){o=6,_(function(){if("function"==typeof n){var r=n(t());return function(){n(null),r&&"function"==typeof r&&r();}}if(n)return n.current=t(),function(){return n.current=null}},null==r?r:r.concat(n));}function T$1(n,r){var u=s(t++,7);return C$1(u.__H,r)&&(u.__=n(),u.__H=r,u.__h=n),u.__}function q$1(n,t){return o=8,T$1(function(){return n},t)}function x$1(n){var u=r$1.context[n.__c],i=s(t++,9);return i.c=n,u?(null==i.__&&(i.__=true,u.sub(r$1)),u.props.value):n.__}function P$1(n,t){c.useDebugValue&&c.useDebugValue(t?t(n):n);}function b(n){var u=s(t++,10),i=d();return u.__=n,r$1.componentDidCatch||(r$1.componentDidCatch=function(n,t){u.__&&u.__(n,t),i[1](n);}),[i[0],function(){i[1](void 0);}]}function g$1(){var n=s(t++,11);if(!n.__){for(var u=r$1.__v;null!==u&&!u.__m&&null!==u.__;)u=u.__;var i=u.__m||(u.__m=[0,0]);n.__="P"+i[0]+"-"+i[1]++;}return n.__}function j$1(){for(var n;n=f$1.shift();){var t=n.__H;if(n.__P&&t)try{t.__h.some(z$1),t.__h.some(B$1),t.__h=[];}catch(r){t.__h=[],c.__e(r,n.__v);}}}c.__b=function(n){r$1=null,e&&e(n);},c.__=function(n,t){n&&t.__k&&t.__k.__m&&(n.__m=t.__k.__m),p&&p(n,t);},c.__r=function(n){a&&a(n),t=0;var i=(r$1=n.__c).__H;i&&(u$1===r$1?(i.__h=[],r$1.__h=[],i.__.some(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0;})):(i.__h.some(z$1),i.__h.some(B$1),i.__h=[],t=0)),u$1=r$1;},c.diffed=function(n){v&&v(n);var t=n.__c;t&&t.__H&&(t.__H.__h.length&&(1!==f$1.push(t)&&i===c.requestAnimationFrame||((i=c.requestAnimationFrame)||w$1)(j$1)),t.__H.__.some(function(n){n.u&&(n.__H=n.u,n.u=void 0);})),u$1=r$1=null;},c.__c=function(n,t){t.some(function(n){try{n.__h.some(z$1),n.__h=n.__h.filter(function(n){return !n.__||B$1(n)});}catch(r){t.some(function(n){n.__h&&(n.__h=[]);}),t=[],c.__e(r,n.__v);}}),l&&l(n,t);},c.unmount=function(n){m&&m(n);var t,r=n.__c;r&&r.__H&&(r.__H.__.some(function(n){try{z$1(n);}catch(n){t=n;}}),r.__H=void 0,t&&c.__e(t,r.__v));};var k$1="function"==typeof requestAnimationFrame;function w$1(n){var t,r=function(){clearTimeout(u),k$1&&cancelAnimationFrame(t),setTimeout(n);},u=setTimeout(r,35);k$1&&(t=requestAnimationFrame(r));}function z$1(n){var t=r$1,u=n.__c;"function"==typeof u&&(n.__c=void 0,u()),r$1=t;}function B$1(n){var t=r$1;n.__c=n.__(),r$1=t;}function C$1(n,t){return !n||n.length!==t.length||t.some(function(t,r){return t!==n[r]})}function D$1(n,t){return "function"==typeof t?t(n):t}
4
4
 
5
5
  function g(n,t){for(var e in t)n[e]=t[e];return n}function E(n,t){for(var e in n)if("__source"!==e&&!(e in t))return true;for(var r in t)if("__source"!==r&&n[r]!==t[r])return true;return false}function C(n,t){var e=t(),r=d({t:{__:e,u:t}}),u=r[0].t,o=r[1];return _(function(){u.__=e,u.u=t,R(u)&&o({t:u});},[n,e,t]),h(function(){return R(u)&&o({t:u}),n(function(){R(u)&&o({t:u});})},[n]),e}function R(n){try{return !((t=n.__)===(e=n.u())&&(0!==t||1/t==1/e)||t!=t&&e!=e)}catch(n){return true}var t,e;}function x(n){n();}function w(n){return n}function k(){return [false,x]}var I=_;function M(n,t){this.props=n,this.context=t;}function N(n,e){function r(n){var t=this.props.ref;return t!=n.ref&&t&&("function"==typeof t?t(null):t.current=null),e?!e(this.props,n)||t!=n.ref:E(this.props,n)}function u(e){return this.shouldComponentUpdate=r,k$2(n,e)}return u.displayName="Memo("+(n.displayName||n.name)+")",u.__f=u.prototype.isReactComponent=true,u.type=n,u}(M.prototype=new C$2).isPureReactComponent=true,M.prototype.shouldComponentUpdate=function(n,t){return E(this.props,n)||E(this.state,t)};var T=l$1.__b;l$1.__b=function(n){n.type&&n.type.__f&&n.ref&&(n.props.ref=n.ref,n.ref=null),T&&T(n);};var A="undefined"!=typeof Symbol&&Symbol.for&&Symbol.for("react.forward_ref")||3911;function D(n){function t(t){var e=g({},t);return delete e.ref,n(e,t.ref||null)}return t.$$typeof=A,t.render=n,t.prototype.isReactComponent=t.__f=true,t.displayName="ForwardRef("+(n.displayName||n.name)+")",t}var F=function(n,t){return null==n?null:F$2(F$2(n).map(t))},L={map:F,forEach:F,count:function(n){return n?F$2(n).length:0},only:function(n){var t=F$2(n);if(1!==t.length)throw "Children.only";return t[0]},toArray:F$2},O=l$1.__e;l$1.__e=function(n,t,e,r){if(n.then)for(var u,o=t;o=o.__;)if((u=o.__c)&&u.__c)return null==t.__e&&(t.__e=e.__e,t.__k=e.__k||[]),u.__c(n,t);O(n,t,e,r);};var U=l$1.unmount;function V(n,t,e){return n&&(n.__c&&n.__c.__H&&(n.__c.__H.__.forEach(function(n){"function"==typeof n.__c&&n.__c();}),n.__c.__H=null),null!=(n=g({},n)).__c&&(n.__c.__P===e&&(n.__c.__P=t),n.__c.__e=true,n.__c=null),n.__k=n.__k&&n.__k.map(function(n){return V(n,t,e)})),n}function W(n,t,e){return n&&e&&(n.__v=null,n.__k=n.__k&&n.__k.map(function(n){return W(n,t,e)}),n.__c&&n.__c.__P===t&&(n.__e&&e.appendChild(n.__e),n.__c.__e=true,n.__c.__P=e)),n}function P(){this.__u=0,this.o=null,this.__b=null;}function j(n){var t=n.__&&n.__.__c;return t&&t.__a&&t.__a(n)}function z(n){var e,r,u,o=null;function i(i){if(e||(e=n()).then(function(n){n&&(o=n.default||n),u=true;},function(n){r=n,u=true;}),r)throw r;if(!u)throw e;return o?k$2(o,i):null}return i.displayName="Lazy",i.__f=true,i}function B(){this.i=null,this.l=null;}l$1.unmount=function(n){var t=n.__c;t&&(t.__z=true),t&&t.__R&&t.__R(),t&&32&n.__u&&(n.type=null),U&&U(n);},(P.prototype=new C$2).__c=function(n,t){var e=t.__c,r=this;null==r.o&&(r.o=[]),r.o.push(e);var u=j(r.__v),o=false,i=function(){o||r.__z||(o=true,e.__R=null,u?u(c):c());};e.__R=i;var l=e.__P;e.__P=null;var c=function(){if(!--r.__u){if(r.state.__a){var n=r.state.__a;r.__v.__k[0]=W(n,n.__c.__P,n.__c.__O);}var t;for(r.setState({__a:r.__b=null});t=r.o.pop();)t.__P=l,t.forceUpdate();}};r.__u++||32&t.__u||r.setState({__a:r.__b=r.__v.__k[0]}),n.then(i,i);},P.prototype.componentWillUnmount=function(){this.o=[];},P.prototype.render=function(n,e){if(this.__b){if(this.__v.__k){var r=document.createElement("div"),o=this.__v.__k[0].__c;this.__v.__k[0]=V(this.__b,r,o.__O=o.__P);}this.__b=null;}var i=e.__a&&k$2(S,null,n.fallback);return i&&(i.__u&=-33),[k$2(S,null,e.__a?null:n.children),i]};var H=function(n,t,e){if(++e[1]===e[0]&&n.l.delete(t),n.props.revealOrder&&("t"!==n.props.revealOrder[0]||!n.l.size))for(e=n.i;e;){for(;e.length>3;)e.pop()();if(e[1]<e[0])break;n.i=e=e[2];}};function Z(n){return this.getChildContext=function(){return n.context},n.children}function Y(n){var e=this,r=n.h;if(e.componentWillUnmount=function(){R$1(null,e.v),e.v=null,e.h=null;},e.h&&e.h!==r&&e.componentWillUnmount(),!e.v){for(var u=e.__v;null!==u&&!u.__m&&null!==u.__;)u=u.__;e.h=r,e.v={nodeType:1,parentNode:r,childNodes:[],__k:{__m:u.__m},contains:function(){return true},namespaceURI:r.namespaceURI,insertBefore:function(n,t){this.childNodes.push(n),e.h.insertBefore(n,t);},removeChild:function(n){this.childNodes.splice(this.childNodes.indexOf(n)>>>1,1),e.h.removeChild(n);}};}R$1(k$2(Z,{context:e.context},n.__v),e.v);}function $(n,e){var r=k$2(Y,{__v:n,h:e});return r.containerInfo=e,r}(B.prototype=new C$2).__a=function(n){var t=this,e=j(t.__v),r=t.l.get(n);return r[0]++,function(u){var o=function(){t.props.revealOrder?(r.push(u),H(t,n,r)):u();};e?e(o):o();}},B.prototype.render=function(n){this.i=null,this.l=new Map;var t=F$2(n.children);n.revealOrder&&"b"===n.revealOrder[0]&&t.reverse();for(var e=t.length;e--;)this.l.set(t[e],this.i=[1,0,this.i]);return n.children},B.prototype.componentDidUpdate=B.prototype.componentDidMount=function(){var n=this;this.l.forEach(function(t,e){H(n,e,t);});};var q="undefined"!=typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103,G=/^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/,J=/^on(Ani|Tra|Tou|BeforeInp|Compo)/,K=/[A-Z0-9]/g,Q="undefined"!=typeof document,X=function(n){return ("undefined"!=typeof Symbol&&"symbol"==typeof Symbol()?/fil|che|rad/:/fil|che|ra/).test(n)};function nn(n,t,e){return null==t.__k&&(t.textContent=""),R$1(n,t),"function"==typeof e&&e(),n?n.__c:null}function tn(n,t,e){return U$1(n,t),"function"==typeof e&&e(),n?n.__c:null}C$2.prototype.isReactComponent=true,["componentWillMount","componentWillReceiveProps","componentWillUpdate"].forEach(function(t){Object.defineProperty(C$2.prototype,t,{configurable:true,get:function(){return this["UNSAFE_"+t]},set:function(n){Object.defineProperty(this,t,{configurable:true,writable:true,value:n});}});});var en=l$1.event;l$1.event=function(n){return en&&(n=en(n)),n.persist=function(){},n.isPropagationStopped=function(){return this.cancelBubble},n.isDefaultPrevented=function(){return this.defaultPrevented},n.nativeEvent=n};var rn,un={configurable:true,get:function(){return this.class}},on$1=l$1.vnode;l$1.vnode=function(n){"string"==typeof n.type&&function(n){var t=n.props,e=n.type,u={},o=-1==e.indexOf("-");for(var i in t){var l=t[i];if(!("value"===i&&"defaultValue"in t&&null==l||Q&&"children"===i&&"noscript"===e||"class"===i||"className"===i)){var c=i.toLowerCase();"defaultValue"===i&&"value"in t&&null==t.value?i="value":"download"===i&&true===l?l="":"translate"===c&&"no"===l?l=false:"o"===c[0]&&"n"===c[1]?"ondoubleclick"===c?i="ondblclick":"onchange"!==c||"input"!==e&&"textarea"!==e||X(t.type)?"onfocus"===c?i="onfocusin":"onblur"===c?i="onfocusout":J.test(i)&&(i=c):c=i="oninput":o&&G.test(i)?i=i.replace(K,"-$&").toLowerCase():null===l&&(l=void 0),"oninput"===c&&u[i=c]&&(i="oninputCapture"),u[i]=l;}}"select"==e&&(u.multiple&&Array.isArray(u.value)&&(u.value=F$2(t.children).forEach(function(n){n.props.selected=-1!=u.value.indexOf(n.props.value);})),null!=u.defaultValue&&(u.value=F$2(t.children).forEach(function(n){n.props.selected=u.multiple?-1!=u.defaultValue.indexOf(n.props.value):u.defaultValue==n.props.value;}))),t.class&&!t.className?(u.class=t.class,Object.defineProperty(u,"className",un)):t.className&&(u.class=u.className=t.className),n.props=u;}(n),n.$$typeof=q,on$1&&on$1(n);};var ln=l$1.__r;l$1.__r=function(n){ln&&ln(n),rn=n.__c;};var cn=l$1.diffed;l$1.diffed=function(n){cn&&cn(n);var t=n.props,e=n.__e;null!=e&&"textarea"===n.type&&"value"in t&&t.value!==e.value&&(e.value=null==t.value?"":t.value),rn=null;};var fn={ReactCurrentDispatcher:{current:{readContext:function(n){return rn.__n[n.__c].props.value},useCallback:q$1,useContext:x$1,useDebugValue:P$1,useDeferredValue:w,useEffect:h,useId:g$1,useImperativeHandle:F$1,useInsertionEffect:I,useLayoutEffect:_,useMemo:T$1,useReducer:y,useRef:A$1,useState:d,useSyncExternalStore:C,useTransition:k}}},an="18.3.1";function sn(n){return k$2.bind(null,n)}function hn(n){return !!n&&n.$$typeof===q}function vn(n){return hn(n)&&n.type===S}function dn(n){return !!n&&"string"==typeof n.displayName&&0==n.displayName.indexOf("Memo(")}function mn(n){return hn(n)?W$1.apply(null,arguments):n}function pn(n){return !!n.__k&&(R$1(null,n),true)}function yn(n){return n&&(n.base||1===n.nodeType&&n)||null}var _n=function(n,t){return n(t)},bn=function(n,t){var r=l$1.debounceRendering;l$1.debounceRendering=function(n){return n()};var u=n(t);return l$1.debounceRendering=r,u},Sn=hn,gn={useState:d,useId:g$1,useReducer:y,useEffect:h,useLayoutEffect:_,useInsertionEffect:I,useTransition:k,useDeferredValue:w,useSyncExternalStore:C,startTransition:x,useRef:A$1,useImperativeHandle:F$1,useMemo:T$1,useCallback:q$1,useContext:x$1,useDebugValue:P$1,version:"18.3.1",Children:L,render:nn,hydrate:tn,unmountComponentAtNode:pn,createPortal:$,createElement:k$2,createContext:X$1,createFactory:sn,cloneElement:mn,createRef:M$1,Fragment:S,isValidElement:hn,isElement:Sn,isFragment:vn,isMemo:dn,findDOMNode:yn,Component:C$2,PureComponent:M,memo:N,forwardRef:D,flushSync:bn,unstable_batchedUpdates:_n,StrictMode:S,Suspense:P,SuspenseList:B,lazy:z,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:fn};
6
6
 
@@ -44182,6 +44182,39 @@ const ReportingProvider = ({
44182
44182
  }, children);
44183
44183
  };
44184
44184
 
44185
+ /**
44186
+ * useReportingContext - Hook to access reporting context
44187
+ *
44188
+ * @returns {Object} Context value with parameters, api, and setter functions
44189
+ * @returns {Object} return.parameters - Current parameters object
44190
+ * @returns {Function} return.setParameters - Function to update parameters
44191
+ * @returns {Object} return.api - Current API configuration
44192
+ * @returns {Function} return.setApi - Function to update API configuration
44193
+ * @returns {Function} return.setReportingContext - Function to update both parameters and api
44194
+ *
44195
+ * @example
44196
+ * const { parameters, setParameters, api, setApi, setReportingContext } = useReportingContext();
44197
+ *
44198
+ * // Update parameters only
44199
+ * setParameters({ base_currency: 'USD', merchants: [...] });
44200
+ *
44201
+ * // Update API only
44202
+ * setApi({ token: 'new-token', base_url: 'https://api.example.com' });
44203
+ *
44204
+ * // Update both at once
44205
+ * setReportingContext({
44206
+ * parameters: { base_currency: 'EUR' },
44207
+ * api: { token: 'token', base_url: 'https://api.example.com' }
44208
+ * });
44209
+ */
44210
+ const useReportingContext = () => {
44211
+ const context = x$1(ReportingContext);
44212
+ if (context === undefined) {
44213
+ throw new Error('useReportingContext must be used within a ReportingProvider');
44214
+ }
44215
+ return context;
44216
+ };
44217
+
44185
44218
  /**
44186
44219
  * useReportingContextOptional - Hook to optionally access reporting context
44187
44220
  * Returns null if used outside of ReportingProvider (useful for optional fallback)
@@ -77135,6 +77168,140 @@ var ArrowDownwardIcon = createSvgIcon$1(/*#__PURE__*/u("path", {
77135
77168
  d: "m20 12-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8z"
77136
77169
  }));
77137
77170
 
77171
+ // Shared drag-and-drop primitives for the index-identified, arrow-reorderable
77172
+ // lists used across the report builder (Dimensions, Metrics, Column Order).
77173
+ // Previously duplicated per-tab; consolidated here so all three behave and
77174
+ // look identical and only need to be fixed in one place.
77175
+
77176
+ const useSortableListSensors = () => useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, {
77177
+ coordinateGetter: sortableKeyboardCoordinates
77178
+ }));
77179
+
77180
+ // Builds drag/move handlers for a list where dnd-kit item ids are the array
77181
+ // index. `onReorder` receives the whole reordered list.
77182
+ const makeListReorderHandlers = (list, onReorder) => {
77183
+ const handleDragEnd = ({
77184
+ active,
77185
+ over
77186
+ }) => {
77187
+ if (over && active.id !== over.id) {
77188
+ const oldIndex = list.findIndex((_, i) => i === active.id);
77189
+ const newIndex = list.findIndex((_, i) => i === over.id);
77190
+ onReorder(arrayMove(list, oldIndex, newIndex));
77191
+ }
77192
+ };
77193
+ const handleMoveUp = index => {
77194
+ if (index > 0) {
77195
+ onReorder(arrayMove(list, index, index - 1));
77196
+ }
77197
+ };
77198
+ const handleMoveDown = index => {
77199
+ if (index < list.length - 1) {
77200
+ onReorder(arrayMove(list, index, index + 1));
77201
+ }
77202
+ };
77203
+ return {
77204
+ handleDragEnd,
77205
+ handleMoveUp,
77206
+ handleMoveDown
77207
+ };
77208
+ };
77209
+
77210
+ // Wraps DndContext + SortableContext + the vertical list container.
77211
+ // `itemIds` must line up with the ids passed to each item's useSortable().
77212
+ const SortableListContext = ({
77213
+ sensors,
77214
+ itemIds,
77215
+ onDragEnd,
77216
+ children
77217
+ }) => /*#__PURE__*/gn.createElement(DndContext, {
77218
+ sensors: sensors,
77219
+ collisionDetection: closestCenter,
77220
+ onDragEnd: onDragEnd
77221
+ }, /*#__PURE__*/gn.createElement(SortableContext, {
77222
+ items: itemIds,
77223
+ strategy: verticalListSortingStrategy
77224
+ }, /*#__PURE__*/gn.createElement(Box, {
77225
+ sx: {
77226
+ display: "flex",
77227
+ flexDirection: "column",
77228
+ gap: 1
77229
+ }
77230
+ }, children)));
77231
+
77232
+ // Per-row useSortable() wiring shared by every sortable chip/row component.
77233
+ const useSortableRow = id => {
77234
+ const {
77235
+ attributes,
77236
+ listeners,
77237
+ setNodeRef,
77238
+ transform,
77239
+ transition,
77240
+ isDragging
77241
+ } = useSortable({
77242
+ id
77243
+ });
77244
+ const style = {
77245
+ transform: CSS$1.Transform.toString(transform),
77246
+ transition,
77247
+ opacity: isDragging ? 0.5 : 1,
77248
+ display: "flex",
77249
+ alignItems: "center",
77250
+ width: "100%"
77251
+ };
77252
+ return {
77253
+ attributes,
77254
+ listeners,
77255
+ setNodeRef,
77256
+ style
77257
+ };
77258
+ };
77259
+ const DragHandle = ({
77260
+ listeners
77261
+ }) => /*#__PURE__*/gn.createElement(Box, _extends({}, listeners, {
77262
+ sx: {
77263
+ display: "flex",
77264
+ alignItems: "center",
77265
+ cursor: "grab",
77266
+ "&:active": {
77267
+ cursor: "grabbing"
77268
+ }
77269
+ }
77270
+ }), /*#__PURE__*/gn.createElement(DragIndicatorIcon, {
77271
+ sx: {
77272
+ cursor: "grab",
77273
+ color: "rgba(110, 110, 110, 0.62)"
77274
+ }
77275
+ }));
77276
+ const MoveButtons = ({
77277
+ onMoveUp,
77278
+ onMoveDown,
77279
+ isFirst,
77280
+ isLast
77281
+ }) => /*#__PURE__*/gn.createElement(Box, {
77282
+ className: "hover-icons",
77283
+ sx: {
77284
+ display: "flex",
77285
+ gap: 0.5,
77286
+ opacity: 0,
77287
+ transition: "opacity 0.2s"
77288
+ }
77289
+ }, /*#__PURE__*/gn.createElement(IconButton, {
77290
+ size: "small",
77291
+ onClick: onMoveUp,
77292
+ disabled: isFirst,
77293
+ "aria-label": "move up"
77294
+ }, /*#__PURE__*/gn.createElement(ArrowUpwardIcon, {
77295
+ fontSize: "small"
77296
+ })), /*#__PURE__*/gn.createElement(IconButton, {
77297
+ size: "small",
77298
+ onClick: onMoveDown,
77299
+ disabled: isLast,
77300
+ "aria-label": "move down"
77301
+ }, /*#__PURE__*/gn.createElement(ArrowDownwardIcon, {
77302
+ fontSize: "small"
77303
+ })));
77304
+
77138
77305
  var SortIcon = createSvgIcon$1(/*#__PURE__*/u("path", {
77139
77306
  d: "M3 18h6v-2H3zM3 6v2h18V6zm0 7h12v-2H3z"
77140
77307
  }));
@@ -77170,6 +77337,50 @@ const interpolateTitle = (template, params) => {
77170
77337
  });
77171
77338
  };
77172
77339
 
77340
+ // Resolves the final column display order for the results grid: dimensions,
77341
+ // computed dimensions, and metrics combined into a single list.
77342
+ //
77343
+ // `columnOrder` is the optional, explicitly-arranged order (raw entries
77344
+ // `{ type: 'dimension' | 'computed' | 'metric', key }`) set on the Column
77345
+ // Order tab. When present, its entries come first, in that order. Anything
77346
+ // not covered by it (new fields added since it was last touched, or when
77347
+ // it's empty/undefined because no custom order has ever been defined) is
77348
+ // appended in the pre-existing default order: dimensions/computed (in their
77349
+ // own `dimensionOrder`) first, then metrics (in their own array order) —
77350
+ // this keeps behavior unchanged for reports that never define a column order.
77351
+ const resolveColumnOrder = (columnOrder, orderedDimensionItems, metrics) => {
77352
+ // Normalise every candidate item to a uniform { kind, key, data } shape
77353
+ // up front, so callers (e.g. the Column Order tab) can always read
77354
+ // `.key` regardless of where the item came from.
77355
+ const normalizedDimensionItems = orderedDimensionItems.map(item => ({
77356
+ kind: item.kind,
77357
+ key: item.kind === 'computed' ? item.data.name : item.data.fullPath,
77358
+ data: item.data
77359
+ }));
77360
+ const normalizedMetricItems = metrics.map(m => ({
77361
+ kind: 'metric',
77362
+ key: m.fullPath,
77363
+ data: m
77364
+ }));
77365
+ const byKey = Object.fromEntries([...normalizedDimensionItems, ...normalizedMetricItems].map(item => [`${item.kind}:${item.key}`, item]));
77366
+ const resolved = [];
77367
+ const seen = new Set();
77368
+ (columnOrder || []).forEach(entry => {
77369
+ const seenKey = `${entry.type}:${entry.key}`;
77370
+ if (seen.has(seenKey) || !byKey[seenKey]) return;
77371
+ resolved.push(byKey[seenKey]);
77372
+ seen.add(seenKey);
77373
+ });
77374
+ [...normalizedDimensionItems, ...normalizedMetricItems].forEach(item => {
77375
+ const seenKey = `${item.kind}:${item.key}`;
77376
+ if (!seen.has(seenKey)) {
77377
+ resolved.push(item);
77378
+ seen.add(seenKey);
77379
+ }
77380
+ });
77381
+ return resolved;
77382
+ };
77383
+
77173
77384
  // Sortable Chip Component
77174
77385
  const SortableChip$1 = ({
77175
77386
  id,
@@ -77198,20 +77409,8 @@ const SortableChip$1 = ({
77198
77409
  attributes,
77199
77410
  listeners,
77200
77411
  setNodeRef,
77201
- transform,
77202
- transition,
77203
- isDragging
77204
- } = useSortable({
77205
- id
77206
- });
77207
- const style = {
77208
- transform: CSS$1.Transform.toString(transform),
77209
- transition,
77210
- opacity: isDragging ? 0.5 : 1,
77211
- display: "flex",
77212
- alignItems: "center",
77213
- width: "100%"
77214
- };
77412
+ style
77413
+ } = useSortableRow(id);
77215
77414
 
77216
77415
  // Focus input when entering edit mode
77217
77416
  h(() => {
@@ -77320,21 +77519,9 @@ const SortableChip$1 = ({
77320
77519
  opacity: 1 // show icons on hover
77321
77520
  }
77322
77521
  }
77323
- }, /*#__PURE__*/gn.createElement(Box, _extends({}, listeners, {
77324
- sx: {
77325
- display: "flex",
77326
- alignItems: "center",
77327
- cursor: "grab",
77328
- "&:active": {
77329
- cursor: "grabbing"
77330
- }
77331
- }
77332
- }), /*#__PURE__*/gn.createElement(DragIndicatorIcon, {
77333
- sx: {
77334
- cursor: "grab",
77335
- color: "rgba(110, 110, 110, 0.62)"
77336
- }
77337
- })), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(Box, {
77522
+ }, /*#__PURE__*/gn.createElement(DragHandle, {
77523
+ listeners: listeners
77524
+ }), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(Box, {
77338
77525
  sx: {
77339
77526
  minWidth: 0
77340
77527
  }
@@ -77397,29 +77584,12 @@ const SortableChip$1 = ({
77397
77584
  sx: {
77398
77585
  flex: 1
77399
77586
  }
77400
- }), /*#__PURE__*/gn.createElement(Box, {
77401
- className: "hover-icons",
77402
- sx: {
77403
- display: "flex",
77404
- gap: 0.5,
77405
- opacity: 0,
77406
- transition: "opacity 0.2s"
77407
- }
77408
- }, /*#__PURE__*/gn.createElement(IconButton, {
77409
- size: "small",
77410
- onClick: onMoveUp,
77411
- disabled: isFirst,
77412
- "aria-label": "move up"
77413
- }, /*#__PURE__*/gn.createElement(ArrowUpwardIcon, {
77414
- fontSize: "small"
77415
- })), /*#__PURE__*/gn.createElement(IconButton, {
77416
- size: "small",
77417
- onClick: onMoveDown,
77418
- disabled: isLast,
77419
- "aria-label": "move down"
77420
- }, /*#__PURE__*/gn.createElement(ArrowDownwardIcon, {
77421
- fontSize: "small"
77422
- })))) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
77587
+ }), /*#__PURE__*/gn.createElement(MoveButtons, {
77588
+ onMoveUp: onMoveUp,
77589
+ onMoveDown: onMoveDown,
77590
+ isFirst: isFirst,
77591
+ isLast: isLast
77592
+ })) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
77423
77593
  inputRef: inputRef,
77424
77594
  value: editValue,
77425
77595
  onChange: e => setEditValue(e.target.value),
@@ -77481,7 +77651,6 @@ const SortableChip$1 = ({
77481
77651
  // the top-of-page Add form, so it stays visible regardless of scroll position.
77482
77652
  const SortableComputedChip = ({
77483
77653
  id,
77484
- index,
77485
77654
  name,
77486
77655
  value,
77487
77656
  onDelete,
@@ -77496,24 +77665,12 @@ const SortableComputedChip = ({
77496
77665
  attributes,
77497
77666
  listeners,
77498
77667
  setNodeRef,
77499
- transform,
77500
- transition,
77501
- isDragging
77502
- } = useSortable({
77503
- id
77504
- });
77668
+ style
77669
+ } = useSortableRow(id);
77505
77670
  const [isEditing, setIsEditing] = d(false);
77506
77671
  const [editName, setEditName] = d("");
77507
77672
  const [editValue, setEditValue] = d("");
77508
77673
  const containerRef = A$1(null);
77509
- const style = {
77510
- transform: CSS$1.Transform.toString(transform),
77511
- transition,
77512
- opacity: isDragging ? 0.5 : 1,
77513
- display: "flex",
77514
- alignItems: "center",
77515
- width: "100%"
77516
- };
77517
77674
 
77518
77675
  // Handle click outside to cancel
77519
77676
  h(() => {
@@ -77534,13 +77691,13 @@ const SortableComputedChip = ({
77534
77691
  setEditValue(value);
77535
77692
  setIsEditing(true);
77536
77693
  };
77537
- const nameError = editName.trim() !== "" && isNameTaken(editName.trim(), index);
77694
+ const nameError = editName.trim() !== "" && isNameTaken(editName.trim(), name);
77538
77695
  const handleSave = () => {
77539
77696
  const trimmedName = editName.trim();
77540
77697
  const trimmedValue = editValue.trim();
77541
77698
  if (!trimmedName || !trimmedValue) return;
77542
- if (isNameTaken(trimmedName, index)) return;
77543
- onUpdate(index, {
77699
+ if (isNameTaken(trimmedName, name)) return;
77700
+ onUpdate(name, {
77544
77701
  name: trimmedName,
77545
77702
  value: trimmedValue
77546
77703
  });
@@ -77576,21 +77733,9 @@ const SortableComputedChip = ({
77576
77733
  opacity: 1
77577
77734
  }
77578
77735
  }
77579
- }, /*#__PURE__*/gn.createElement(Box, _extends({}, listeners, {
77580
- sx: {
77581
- display: "flex",
77582
- alignItems: "center",
77583
- cursor: "grab",
77584
- "&:active": {
77585
- cursor: "grabbing"
77586
- }
77587
- }
77588
- }), /*#__PURE__*/gn.createElement(DragIndicatorIcon, {
77589
- sx: {
77590
- cursor: "grab",
77591
- color: "rgba(110, 110, 110, 0.62)"
77592
- }
77593
- })), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(Chip, {
77736
+ }, /*#__PURE__*/gn.createElement(DragHandle, {
77737
+ listeners: listeners
77738
+ }), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(Chip, {
77594
77739
  icon: /*#__PURE__*/gn.createElement(FunctionsIcon, {
77595
77740
  sx: {
77596
77741
  fontSize: "15px !important"
@@ -77654,29 +77799,12 @@ const SortableComputedChip = ({
77654
77799
  sx: {
77655
77800
  flex: 1
77656
77801
  }
77657
- }), /*#__PURE__*/gn.createElement(Box, {
77658
- className: "hover-icons",
77659
- sx: {
77660
- display: "flex",
77661
- gap: 0.5,
77662
- opacity: 0,
77663
- transition: "opacity 0.2s"
77664
- }
77665
- }, /*#__PURE__*/gn.createElement(IconButton, {
77666
- size: "small",
77667
- onClick: onMoveUp,
77668
- disabled: isFirst,
77669
- "aria-label": "move up"
77670
- }, /*#__PURE__*/gn.createElement(ArrowUpwardIcon, {
77671
- fontSize: "small"
77672
- })), /*#__PURE__*/gn.createElement(IconButton, {
77673
- size: "small",
77674
- onClick: onMoveDown,
77675
- disabled: isLast,
77676
- "aria-label": "move down"
77677
- }, /*#__PURE__*/gn.createElement(ArrowDownwardIcon, {
77678
- fontSize: "small"
77679
- })))) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
77802
+ }), /*#__PURE__*/gn.createElement(MoveButtons, {
77803
+ onMoveUp: onMoveUp,
77804
+ onMoveDown: onMoveDown,
77805
+ isFirst: isFirst,
77806
+ isLast: isLast
77807
+ })) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
77680
77808
  value: editName,
77681
77809
  onChange: e => setEditName(e.target.value),
77682
77810
  onKeyDown: handleKeyDown,
@@ -77738,7 +77866,7 @@ const Dimensions = ({
77738
77866
  savedDimensions = [],
77739
77867
  onSaveDimension,
77740
77868
  onRemoveDimension,
77741
- onReorderDimensions,
77869
+ onUpdateDimensionSortOrder,
77742
77870
  titleOverrides = {},
77743
77871
  onUpdateTitle,
77744
77872
  onResetTitle,
@@ -77748,7 +77876,8 @@ const Dimensions = ({
77748
77876
  onSaveComputedDimension,
77749
77877
  onUpdateComputedDimension,
77750
77878
  onRemoveComputedDimension,
77751
- onReorderComputedDimensions
77879
+ dimensionOrder = [],
77880
+ onReorderDimensionItems
77752
77881
  }) => {
77753
77882
  const reportingContext = useReportingContextOptional();
77754
77883
  const parameters = reportingContext?.parameters;
@@ -77760,53 +77889,38 @@ const Dimensions = ({
77760
77889
  const [computedValue, setComputedValue] = d("");
77761
77890
 
77762
77891
  // Setup drag and drop sensors
77763
- const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, {
77764
- coordinateGetter: sortableKeyboardCoordinates
77765
- }));
77766
-
77767
- // Handle drag end
77768
- const handleDragEnd = event => {
77769
- const {
77770
- active,
77771
- over
77772
- } = event;
77773
- if (over && active.id !== over.id) {
77774
- const oldIndex = savedDimensions.findIndex((_, i) => i === active.id);
77775
- const newIndex = savedDimensions.findIndex((_, i) => i === over.id);
77776
- const newOrder = arrayMove(savedDimensions, oldIndex, newIndex);
77777
- onReorderDimensions(newOrder);
77778
- }
77779
- };
77780
-
77781
- // Handle move up
77782
- const handleMoveUp = index => {
77783
- if (index > 0) {
77784
- const newOrder = arrayMove(savedDimensions, index, index - 1);
77785
- onReorderDimensions(newOrder);
77786
- }
77787
- };
77892
+ const sensors = useSortableListSensors();
77893
+
77894
+ // Resolve the unified dimensionOrder into the actual dimension / computed
77895
+ // dimension objects, so dimensions and computed dimensions can be shown
77896
+ // (and dragged) as a single list while staying stored in their own arrays.
77897
+ const dimensionByFullPath = Object.fromEntries(savedDimensions.map(d => [d.fullPath, d]));
77898
+ const computedDimensionByName = Object.fromEntries(savedComputedDimensions.map(cd => [cd.name, cd]));
77899
+ const orderedItems = dimensionOrder.map(entry => entry.type === "dimension" ? {
77900
+ kind: "dimension",
77901
+ key: entry.key,
77902
+ data: dimensionByFullPath[entry.key]
77903
+ } : {
77904
+ kind: "computed",
77905
+ key: entry.key,
77906
+ data: computedDimensionByName[entry.key]
77907
+ }).filter(item => item.data);
77788
77908
 
77789
- // Handle move down
77790
- const handleMoveDown = index => {
77791
- if (index < savedDimensions.length - 1) {
77792
- const newOrder = arrayMove(savedDimensions, index, index + 1);
77793
- onReorderDimensions(newOrder);
77794
- }
77795
- };
77909
+ // Handle drag/move for the unified list
77910
+ const {
77911
+ handleDragEnd,
77912
+ handleMoveUp,
77913
+ handleMoveDown
77914
+ } = makeListReorderHandlers(dimensionOrder, onReorderDimensionItems);
77796
77915
 
77797
- // Handle sort order change
77798
- const handleSortOrderChange = (index, sortOrder) => {
77799
- const newDimensions = [...savedDimensions];
77800
- newDimensions[index] = {
77801
- ...newDimensions[index],
77802
- sortOrder
77803
- };
77804
- onReorderDimensions(newDimensions);
77916
+ // Handle sort order change (dimensions only - computed dimensions have no order_by)
77917
+ const handleSortOrderChange = (fullPath, sortOrder) => {
77918
+ onUpdateDimensionSortOrder(fullPath, sortOrder);
77805
77919
  };
77806
77920
 
77807
77921
  // Computed Dimensions: { name, value } pairs independent of any provider.
77808
77922
  // Their `value` is a raw SQL literal/expression sent to the API as-is.
77809
- const isComputedNameTaken = (name, excludeIndex = null) => savedComputedDimensions.some((cd, i) => cd.name === name && i !== excludeIndex);
77923
+ const isComputedNameTaken = (name, excludeName = null) => savedComputedDimensions.some(cd => cd.name === name) && name !== excludeName;
77810
77924
  const computedNameError = computedName.trim() !== "" && isComputedNameTaken(computedName.trim());
77811
77925
  const handleAddComputedClick = () => {
77812
77926
  setIsAddingComputed(true);
@@ -77830,27 +77944,6 @@ const Dimensions = ({
77830
77944
  });
77831
77945
  handleCancelComputed();
77832
77946
  };
77833
- const handleComputedDragEnd = event => {
77834
- const {
77835
- active,
77836
- over
77837
- } = event;
77838
- if (over && active.id !== over.id) {
77839
- const oldIndex = savedComputedDimensions.findIndex((_, i) => i === active.id);
77840
- const newIndex = savedComputedDimensions.findIndex((_, i) => i === over.id);
77841
- onReorderComputedDimensions(arrayMove(savedComputedDimensions, oldIndex, newIndex));
77842
- }
77843
- };
77844
- const handleMoveComputedUp = index => {
77845
- if (index > 0) {
77846
- onReorderComputedDimensions(arrayMove(savedComputedDimensions, index, index - 1));
77847
- }
77848
- };
77849
- const handleMoveComputedDown = index => {
77850
- if (index < savedComputedDimensions.length - 1) {
77851
- onReorderComputedDimensions(arrayMove(savedComputedDimensions, index, index + 1));
77852
- }
77853
- };
77854
77947
 
77855
77948
  // Get the current provider based on selection chain
77856
77949
  const getCurrentProvider = () => {
@@ -78293,7 +78386,7 @@ const Dimensions = ({
78293
78386
  boxShadow: "none"
78294
78387
  }
78295
78388
  }
78296
- }, "Save Dimension"))), savedDimensions.length > 0 && /*#__PURE__*/gn.createElement(Box, {
78389
+ }, "Save Dimension"))), orderedItems.length > 0 && /*#__PURE__*/gn.createElement(Box, {
78297
78390
  sx: {
78298
78391
  marginTop: 0
78299
78392
  }
@@ -78311,81 +78404,40 @@ const Dimensions = ({
78311
78404
  color: "#666",
78312
78405
  marginBottom: 2
78313
78406
  }
78314
- }, "Drag to reorder or use arrows"), /*#__PURE__*/gn.createElement(DndContext, {
78407
+ }, "Drag to reorder or use arrows"), /*#__PURE__*/gn.createElement(SortableListContext, {
78315
78408
  sensors: sensors,
78316
- collisionDetection: closestCenter,
78409
+ itemIds: orderedItems.map((_, index) => index),
78317
78410
  onDragEnd: handleDragEnd
78318
- }, /*#__PURE__*/gn.createElement(SortableContext, {
78319
- items: savedDimensions.map((_, index) => index),
78320
- strategy: verticalListSortingStrategy
78321
- }, /*#__PURE__*/gn.createElement(Box, {
78322
- sx: {
78323
- display: "flex",
78324
- flexDirection: "column",
78325
- gap: 1
78326
- }
78327
- }, savedDimensions.map((dim, index) => /*#__PURE__*/gn.createElement(SortableChip$1, {
78328
- key: index,
78411
+ }, orderedItems.map((item, index) => item.kind === "dimension" ? /*#__PURE__*/gn.createElement(SortableChip$1, {
78412
+ key: `dim-${item.key}`,
78329
78413
  id: index,
78330
- label: dim.dimensionTitle,
78331
- fullLabel: `${formatProviderPath(dim)} → ${dim.dimensionTitle} (${dim.fullPath})`,
78332
- onDelete: () => onRemoveDimension(index),
78414
+ label: item.data.dimensionTitle,
78415
+ fullLabel: `${formatProviderPath(item.data)} → ${item.data.dimensionTitle} (${item.data.fullPath})`,
78416
+ onDelete: () => onRemoveDimension(item.key),
78333
78417
  onMoveUp: () => handleMoveUp(index),
78334
78418
  onMoveDown: () => handleMoveDown(index),
78335
78419
  isFirst: index === 0,
78336
- isLast: index === savedDimensions.length - 1,
78337
- sortOrder: dim.sortOrder || null,
78338
- onSortOrderChange: sortOrder => handleSortOrderChange(index, sortOrder),
78339
- fullPath: dim.fullPath,
78340
- defaultTitle: dim.dimensionTitle,
78341
- customTitle: titleOverrides[dim.fullPath],
78420
+ isLast: index === orderedItems.length - 1,
78421
+ sortOrder: item.data.sortOrder || null,
78422
+ onSortOrderChange: sortOrder => handleSortOrderChange(item.key, sortOrder),
78423
+ fullPath: item.data.fullPath,
78424
+ defaultTitle: item.data.dimensionTitle,
78425
+ customTitle: titleOverrides[item.data.fullPath],
78342
78426
  onUpdateTitle: onUpdateTitle,
78343
78427
  onResetTitle: onResetTitle
78344
- })))))), savedComputedDimensions.length > 0 && /*#__PURE__*/gn.createElement(Box, {
78345
- sx: {
78346
- marginTop: 2
78347
- }
78348
- }, /*#__PURE__*/gn.createElement(Typography, {
78349
- variant: "h6",
78350
- sx: {
78351
- fontSize: "16px",
78352
- fontWeight: 600,
78353
- marginTop: 2,
78354
- color: "rgb(37, 37, 37)"
78355
- }
78356
- }, "Saved Computed Dimensions"), /*#__PURE__*/gn.createElement(Typography, {
78357
- sx: {
78358
- fontSize: "13px",
78359
- color: "#666",
78360
- marginBottom: 2
78361
- }
78362
- }, "Drag to reorder or use arrows"), /*#__PURE__*/gn.createElement(DndContext, {
78363
- sensors: sensors,
78364
- collisionDetection: closestCenter,
78365
- onDragEnd: handleComputedDragEnd
78366
- }, /*#__PURE__*/gn.createElement(SortableContext, {
78367
- items: savedComputedDimensions.map((_, index) => index),
78368
- strategy: verticalListSortingStrategy
78369
- }, /*#__PURE__*/gn.createElement(Box, {
78370
- sx: {
78371
- display: "flex",
78372
- flexDirection: "column",
78373
- gap: 1
78374
- }
78375
- }, savedComputedDimensions.map((cd, index) => /*#__PURE__*/gn.createElement(SortableComputedChip, {
78376
- key: index,
78428
+ }) : /*#__PURE__*/gn.createElement(SortableComputedChip, {
78429
+ key: `comp-${item.key}`,
78377
78430
  id: index,
78378
- index: index,
78379
- name: cd.name,
78380
- value: cd.value,
78431
+ name: item.data.name,
78432
+ value: item.data.value,
78381
78433
  onUpdate: onUpdateComputedDimension,
78382
78434
  isNameTaken: isComputedNameTaken,
78383
- onDelete: () => onRemoveComputedDimension(index),
78384
- onMoveUp: () => handleMoveComputedUp(index),
78385
- onMoveDown: () => handleMoveComputedDown(index),
78435
+ onDelete: () => onRemoveComputedDimension(item.key),
78436
+ onMoveUp: () => handleMoveUp(index),
78437
+ onMoveDown: () => handleMoveDown(index),
78386
78438
  isFirst: index === 0,
78387
- isLast: index === savedComputedDimensions.length - 1
78388
- })))))));
78439
+ isLast: index === orderedItems.length - 1
78440
+ })))));
78389
78441
  };
78390
78442
 
78391
78443
  var StorageIcon = createSvgIcon$1(/*#__PURE__*/u("path", {
@@ -78472,20 +78524,8 @@ const SortableChip = ({
78472
78524
  attributes,
78473
78525
  listeners,
78474
78526
  setNodeRef,
78475
- transform,
78476
- transition,
78477
- isDragging
78478
- } = useSortable({
78479
- id
78480
- });
78481
- const style = {
78482
- transform: CSS$1.Transform.toString(transform),
78483
- transition,
78484
- opacity: isDragging ? 0.5 : 1,
78485
- display: 'flex',
78486
- alignItems: 'center',
78487
- width: '100%'
78488
- };
78527
+ style
78528
+ } = useSortableRow(id);
78489
78529
 
78490
78530
  // Focus input when entering edit mode
78491
78531
  h(() => {
@@ -78563,21 +78603,9 @@ const SortableChip = ({
78563
78603
  opacity: 1 // show icons on hover
78564
78604
  }
78565
78605
  }
78566
- }, /*#__PURE__*/gn.createElement(Box, _extends({}, listeners, {
78567
- sx: {
78568
- display: "flex",
78569
- alignItems: "center",
78570
- cursor: "grab",
78571
- "&:active": {
78572
- cursor: "grabbing"
78573
- }
78574
- }
78575
- }), /*#__PURE__*/gn.createElement(DragIndicatorIcon, {
78576
- sx: {
78577
- cursor: "grab",
78578
- color: "rgba(110, 110, 110, 0.62)"
78579
- }
78580
- })), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, source && /*#__PURE__*/gn.createElement(Box, {
78606
+ }, /*#__PURE__*/gn.createElement(DragHandle, {
78607
+ listeners: listeners
78608
+ }), !isEditing ? /*#__PURE__*/gn.createElement(gn.Fragment, null, source && /*#__PURE__*/gn.createElement(Box, {
78581
78609
  sx: {
78582
78610
  display: 'flex',
78583
78611
  alignItems: 'center',
@@ -78639,29 +78667,12 @@ const SortableChip = ({
78639
78667
  sx: {
78640
78668
  flex: 1
78641
78669
  }
78642
- }), /*#__PURE__*/gn.createElement(Box, {
78643
- className: "hover-icons",
78644
- sx: {
78645
- display: "flex",
78646
- gap: 0.5,
78647
- opacity: 0,
78648
- transition: "opacity 0.2s"
78649
- }
78650
- }, /*#__PURE__*/gn.createElement(IconButton, {
78651
- size: "small",
78652
- onClick: onMoveUp,
78653
- disabled: isFirst,
78654
- "aria-label": "move up"
78655
- }, /*#__PURE__*/gn.createElement(ArrowUpwardIcon, {
78656
- fontSize: "small"
78657
- })), /*#__PURE__*/gn.createElement(IconButton, {
78658
- size: "small",
78659
- onClick: onMoveDown,
78660
- disabled: isLast,
78661
- "aria-label": "move down"
78662
- }, /*#__PURE__*/gn.createElement(ArrowDownwardIcon, {
78663
- fontSize: "small"
78664
- })))) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
78670
+ }), /*#__PURE__*/gn.createElement(MoveButtons, {
78671
+ onMoveUp: onMoveUp,
78672
+ onMoveDown: onMoveDown,
78673
+ isFirst: isFirst,
78674
+ isLast: isLast
78675
+ })) : /*#__PURE__*/gn.createElement(gn.Fragment, null, /*#__PURE__*/gn.createElement(TextField, {
78665
78676
  inputRef: inputRef,
78666
78677
  value: editValue,
78667
78678
  onChange: e => setEditValue(e.target.value),
@@ -78736,39 +78747,14 @@ const Metrics = ({
78736
78747
  const [selectedMetric, setSelectedMetric] = d('');
78737
78748
 
78738
78749
  // Setup drag and drop sensors
78739
- const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, {
78740
- coordinateGetter: sortableKeyboardCoordinates
78741
- }));
78750
+ const sensors = useSortableListSensors();
78742
78751
 
78743
- // Handle drag end
78744
- const handleDragEnd = event => {
78745
- const {
78746
- active,
78747
- over
78748
- } = event;
78749
- if (over && active.id !== over.id) {
78750
- const oldIndex = savedMetrics.findIndex((_, i) => i === active.id);
78751
- const newIndex = savedMetrics.findIndex((_, i) => i === over.id);
78752
- const newOrder = arrayMove(savedMetrics, oldIndex, newIndex);
78753
- onReorderMetrics(newOrder);
78754
- }
78755
- };
78756
-
78757
- // Handle move up
78758
- const handleMoveUp = index => {
78759
- if (index > 0) {
78760
- const newOrder = arrayMove(savedMetrics, index, index - 1);
78761
- onReorderMetrics(newOrder);
78762
- }
78763
- };
78764
-
78765
- // Handle move down
78766
- const handleMoveDown = index => {
78767
- if (index < savedMetrics.length - 1) {
78768
- const newOrder = arrayMove(savedMetrics, index, index + 1);
78769
- onReorderMetrics(newOrder);
78770
- }
78771
- };
78752
+ // Handle drag/move
78753
+ const {
78754
+ handleDragEnd,
78755
+ handleMoveUp,
78756
+ handleMoveDown
78757
+ } = makeListReorderHandlers(savedMetrics, onReorderMetrics);
78772
78758
 
78773
78759
  // Get the current provider based on selection chain
78774
78760
  const getCurrentProvider = () => {
@@ -79107,19 +79093,10 @@ const Metrics = ({
79107
79093
  marginBottom: 1,
79108
79094
  color: "rgb(37, 37, 37)"
79109
79095
  }
79110
- }, "Saved Metrics"), /*#__PURE__*/gn.createElement(DndContext, {
79096
+ }, "Saved Metrics"), /*#__PURE__*/gn.createElement(SortableListContext, {
79111
79097
  sensors: sensors,
79112
- collisionDetection: closestCenter,
79098
+ itemIds: savedMetrics.map((_, index) => index),
79113
79099
  onDragEnd: handleDragEnd
79114
- }, /*#__PURE__*/gn.createElement(SortableContext, {
79115
- items: savedMetrics.map((_, index) => index),
79116
- strategy: verticalListSortingStrategy
79117
- }, /*#__PURE__*/gn.createElement(Box, {
79118
- sx: {
79119
- display: 'flex',
79120
- flexDirection: 'column',
79121
- gap: 1
79122
- }
79123
79100
  }, savedMetrics.map((metric, index) => /*#__PURE__*/gn.createElement(SortableChip, {
79124
79101
  key: index,
79125
79102
  id: index,
@@ -79136,7 +79113,7 @@ const Metrics = ({
79136
79113
  onUpdateTitle: onUpdateTitle,
79137
79114
  onResetTitle: onResetTitle,
79138
79115
  source: metric.metric?.source
79139
- })))))));
79116
+ })))));
79140
79117
  };
79141
79118
 
79142
79119
  var FilterAltIcon = createSvgIcon$1(/*#__PURE__*/u("path", {
@@ -91699,6 +91676,209 @@ const Filters = ({
91699
91676
  }))));
91700
91677
  };
91701
91678
 
91679
+ var CalculateIcon = createSvgIcon$1(/*#__PURE__*/u("path", {
91680
+ d: "M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2m-5.97 4.06L14.09 6l1.41 1.41L16.91 6l1.06 1.06-1.41 1.41 1.41 1.41-1.06 1.06-1.41-1.4-1.41 1.41-1.06-1.06 1.41-1.41zm-6.78.66h5v1.5h-5zM11.5 16h-2v2H8v-2H6v-1.5h2v-2h1.5v2h2zm6.5 1.25h-5v-1.5h5zm0-2.5h-5v-1.5h5z"
91681
+ }));
91682
+
91683
+ const KIND_LABEL = {
91684
+ dimension: "Dimension",
91685
+ computed: "Computed",
91686
+ metric: "Metric"
91687
+ };
91688
+ const KindChip = ({
91689
+ kind,
91690
+ source
91691
+ }) => {
91692
+ if (kind === "metric") {
91693
+ return /*#__PURE__*/gn.createElement(Box, {
91694
+ sx: {
91695
+ display: "flex",
91696
+ alignItems: "center",
91697
+ flexShrink: 0
91698
+ }
91699
+ }, /*#__PURE__*/gn.createElement(MetricSourceIcon, {
91700
+ source: source
91701
+ }));
91702
+ }
91703
+ return /*#__PURE__*/gn.createElement(Chip, {
91704
+ icon: kind === "computed" ? /*#__PURE__*/gn.createElement(CalculateIcon, {
91705
+ sx: {
91706
+ fontSize: "15px !important"
91707
+ }
91708
+ }) : undefined,
91709
+ label: KIND_LABEL[kind],
91710
+ size: "small",
91711
+ sx: {
91712
+ fontWeight: 500,
91713
+ backgroundColor: "rgba(70, 134, 128, 0.1)",
91714
+ color: "rgb(70, 134, 128)",
91715
+ flexShrink: 0
91716
+ }
91717
+ });
91718
+ };
91719
+
91720
+ // Read-only sortable row: this tab only reorders columns. Adding, removing,
91721
+ // and renaming stay on the Dimensions/Metrics tabs so there's one place each
91722
+ // field is managed.
91723
+ const SortableColumnRow = ({
91724
+ id,
91725
+ label,
91726
+ fullPath,
91727
+ kind,
91728
+ source,
91729
+ onMoveUp,
91730
+ onMoveDown,
91731
+ isFirst,
91732
+ isLast
91733
+ }) => {
91734
+ const reportingContext = useReportingContextOptional();
91735
+ const parameters = reportingContext?.parameters;
91736
+ const {
91737
+ attributes,
91738
+ listeners,
91739
+ setNodeRef,
91740
+ style
91741
+ } = useSortableRow(id);
91742
+ const displayLabel = interpolateTitle(label, parameters);
91743
+ return /*#__PURE__*/gn.createElement("div", _extends({
91744
+ ref: setNodeRef,
91745
+ style: style
91746
+ }, attributes), /*#__PURE__*/gn.createElement(Box, {
91747
+ sx: {
91748
+ display: "flex",
91749
+ alignItems: "center",
91750
+ width: "100%",
91751
+ gap: 1,
91752
+ backgroundColor: "white",
91753
+ border: "1px solid #e0e0e0",
91754
+ borderRadius: 2,
91755
+ padding: 1,
91756
+ transition: "transform 0.2s ease, box-shadow 0.2s ease",
91757
+ "&:hover .hover-icons": {
91758
+ opacity: 1
91759
+ }
91760
+ }
91761
+ }, /*#__PURE__*/gn.createElement(DragHandle, {
91762
+ listeners: listeners
91763
+ }), /*#__PURE__*/gn.createElement(KindChip, {
91764
+ kind: kind,
91765
+ source: source
91766
+ }), /*#__PURE__*/gn.createElement(Tooltip, {
91767
+ title: fullPath,
91768
+ arrow: true,
91769
+ placement: "top"
91770
+ }, /*#__PURE__*/gn.createElement(Typography, {
91771
+ variant: "h6",
91772
+ sx: {
91773
+ fontWeight: 500,
91774
+ color: "#1a1a1a",
91775
+ fontSize: "14px",
91776
+ whiteSpace: "nowrap",
91777
+ overflow: "hidden",
91778
+ textOverflow: "ellipsis"
91779
+ }
91780
+ }, displayLabel)), /*#__PURE__*/gn.createElement(Box, {
91781
+ sx: {
91782
+ flex: 1
91783
+ }
91784
+ }), /*#__PURE__*/gn.createElement(MoveButtons, {
91785
+ onMoveUp: onMoveUp,
91786
+ onMoveDown: onMoveDown,
91787
+ isFirst: isFirst,
91788
+ isLast: isLast
91789
+ })));
91790
+ };
91791
+ const ColumnOrder = ({
91792
+ orderedDimensionItems = [],
91793
+ metrics = [],
91794
+ columnOrder = [],
91795
+ onReorderColumnItems,
91796
+ titleOverrides = {
91797
+ dimensions: {},
91798
+ metrics: {}
91799
+ }
91800
+ }) => {
91801
+ const isCustomized = columnOrder.length > 0;
91802
+ const resolvedItems = resolveColumnOrder(columnOrder, orderedDimensionItems, metrics);
91803
+ const sensors = useSortableListSensors();
91804
+ const reorderResolved = newResolvedItems => {
91805
+ onReorderColumnItems(newResolvedItems.map(item => ({
91806
+ type: item.kind,
91807
+ key: item.key
91808
+ })));
91809
+ };
91810
+ const {
91811
+ handleDragEnd,
91812
+ handleMoveUp,
91813
+ handleMoveDown
91814
+ } = makeListReorderHandlers(resolvedItems, reorderResolved);
91815
+ const labelFor = item => {
91816
+ if (item.kind === "computed") return item.data.name;
91817
+ if (item.kind === "metric") {
91818
+ return titleOverrides.metrics[item.key] || item.data.metricTitle || item.key;
91819
+ }
91820
+ return titleOverrides.dimensions[item.key] || item.data.dimensionTitle || item.key;
91821
+ };
91822
+ return /*#__PURE__*/gn.createElement("div", null, /*#__PURE__*/gn.createElement(Box, {
91823
+ sx: {
91824
+ display: "flex",
91825
+ justifyContent: "space-between",
91826
+ alignItems: "flex-start",
91827
+ mb: 2,
91828
+ gap: 2
91829
+ }
91830
+ }, /*#__PURE__*/gn.createElement(Typography, {
91831
+ sx: {
91832
+ fontSize: "13px",
91833
+ color: "#666"
91834
+ }
91835
+ }, "Optional: define a single order for all columns (dimensions, computed dimensions and metrics together) in the results grid. Drag to reorder or use the arrows. Newly added dimensions/metrics are appended to the end automatically. Without a custom order, columns follow the Dimensions tab order, then the Metrics tab order."), isCustomized && /*#__PURE__*/gn.createElement(Tooltip, {
91836
+ title: "Discard the custom order and go back to the default (Dimensions, then Metrics)",
91837
+ arrow: true
91838
+ }, /*#__PURE__*/gn.createElement(Button, {
91839
+ variant: "outlined",
91840
+ size: "small",
91841
+ startIcon: /*#__PURE__*/gn.createElement(RestartAltIcon, null),
91842
+ onClick: () => onReorderColumnItems([]),
91843
+ sx: {
91844
+ height: "36px",
91845
+ flexShrink: 0,
91846
+ fontFamily: "system-ui",
91847
+ fontSize: "13px",
91848
+ fontWeight: 500,
91849
+ borderRadius: "8px",
91850
+ boxShadow: "none",
91851
+ textTransform: "none",
91852
+ borderColor: "#e0e0e0",
91853
+ color: "rgb(37, 37, 37)",
91854
+ "&:hover": {
91855
+ backgroundColor: "#f5f5f5",
91856
+ borderColor: "#d0d0d0"
91857
+ }
91858
+ }
91859
+ }, "Reset to Default"))), resolvedItems.length === 0 ? /*#__PURE__*/gn.createElement(Typography, {
91860
+ sx: {
91861
+ fontSize: "13px",
91862
+ color: "#999"
91863
+ }
91864
+ }, "Add dimensions or metrics in the other tabs first.") : /*#__PURE__*/gn.createElement(SortableListContext, {
91865
+ sensors: sensors,
91866
+ itemIds: resolvedItems.map((_, index) => index),
91867
+ onDragEnd: handleDragEnd
91868
+ }, resolvedItems.map((item, index) => /*#__PURE__*/gn.createElement(SortableColumnRow, {
91869
+ key: `${item.kind}-${item.key}`,
91870
+ id: index,
91871
+ label: labelFor(item),
91872
+ fullPath: item.kind === "computed" ? item.data.value : item.key,
91873
+ kind: item.kind,
91874
+ source: item.kind === "metric" ? item.data.metric?.source : undefined,
91875
+ onMoveUp: () => handleMoveUp(index),
91876
+ onMoveDown: () => handleMoveDown(index),
91877
+ isFirst: index === 0,
91878
+ isLast: index === resolvedItems.length - 1
91879
+ }))));
91880
+ };
91881
+
91702
91882
  // Default numeral.js formats applied when a dimension/metric definition has a
91703
91883
  // known type but no explicit `format` set on the provider.
91704
91884
  const DEFAULT_FORMATS_BY_TYPE = {
@@ -91713,11 +91893,107 @@ const formatValue = (value, def) => {
91713
91893
  return def?.prefix ? `${def.prefix} ${formatted}` : formatted;
91714
91894
  };
91715
91895
  const isNumericType = type => type === 'integer' || type === 'currency';
91896
+
91897
+ // Builds a single DataGrid column definition for one resolved column item
91898
+ // (a dimension, computed dimension, or metric). Field naming logic:
91899
+ // - Dimensions from base provider: baseAlias.field -> baseAlias_field
91900
+ // - Dimensions from nested provider: baseAlias_rel1_rel2.field -> rel1_rel2_field (drops base alias)
91901
+ // - Computed dimensions have no provider/relations chain, so the API returns
91902
+ // each row keyed by the computed dimension's own `name`.
91903
+ // - Metrics: API returns metric keys in different forms depending on path depth
91904
+ // (see comment inline below), normalised to match the row-key normalisation
91905
+ // done when building `rows`.
91906
+ const buildColumn = (item, titleOverrides, parameters) => {
91907
+ if (item.kind === 'computed') {
91908
+ const cd = item.data;
91909
+ return {
91910
+ field: cd.name,
91911
+ headerName: cd.name,
91912
+ flex: 1,
91913
+ minWidth: 150,
91914
+ type: 'string'
91915
+ };
91916
+ }
91917
+ if (item.kind === 'metric') {
91918
+ const metric = item.data;
91919
+ const metricDef = metric.metric;
91920
+ let fieldName;
91921
+ const dotCount = (metric.fullPath.match(/\./g) || []).length;
91922
+ if (dotCount >= 2) {
91923
+ // 3+-part namespaced path: API returns the full dotted path as the key.
91924
+ // Normalise dots to underscores to match the row key normalisation below.
91925
+ // Example: "mc.dkpi.activeAgreementsCount" -> "mc_dkpi_activeAgreementsCount"
91926
+ fieldName = metric.fullPath.replace(/\./g, '_');
91927
+ } else if (metric.relations && metric.relations.length > 0) {
91928
+ // 2-part path from a nested provider: API drops the base provider alias.
91929
+ // Example: "mc_fa.total_amount" -> "fa_total_amount"
91930
+ const parts = metric.fullPath.split('.');
91931
+ const pathWithoutField = parts[0]; // e.g., "mc_fa"
91932
+ const field = parts[1]; // e.g., "total_amount"
91933
+
91934
+ const pathParts = pathWithoutField.split('_');
91935
+ pathParts.shift(); // remove base provider alias
91936
+ const pathWithoutBase = pathParts.join('_');
91937
+ fieldName = pathWithoutBase ? `${pathWithoutBase}_${field}` : field;
91938
+ } else {
91939
+ // 2-part path from the base provider: API returns just the metric name.
91940
+ // Example: "mc.record_count" -> "record_count"
91941
+ fieldName = metric.metricName;
91942
+ }
91943
+ const headerName = interpolateTitle(titleOverrides.metrics[metric.fullPath] || metric.metricTitle || fieldName, parameters);
91944
+ return {
91945
+ field: fieldName,
91946
+ headerName: headerName,
91947
+ flex: 1,
91948
+ minWidth: 150,
91949
+ type: isNumericType(metricDef?.type) ? 'number' : 'string',
91950
+ renderHeader: () => /*#__PURE__*/gn.createElement(Box, {
91951
+ sx: {
91952
+ display: 'flex',
91953
+ alignItems: 'center',
91954
+ gap: 0.5
91955
+ }
91956
+ }, /*#__PURE__*/gn.createElement(MetricSourceIcon, {
91957
+ source: metricDef?.source
91958
+ }), /*#__PURE__*/gn.createElement("span", null, headerName)),
91959
+ valueFormatter: value => formatValue(value, metricDef)
91960
+ };
91961
+ }
91962
+
91963
+ // Dimension
91964
+ const dim = item.data;
91965
+ let fieldName;
91966
+ if (dim.relations && dim.relations.length > 0) {
91967
+ // From nested provider: drop the base provider alias
91968
+ // Example: ft_fa_db.currency -> fa_db_currency
91969
+ const parts = dim.fullPath.split('.');
91970
+ const pathWithoutField = parts[0]; // e.g., "ft_fa_db"
91971
+ const field = parts[1]; // e.g., "currency"
91972
+
91973
+ const pathParts = pathWithoutField.split('_');
91974
+ pathParts.shift(); // Remove base provider alias
91975
+ const pathWithoutBase = pathParts.join('_'); // e.g., "fa_db"
91976
+
91977
+ fieldName = pathWithoutBase ? `${pathWithoutBase}_${field}` : field;
91978
+ } else {
91979
+ // From base provider: keep the full path with underscore
91980
+ // Example: ba.created_at -> ba_created_at
91981
+ fieldName = dim.fullPath.replace('.', '_');
91982
+ }
91983
+ const headerName = interpolateTitle(titleOverrides.dimensions[dim.fullPath] || dim.dimensionTitle || fieldName, parameters);
91984
+ const dimDef = dim.dimension;
91985
+ return {
91986
+ field: fieldName,
91987
+ headerName: headerName,
91988
+ flex: 1,
91989
+ minWidth: 150,
91990
+ type: isNumericType(dimDef?.type) ? 'number' : 'string',
91991
+ valueFormatter: value => formatValue(value, dimDef)
91992
+ };
91993
+ };
91716
91994
  const ReportDataGrid = ({
91717
91995
  reportData,
91718
- dimensions,
91719
- metrics,
91720
- computedDimensions = [],
91996
+ columnItems = [],
91721
91997
  loading,
91722
91998
  onPageChange,
91723
91999
  totalRows = 0,
@@ -91733,124 +92009,10 @@ const ReportDataGrid = ({
91733
92009
  pageSize: 50
91734
92010
  });
91735
92011
 
91736
- // Generate columns dynamically from dimensions and metrics
91737
- const columns = gn.useMemo(() => {
91738
- const cols = [];
91739
-
91740
- // Add dimension columns
91741
- // Field naming logic:
91742
- // - If from base provider: baseAlias.field -> baseAlias_field
91743
- // - If from nested provider: baseAlias_rel1_rel2.field -> rel1_rel2_field (drops base alias)
91744
- dimensions.forEach(dim => {
91745
- let fieldName;
91746
- let headerName;
91747
-
91748
- // Check if there are relations (nested providers)
91749
- if (dim.relations && dim.relations.length > 0) {
91750
- // From nested provider: drop the base provider alias
91751
- // Example: ft_fa_db.currency -> fa_db_currency
91752
- const parts = dim.fullPath.split('.');
91753
- const pathWithoutField = parts[0]; // e.g., "ft_fa_db"
91754
- const field = parts[1]; // e.g., "currency"
91755
-
91756
- // Remove the base provider alias (first part before first underscore)
91757
- const pathParts = pathWithoutField.split('_');
91758
- pathParts.shift(); // Remove base provider alias
91759
- const pathWithoutBase = pathParts.join('_'); // e.g., "fa_db"
91760
-
91761
- fieldName = pathWithoutBase ? `${pathWithoutBase}_${field}` : field;
91762
- } else {
91763
- // From base provider: keep the full path with underscore
91764
- // Example: ba.created_at -> ba_created_at
91765
- fieldName = dim.fullPath.replace('.', '_');
91766
- }
91767
-
91768
- // Check for title override, otherwise use the friendly dimension title from provider
91769
- headerName = titleOverrides.dimensions[dim.fullPath] || dim.dimensionTitle || fieldName;
91770
- headerName = interpolateTitle(headerName, parameters);
91771
- const dimDef = dim.dimension;
91772
- cols.push({
91773
- field: fieldName,
91774
- headerName: headerName,
91775
- flex: 1,
91776
- minWidth: 150,
91777
- type: isNumericType(dimDef?.type) ? 'number' : 'string',
91778
- valueFormatter: value => formatValue(value, dimDef)
91779
- });
91780
- });
91781
-
91782
- // Add computed dimension columns. These have no provider/relations chain,
91783
- // so the API returns each row keyed by the computed dimension's own `name`.
91784
- computedDimensions.forEach(cd => {
91785
- cols.push({
91786
- field: cd.name,
91787
- headerName: cd.name,
91788
- flex: 1,
91789
- minWidth: 150,
91790
- type: 'string'
91791
- });
91792
- });
91793
-
91794
- // Add metric columns
91795
- // The API returns metric keys in two different forms depending on the path depth:
91796
- //
91797
- // • 2-part path "mc.record_count" → API key: "record_count"
91798
- // • 2-part path "mc_fa.total_amount" (nested) → API key: "fa_total_amount"
91799
- // • 3+-part path "mc.dkpi.activeAgreementsCount"→ API key: "mc.dkpi.activeAgreementsCount" (dots)
91800
- //
91801
- // For 3+-part paths the API key contains dots. MUI DataGrid interprets dots
91802
- // in field names as nested-object accessors, so the rows useMemo normalises
91803
- // those keys (dots → underscores). The column field must match that normalised key.
91804
- metrics.forEach(metric => {
91805
- const metricDef = metric.metric;
91806
- let fieldName;
91807
- let headerName;
91808
- const dotCount = (metric.fullPath.match(/\./g) || []).length;
91809
- if (dotCount >= 2) {
91810
- // 3+-part namespaced path: API returns the full dotted path as the key.
91811
- // Normalise dots to underscores to match the row key normalisation below.
91812
- // Example: "mc.dkpi.activeAgreementsCount" -> "mc_dkpi_activeAgreementsCount"
91813
- fieldName = metric.fullPath.replace(/\./g, '_');
91814
- } else if (metric.relations && metric.relations.length > 0) {
91815
- // 2-part path from a nested provider: API drops the base provider alias.
91816
- // Example: "mc_fa.total_amount" -> "fa_total_amount"
91817
- const parts = metric.fullPath.split('.');
91818
- const pathWithoutField = parts[0]; // e.g., "mc_fa"
91819
- const field = parts[1]; // e.g., "total_amount"
91820
-
91821
- const pathParts = pathWithoutField.split('_');
91822
- pathParts.shift(); // remove base provider alias
91823
- const pathWithoutBase = pathParts.join('_');
91824
- fieldName = pathWithoutBase ? `${pathWithoutBase}_${field}` : field;
91825
- } else {
91826
- // 2-part path from the base provider: API returns just the metric name.
91827
- // Example: "mc.record_count" -> "record_count"
91828
- fieldName = metric.metricName;
91829
- }
91830
-
91831
- // Check for title override, otherwise use the friendly metric title from provider
91832
- headerName = titleOverrides.metrics[metric.fullPath] || metric.metricTitle || fieldName;
91833
- headerName = interpolateTitle(headerName, parameters);
91834
- cols.push({
91835
- field: fieldName,
91836
- headerName: headerName,
91837
- flex: 1,
91838
- minWidth: 150,
91839
- type: isNumericType(metricDef?.type) ? 'number' : 'string',
91840
- renderHeader: () => /*#__PURE__*/gn.createElement(Box, {
91841
- sx: {
91842
- display: 'flex',
91843
- alignItems: 'center',
91844
- gap: 0.5
91845
- }
91846
- }, /*#__PURE__*/gn.createElement(MetricSourceIcon, {
91847
- source: metricDef?.source
91848
- }), /*#__PURE__*/gn.createElement("span", null, headerName)),
91849
- valueFormatter: value => formatValue(value, metricDef)
91850
- });
91851
- });
91852
- return cols;
91853
- }, [dimensions, metrics, computedDimensions, titleOverrides, parameters]);
92012
+ // Generate columns dynamically, in the final resolved column order
92013
+ // (custom Column Order tab arrangement when defined, otherwise
92014
+ // dimensions/computed then metrics).
92015
+ const columns = gn.useMemo(() => columnItems.map(item => buildColumn(item, titleOverrides, parameters)), [columnItems, titleOverrides, parameters]);
91854
92016
 
91855
92017
  // Transform report data to rows with unique IDs.
91856
92018
  // Keys are normalised by replacing dots with underscores so that MUI DataGrid
@@ -91957,7 +92119,15 @@ const ReportBuilder = ({
91957
92119
  dimensions: [],
91958
92120
  metrics: [],
91959
92121
  filters: {},
91960
- computedDimensions: []
92122
+ computedDimensions: [],
92123
+ // Unified display/execution order across dimensions + computedDimensions.
92124
+ // Entries: { type: 'dimension', key: fullPath } | { type: 'computed', key: name }
92125
+ dimensionOrder: [],
92126
+ // Optional unified display order across dimensions + computedDimensions +
92127
+ // metrics, set on the Column Order tab. Empty means "not defined" — the
92128
+ // results grid falls back to dimensionOrder followed by metrics order.
92129
+ // Entries: { type: 'dimension' | 'computed' | 'metric', key }
92130
+ columnOrder: []
91961
92131
  });
91962
92132
  const [titleOverrides, setTitleOverrides] = d({
91963
92133
  dimensions: {},
@@ -92003,6 +92173,16 @@ const ReportBuilder = ({
92003
92173
  }
92004
92174
  }, [autoRun, reportLoaded]);
92005
92175
 
92176
+ // Re-run the currently displayed report when base_currency changes in context
92177
+ const currentBaseCurrency = reportingContext?.parameters?.base_currency;
92178
+ const prevBaseCurrencyRef = A$1(currentBaseCurrency);
92179
+ h(() => {
92180
+ if (reportData && prevBaseCurrencyRef.current !== undefined && prevBaseCurrencyRef.current !== currentBaseCurrency) {
92181
+ runReportWithPagination(currentPage, pageSize);
92182
+ }
92183
+ prevBaseCurrencyRef.current = currentBaseCurrency;
92184
+ }, [currentBaseCurrency]);
92185
+
92006
92186
  // Utility function to reconstruct dimension object from fullPath
92007
92187
  const reconstructDimensionFromPath = (fullPath, providersData, rootProvider) => {
92008
92188
  try {
@@ -92132,6 +92312,92 @@ const ReportBuilder = ({
92132
92312
  return null;
92133
92313
  }
92134
92314
  };
92315
+
92316
+ // Build the unified display/execution order for dimensions + computed
92317
+ // dimensions. Prefers the persisted `ordered_dimensions` field; falls back
92318
+ // to "dimensions first, then computed dimensions" for report definitions
92319
+ // saved before this field existed.
92320
+ const buildDimensionOrder = (rawOrderedDimensions, dimensions, computedDimensions) => {
92321
+ const dimensionOrder = [];
92322
+ const seenDimensionKeys = new Set();
92323
+ const seenComputedKeys = new Set();
92324
+ if (Array.isArray(rawOrderedDimensions)) {
92325
+ rawOrderedDimensions.forEach(entry => {
92326
+ if (entry.type === "dimension" && dimensions.some(d => d.fullPath === entry.ref)) {
92327
+ dimensionOrder.push({
92328
+ type: "dimension",
92329
+ key: entry.ref
92330
+ });
92331
+ seenDimensionKeys.add(entry.ref);
92332
+ } else if (entry.type === "computed" && computedDimensions.some(cd => cd.name === entry.ref)) {
92333
+ dimensionOrder.push({
92334
+ type: "computed",
92335
+ key: entry.ref
92336
+ });
92337
+ seenComputedKeys.add(entry.ref);
92338
+ }
92339
+ });
92340
+ }
92341
+
92342
+ // Append anything not covered by ordered_dimensions (older reports, or drift)
92343
+ dimensions.forEach(d => {
92344
+ if (!seenDimensionKeys.has(d.fullPath)) {
92345
+ dimensionOrder.push({
92346
+ type: "dimension",
92347
+ key: d.fullPath
92348
+ });
92349
+ }
92350
+ });
92351
+ computedDimensions.forEach(cd => {
92352
+ if (!seenComputedKeys.has(cd.name)) {
92353
+ dimensionOrder.push({
92354
+ type: "computed",
92355
+ key: cd.name
92356
+ });
92357
+ }
92358
+ });
92359
+ return dimensionOrder;
92360
+ };
92361
+
92362
+ // Build the optional unified column order (dimensions + computed
92363
+ // dimensions + metrics) from the persisted `ordered_columns` field.
92364
+ // Unlike buildDimensionOrder, entries that no longer resolve are simply
92365
+ // dropped rather than backfilled — an empty/partial result just means "no
92366
+ // custom order defined (yet)", which resolveColumnOrder treats as
92367
+ // "fall back to the default order".
92368
+ const buildColumnOrder = (rawOrderedColumns, dimensions, computedDimensions, metrics) => {
92369
+ if (!Array.isArray(rawOrderedColumns)) return [];
92370
+ const dimensionPaths = new Set(dimensions.map(d => d.fullPath));
92371
+ const computedNames = new Set(computedDimensions.map(cd => cd.name));
92372
+ const metricPaths = new Set(metrics.map(m => m.fullPath));
92373
+ const columnOrder = [];
92374
+ rawOrderedColumns.forEach(entry => {
92375
+ if (entry.type === "dimension" && dimensionPaths.has(entry.ref)) {
92376
+ columnOrder.push({
92377
+ type: "dimension",
92378
+ key: entry.ref
92379
+ });
92380
+ } else if (entry.type === "computed" && computedNames.has(entry.ref)) {
92381
+ columnOrder.push({
92382
+ type: "computed",
92383
+ key: entry.ref
92384
+ });
92385
+ } else if (entry.type === "metric" && metricPaths.has(entry.ref)) {
92386
+ columnOrder.push({
92387
+ type: "metric",
92388
+ key: entry.ref
92389
+ });
92390
+ }
92391
+ });
92392
+ return columnOrder;
92393
+ };
92394
+
92395
+ // Auto-append a newly added dimension/computed dimension/metric to the end
92396
+ // of columnOrder — but only once a custom order actually exists. While
92397
+ // columnOrder is empty (no custom order defined) it's left untouched, so
92398
+ // the results grid keeps falling back to the default dimensions-then-
92399
+ // metrics order until the user visits the Column Order tab.
92400
+ const appendToColumnOrder = (columnOrder, entry) => columnOrder.length > 0 ? [...columnOrder, entry] : columnOrder;
92135
92401
  const loadReportDefinition = async id => {
92136
92402
  try {
92137
92403
  console.log("Loading report definition:", id);
@@ -92227,13 +92493,17 @@ const ReportBuilder = ({
92227
92493
  // Computed dimensions are self-contained { name, value } pairs with no
92228
92494
  // provider/relation chain, so they're loaded as-is (no reconstruction needed)
92229
92495
  const computedDimensions = reportDef.definition?.doc?.query?.computed_dimensions || [];
92496
+ const dimensionOrder = buildDimensionOrder(reportDef.definition?.doc?.query?.ordered_dimensions, reconstructedDimensions, computedDimensions);
92497
+ const columnOrder = buildColumnOrder(reportDef.definition?.doc?.query?.ordered_columns, reconstructedDimensions, computedDimensions, reconstructedMetrics);
92230
92498
 
92231
92499
  // Set the report state
92232
92500
  setReport({
92233
92501
  dimensions: reconstructedDimensions,
92234
92502
  metrics: reconstructedMetrics,
92235
92503
  filters: loadedFilters,
92236
- computedDimensions
92504
+ computedDimensions,
92505
+ dimensionOrder,
92506
+ columnOrder
92237
92507
  });
92238
92508
 
92239
92509
  // Set title overrides
@@ -92342,13 +92612,17 @@ const ReportBuilder = ({
92342
92612
  // Computed dimensions are self-contained { name, value } pairs with no
92343
92613
  // provider/relation chain, so they're loaded as-is (no reconstruction needed)
92344
92614
  const computedDimensions = reportDef.definition?.doc?.query?.computed_dimensions || [];
92615
+ const dimensionOrder = buildDimensionOrder(reportDef.definition?.doc?.query?.ordered_dimensions, reconstructedDimensions, computedDimensions);
92616
+ const columnOrder = buildColumnOrder(reportDef.definition?.doc?.query?.ordered_columns, reconstructedDimensions, computedDimensions, reconstructedMetrics);
92345
92617
 
92346
92618
  // Set the report state
92347
92619
  setReport({
92348
92620
  dimensions: reconstructedDimensions,
92349
92621
  metrics: reconstructedMetrics,
92350
92622
  filters: loadedFilters,
92351
- computedDimensions
92623
+ computedDimensions,
92624
+ dimensionOrder,
92625
+ columnOrder
92352
92626
  });
92353
92627
 
92354
92628
  // Set title overrides
@@ -92381,7 +92655,9 @@ const ReportBuilder = ({
92381
92655
  dimensions: [],
92382
92656
  metrics: [],
92383
92657
  filters: {},
92384
- computedDimensions: []
92658
+ computedDimensions: [],
92659
+ dimensionOrder: [],
92660
+ columnOrder: []
92385
92661
  });
92386
92662
  // Reset title overrides
92387
92663
  setTitleOverrides({
@@ -92458,58 +92734,93 @@ const ReportBuilder = ({
92458
92734
  setReport(prev => {
92459
92735
  const newReport = {
92460
92736
  ...prev,
92461
- dimensions: [...prev.dimensions, dimensionData]
92737
+ dimensions: [...prev.dimensions, dimensionData],
92738
+ dimensionOrder: [...prev.dimensionOrder, {
92739
+ type: "dimension",
92740
+ key: dimensionData.fullPath
92741
+ }],
92742
+ columnOrder: appendToColumnOrder(prev.columnOrder, {
92743
+ type: "dimension",
92744
+ key: dimensionData.fullPath
92745
+ })
92462
92746
  };
92463
92747
  console.log("Dimension saved:", dimensionData);
92464
92748
  console.log("Complete report:", newReport);
92465
92749
  return newReport;
92466
92750
  });
92467
92751
  };
92468
- const handleRemoveDimension = index => {
92752
+ const handleRemoveDimension = fullPath => {
92469
92753
  setReport(prev => ({
92470
92754
  ...prev,
92471
- dimensions: prev.dimensions.filter((_, i) => i !== index)
92755
+ dimensions: prev.dimensions.filter(d => d.fullPath !== fullPath),
92756
+ dimensionOrder: prev.dimensionOrder.filter(entry => !(entry.type === "dimension" && entry.key === fullPath)),
92757
+ columnOrder: prev.columnOrder.filter(entry => !(entry.type === "dimension" && entry.key === fullPath))
92472
92758
  }));
92473
92759
  };
92474
- const handleReorderDimensions = newOrder => {
92760
+ const handleUpdateDimensionSortOrder = (fullPath, sortOrder) => {
92475
92761
  setReport(prev => ({
92476
92762
  ...prev,
92477
- dimensions: newOrder
92763
+ dimensions: prev.dimensions.map(d => d.fullPath === fullPath ? {
92764
+ ...d,
92765
+ sortOrder
92766
+ } : d)
92478
92767
  }));
92479
92768
  };
92480
- const handleSaveComputedDimension = computedDimensionData => {
92769
+
92770
+ // Reorders the single unified dimensions + computed-dimensions list;
92771
+ // `dimensions`/`computedDimensions` arrays themselves are unordered from
92772
+ // this perspective, so only `dimensionOrder` needs to change.
92773
+ const handleReorderDimensionItems = newDimensionOrder => {
92481
92774
  setReport(prev => ({
92482
92775
  ...prev,
92483
- computedDimensions: [...prev.computedDimensions, computedDimensionData]
92776
+ dimensionOrder: newDimensionOrder
92484
92777
  }));
92485
92778
  };
92486
- const handleUpdateComputedDimension = (index, computedDimensionData) => {
92487
- setReport(prev => {
92488
- const newComputedDimensions = [...prev.computedDimensions];
92489
- newComputedDimensions[index] = computedDimensionData;
92490
- return {
92491
- ...prev,
92492
- computedDimensions: newComputedDimensions
92493
- };
92494
- });
92779
+ const handleSaveComputedDimension = computedDimensionData => {
92780
+ setReport(prev => ({
92781
+ ...prev,
92782
+ computedDimensions: [...prev.computedDimensions, computedDimensionData],
92783
+ dimensionOrder: [...prev.dimensionOrder, {
92784
+ type: "computed",
92785
+ key: computedDimensionData.name
92786
+ }],
92787
+ columnOrder: appendToColumnOrder(prev.columnOrder, {
92788
+ type: "computed",
92789
+ key: computedDimensionData.name
92790
+ })
92791
+ }));
92495
92792
  };
92496
- const handleRemoveComputedDimension = index => {
92793
+ const handleUpdateComputedDimension = (name, computedDimensionData) => {
92497
92794
  setReport(prev => ({
92498
92795
  ...prev,
92499
- computedDimensions: prev.computedDimensions.filter((_, i) => i !== index)
92796
+ computedDimensions: prev.computedDimensions.map(cd => cd.name === name ? computedDimensionData : cd),
92797
+ dimensionOrder: prev.dimensionOrder.map(entry => entry.type === "computed" && entry.key === name ? {
92798
+ ...entry,
92799
+ key: computedDimensionData.name
92800
+ } : entry),
92801
+ columnOrder: prev.columnOrder.map(entry => entry.type === "computed" && entry.key === name ? {
92802
+ ...entry,
92803
+ key: computedDimensionData.name
92804
+ } : entry)
92500
92805
  }));
92501
92806
  };
92502
- const handleReorderComputedDimensions = newOrder => {
92807
+ const handleRemoveComputedDimension = name => {
92503
92808
  setReport(prev => ({
92504
92809
  ...prev,
92505
- computedDimensions: newOrder
92810
+ computedDimensions: prev.computedDimensions.filter(cd => cd.name !== name),
92811
+ dimensionOrder: prev.dimensionOrder.filter(entry => !(entry.type === "computed" && entry.key === name)),
92812
+ columnOrder: prev.columnOrder.filter(entry => !(entry.type === "computed" && entry.key === name))
92506
92813
  }));
92507
92814
  };
92508
92815
  const handleSaveMetric = metricData => {
92509
92816
  setReport(prev => {
92510
92817
  const newReport = {
92511
92818
  ...prev,
92512
- metrics: [...prev.metrics, metricData]
92819
+ metrics: [...prev.metrics, metricData],
92820
+ columnOrder: appendToColumnOrder(prev.columnOrder, {
92821
+ type: "metric",
92822
+ key: metricData.fullPath
92823
+ })
92513
92824
  };
92514
92825
  console.log("Metric saved:", metricData);
92515
92826
  console.log("Complete report:", newReport);
@@ -92517,15 +92828,29 @@ const ReportBuilder = ({
92517
92828
  });
92518
92829
  };
92519
92830
  const handleRemoveMetric = index => {
92831
+ setReport(prev => {
92832
+ const fullPath = prev.metrics[index]?.fullPath;
92833
+ return {
92834
+ ...prev,
92835
+ metrics: prev.metrics.filter((_, i) => i !== index),
92836
+ columnOrder: prev.columnOrder.filter(entry => !(entry.type === "metric" && entry.key === fullPath))
92837
+ };
92838
+ });
92839
+ };
92840
+ const handleReorderMetrics = newOrder => {
92520
92841
  setReport(prev => ({
92521
92842
  ...prev,
92522
- metrics: prev.metrics.filter((_, i) => i !== index)
92843
+ metrics: newOrder
92523
92844
  }));
92524
92845
  };
92525
- const handleReorderMetrics = newOrder => {
92846
+
92847
+ // Sets the unified column order shown on the Column Order tab. An empty
92848
+ // array (e.g. via "Reset to Default") clears the customization, and the
92849
+ // results grid falls back to dimensionOrder followed by metrics order.
92850
+ const handleReorderColumnItems = newColumnOrder => {
92526
92851
  setReport(prev => ({
92527
92852
  ...prev,
92528
- metrics: newOrder
92853
+ columnOrder: newColumnOrder
92529
92854
  }));
92530
92855
  };
92531
92856
  const handleSaveFilter = (fullPath, filterData) => {
@@ -92558,10 +92883,20 @@ const ReportBuilder = ({
92558
92883
  });
92559
92884
  };
92560
92885
 
92561
- // Convert internal report structure to API format
92562
- const convertReportToApiFormat = (page = 0, size = 50) => {
92886
+ // Builds the query object shared between running (paginated) and saving
92887
+ // (unpaginated) a report. Dimension and computed-dimension order both
92888
+ // derive from the single unified `report.dimensionOrder` list, so the
92889
+ // saved `dimensions`/`computed_dimensions` arrays, `order_by`, and the
92890
+ // persisted `ordered_dimensions` field all stay consistent with whatever
92891
+ // order the user arranged on screen.
92892
+ const buildQueryObject = () => {
92893
+ const dimensionByPath = Object.fromEntries(report.dimensions.map(d => [d.fullPath, d]));
92894
+ const computedByName = Object.fromEntries(report.computedDimensions.map(cd => [cd.name, cd]));
92895
+ const orderedDimensionsOnly = report.dimensionOrder.filter(entry => entry.type === "dimension").map(entry => dimensionByPath[entry.key]).filter(Boolean);
92896
+ const orderedComputedOnly = report.dimensionOrder.filter(entry => entry.type === "computed").map(entry => computedByName[entry.key]).filter(Boolean);
92897
+
92563
92898
  // Build order_by array - include all dimensions, add desc property only when needed
92564
- const orderBy = report.dimensions.map(dim => {
92899
+ const orderBy = orderedDimensionsOnly.map(dim => {
92565
92900
  const orderItem = {
92566
92901
  name: dim.fullPath
92567
92902
  };
@@ -92617,21 +92952,40 @@ const ReportBuilder = ({
92617
92952
  titles.filters = titleOverrides.filters;
92618
92953
  }
92619
92954
  const queryObj = {
92620
- dimensions: report.dimensions.map(dim => dim.fullPath),
92955
+ dimensions: orderedDimensionsOnly.map(dim => dim.fullPath),
92621
92956
  metrics: report.metrics.map(metric => metric.fullPath),
92622
- order_by: orderBy,
92623
- limit: size,
92624
- offset: page * size
92957
+ order_by: orderBy
92625
92958
  };
92626
92959
 
92627
92960
  // Only add computed_dimensions if there are any
92628
- if (report.computedDimensions && report.computedDimensions.length > 0) {
92629
- queryObj.computed_dimensions = report.computedDimensions.map(cd => ({
92961
+ if (orderedComputedOnly.length > 0) {
92962
+ queryObj.computed_dimensions = orderedComputedOnly.map(cd => ({
92630
92963
  name: cd.name,
92631
92964
  value: cd.value
92632
92965
  }));
92633
92966
  }
92634
92967
 
92968
+ // Persist the unified dimension + computed-dimension order so it can be
92969
+ // reconstructed on reload and so executed reports render columns in the
92970
+ // same order they were arranged in the builder.
92971
+ if (report.dimensionOrder.length > 0) {
92972
+ queryObj.ordered_dimensions = report.dimensionOrder.map(entry => ({
92973
+ type: entry.type,
92974
+ ref: entry.key
92975
+ }));
92976
+ }
92977
+
92978
+ // Persist the optional unified column order (Column Order tab) so the
92979
+ // results grid can reproduce it on reload. Absent when no custom order
92980
+ // has been defined, so old/untouched reports keep their current
92981
+ // dimensions-then-metrics column order unchanged.
92982
+ if (report.columnOrder.length > 0) {
92983
+ queryObj.ordered_columns = report.columnOrder.map(entry => ({
92984
+ type: entry.type,
92985
+ ref: entry.key
92986
+ }));
92987
+ }
92988
+
92635
92989
  // Only add titles if there are any overrides
92636
92990
  if (Object.keys(titles).length > 0) {
92637
92991
  queryObj.titles = titles;
@@ -92641,6 +92995,14 @@ const ReportBuilder = ({
92641
92995
  if (filter) {
92642
92996
  queryObj.filter = filter;
92643
92997
  }
92998
+ return queryObj;
92999
+ };
93000
+
93001
+ // Convert internal report structure to API format
93002
+ const convertReportToApiFormat = (page = 0, size = 50) => {
93003
+ const queryObj = buildQueryObject();
93004
+ queryObj.limit = size;
93005
+ queryObj.offset = page * size;
92644
93006
 
92645
93007
  // Get parameters from context if available, otherwise use default
92646
93008
  const parameters = reportingContext?.parameters || {
@@ -92683,8 +93045,8 @@ const ReportBuilder = ({
92683
93045
  const handleRunReport = async () => {
92684
93046
  setCurrentPage(0);
92685
93047
  await runReportWithPagination(0, pageSize);
92686
- // Switch to Results tab after running report (now index 3 after adding Filters tab)
92687
- setActiveTab(3);
93048
+ // Switch to Results tab after running report (index 4: Dimensions, Metrics, Filters, Column Order, Results)
93049
+ setActiveTab(4);
92688
93050
  };
92689
93051
  const handleDownloadReport = async () => {
92690
93052
  try {
@@ -92734,77 +93096,7 @@ const ReportBuilder = ({
92734
93096
  }
92735
93097
  try {
92736
93098
  setSaving(true);
92737
-
92738
- // Build order_by array - include all dimensions, add desc property only when needed
92739
- const orderBy = report.dimensions.map(dim => {
92740
- const orderItem = {
92741
- name: dim.fullPath
92742
- };
92743
- if (dim.sortOrder === "desc") {
92744
- orderItem.desc = true;
92745
- }
92746
- // For 'asc' or null, just include { name: fullPath } without desc property
92747
- return orderItem;
92748
- });
92749
-
92750
- // Build titles object - only include overridden titles
92751
- const titles = {};
92752
- if (Object.keys(titleOverrides.dimensions).length > 0) {
92753
- titles.dimensions = titleOverrides.dimensions;
92754
- }
92755
- if (Object.keys(titleOverrides.metrics).length > 0) {
92756
- titles.metrics = titleOverrides.metrics;
92757
- }
92758
- if (Object.keys(titleOverrides.filters).length > 0) {
92759
- titles.filters = titleOverrides.filters;
92760
- }
92761
- const queryObj = {
92762
- dimensions: report.dimensions.map(dim => dim.fullPath),
92763
- metrics: report.metrics.map(metric => metric.fullPath),
92764
- order_by: orderBy
92765
- };
92766
-
92767
- // Only add computed_dimensions if there are any
92768
- if (report.computedDimensions && report.computedDimensions.length > 0) {
92769
- queryObj.computed_dimensions = report.computedDimensions.map(cd => ({
92770
- name: cd.name,
92771
- value: cd.value
92772
- }));
92773
- }
92774
-
92775
- // Only add titles if there are any overrides
92776
- if (Object.keys(titles).length > 0) {
92777
- queryObj.titles = titles;
92778
- }
92779
-
92780
- // Add filter if there are any - NEW API format with top-level 'and' operator
92781
- // NEW format: { and: [ { "ft.currency": ["USD", "EUR"] }, { "ft_ba.created_at": {gte: "2025-05-01", lte: "2025-12-31"} } ] }
92782
- if (Object.keys(report.filters).length > 0) {
92783
- const conditions = [];
92784
- Object.entries(report.filters).forEach(([fullPath, filterData]) => {
92785
- if (filterData.values) {
92786
- // Check if values is an object (date range) or array (regular filter)
92787
- if (Array.isArray(filterData.values)) {
92788
- // Regular filter - only add if array has values
92789
- if (filterData.values.length > 0) {
92790
- conditions.push({
92791
- [fullPath]: filterData.values
92792
- });
92793
- }
92794
- } else if (typeof filterData.values === "object") {
92795
- // Date range filter - add the object as-is
92796
- conditions.push({
92797
- [fullPath]: filterData.values
92798
- });
92799
- }
92800
- }
92801
- });
92802
- if (conditions.length > 0) {
92803
- queryObj.filter = {
92804
- and: conditions
92805
- };
92806
- }
92807
- }
93099
+ const queryObj = buildQueryObject();
92808
93100
 
92809
93101
  // Build the doc object
92810
93102
  const docObj = {
@@ -92849,6 +93141,23 @@ const ReportBuilder = ({
92849
93141
  }
92850
93142
  };
92851
93143
  const canSaveReport = selectedProvider && reportTitle.trim() && (report.dimensions.length > 0 || report.metrics.length > 0 || report.computedDimensions.length > 0);
93144
+
93145
+ // Resolve the unified dimensionOrder into actual dimension/computed-dimension
93146
+ // objects so the results grid can render columns in that same order.
93147
+ const dimensionByFullPath = Object.fromEntries(report.dimensions.map(d => [d.fullPath, d]));
93148
+ const computedDimensionByName = Object.fromEntries(report.computedDimensions.map(cd => [cd.name, cd]));
93149
+ const orderedDimensionItems = report.dimensionOrder.map(entry => entry.type === "dimension" ? {
93150
+ kind: "dimension",
93151
+ data: dimensionByFullPath[entry.key]
93152
+ } : {
93153
+ kind: "computed",
93154
+ data: computedDimensionByName[entry.key]
93155
+ }).filter(item => item.data);
93156
+
93157
+ // Final column list for the results grid: the explicit Column Order tab
93158
+ // arrangement when defined, otherwise dimensions/computed (in their own
93159
+ // order) followed by metrics (in their own order) — today's behavior.
93160
+ const columnItems = resolveColumnOrder(report.columnOrder, orderedDimensionItems, report.metrics);
92852
93161
  return /*#__PURE__*/gn.createElement(Box, {
92853
93162
  sx: {
92854
93163
  p: 3,
@@ -93103,9 +93412,36 @@ const ReportBuilder = ({
93103
93412
  }
93104
93413
  }
93105
93414
  }), /*#__PURE__*/gn.createElement(Tab, {
93106
- label: reportData ? "Results" : "Results (Run report first)",
93415
+ label: /*#__PURE__*/gn.createElement(Badge, {
93416
+ variant: "dot",
93417
+ invisible: report.columnOrder.length === 0,
93418
+ sx: {
93419
+ "& .MuiBadge-dot": {
93420
+ backgroundColor: "rgb(70, 134, 128)"
93421
+ }
93422
+ }
93423
+ }, /*#__PURE__*/gn.createElement("span", {
93424
+ style: {
93425
+ marginRight: report.columnOrder.length > 0 ? "8px" : "0"
93426
+ }
93427
+ }, "Column Order")),
93107
93428
  id: "report-tab-3",
93108
93429
  "aria-controls": "report-tabpanel-3",
93430
+ sx: {
93431
+ height: "41px",
93432
+ fontFamily: "system-ui",
93433
+ borderRadius: "0.5rem",
93434
+ boxShadow: "none",
93435
+ textTransform: "none",
93436
+ color: "rgb(37, 37, 37)",
93437
+ "&.Mui-selected": {
93438
+ color: "rgb(70, 134, 128)"
93439
+ }
93440
+ }
93441
+ }), /*#__PURE__*/gn.createElement(Tab, {
93442
+ label: reportData ? "Results" : "Results (Run report first)",
93443
+ id: "report-tab-4",
93444
+ "aria-controls": "report-tabpanel-4",
93109
93445
  disabled: !reportData,
93110
93446
  sx: {
93111
93447
  height: "41px",
@@ -93127,7 +93463,7 @@ const ReportBuilder = ({
93127
93463
  savedDimensions: report.dimensions,
93128
93464
  onSaveDimension: handleSaveDimension,
93129
93465
  onRemoveDimension: handleRemoveDimension,
93130
- onReorderDimensions: handleReorderDimensions,
93466
+ onUpdateDimensionSortOrder: handleUpdateDimensionSortOrder,
93131
93467
  titleOverrides: titleOverrides.dimensions,
93132
93468
  onUpdateTitle: handleUpdateDimensionTitle,
93133
93469
  onResetTitle: handleResetDimensionTitle,
@@ -93137,7 +93473,8 @@ const ReportBuilder = ({
93137
93473
  onSaveComputedDimension: handleSaveComputedDimension,
93138
93474
  onUpdateComputedDimension: handleUpdateComputedDimension,
93139
93475
  onRemoveComputedDimension: handleRemoveComputedDimension,
93140
- onReorderComputedDimensions: handleReorderComputedDimensions
93476
+ dimensionOrder: report.dimensionOrder,
93477
+ onReorderDimensionItems: handleReorderDimensionItems
93141
93478
  })), /*#__PURE__*/gn.createElement(TabPanel, {
93142
93479
  value: activeTab,
93143
93480
  index: 1
@@ -93171,11 +93508,18 @@ const ReportBuilder = ({
93171
93508
  })), /*#__PURE__*/gn.createElement(TabPanel, {
93172
93509
  value: activeTab,
93173
93510
  index: 3
93511
+ }, /*#__PURE__*/gn.createElement(ColumnOrder, {
93512
+ orderedDimensionItems: orderedDimensionItems,
93513
+ metrics: report.metrics,
93514
+ columnOrder: report.columnOrder,
93515
+ onReorderColumnItems: handleReorderColumnItems,
93516
+ titleOverrides: titleOverrides
93517
+ })), /*#__PURE__*/gn.createElement(TabPanel, {
93518
+ value: activeTab,
93519
+ index: 4
93174
93520
  }, reportData && /*#__PURE__*/gn.createElement(ReportDataGrid, {
93175
93521
  reportData: reportData,
93176
- dimensions: report.dimensions,
93177
- metrics: report.metrics,
93178
- computedDimensions: report.computedDimensions,
93522
+ columnItems: columnItems,
93179
93523
  loading: loading,
93180
93524
  onPageChange: handlePageChange,
93181
93525
  totalRows: totalRows,
@@ -93238,6 +93582,50 @@ const ReportDefinitionsManager = () => {
93238
93582
  });
93239
93583
  };
93240
93584
 
93585
+ // TODO: replace with currencies fetched from the API once that's available.
93586
+ const CURRENCY_OPTIONS = [{
93587
+ key: "USD",
93588
+ value: "USD"
93589
+ }, {
93590
+ key: "EUR",
93591
+ value: "EUR"
93592
+ }, {
93593
+ key: "SEK",
93594
+ value: "SEK"
93595
+ }, {
93596
+ key: "GBP",
93597
+ value: "GBP"
93598
+ }, {
93599
+ key: "DKK",
93600
+ value: "DKK"
93601
+ }, {
93602
+ key: "NOK",
93603
+ value: "NOK"
93604
+ }];
93605
+ const CurrencySelector = () => {
93606
+ const {
93607
+ parameters,
93608
+ setParameters
93609
+ } = useReportingContext();
93610
+
93611
+ // Hidden by default; only shown when a `cur` query parameter is present in the top-level URL.
93612
+ const showCurrencySelector = new URLSearchParams(window.location.search).has("cur");
93613
+ if (!showCurrencySelector) {
93614
+ return null;
93615
+ }
93616
+ return /*#__PURE__*/gn.createElement(SingleSelect, {
93617
+ items: CURRENCY_OPTIONS,
93618
+ value: parameters.base_currency,
93619
+ label: "Currency",
93620
+ onChange: e => setParameters({
93621
+ ...parameters,
93622
+ base_currency: e.target.value
93623
+ }),
93624
+ sx: {
93625
+ width: 110
93626
+ }
93627
+ });
93628
+ };
93241
93629
  const ReportApp = ({
93242
93630
  params,
93243
93631
  api
@@ -93304,7 +93692,13 @@ const ReportApp = ({
93304
93692
  }
93305
93693
  }
93306
93694
  })
93307
- }, /*#__PURE__*/gn.createElement(ReportDefinitionsManager, null))));
93695
+ }, /*#__PURE__*/gn.createElement(Box, {
93696
+ sx: {
93697
+ display: "flex",
93698
+ justifyContent: "flex-end",
93699
+ p: 1
93700
+ }
93701
+ }, /*#__PURE__*/gn.createElement(CurrencySelector, null)), /*#__PURE__*/gn.createElement(ReportDefinitionsManager, null))));
93308
93702
  };
93309
93703
 
93310
93704
  const Root = () => {