i(ot,rt))I[ht]=ot,I[Y]=rt,ht=Y;else break t}}return W}function i(I,W){var rt=I.sortIndex-W.sortIndex;return rt!==0?rt:I.id-W.id}if(e.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var a=performance;e.unstable_now=function(){return a.now()}}else{var o=Date,u=o.now();e.unstable_now=function(){return o.now()-u}}var c=[],h=[],g=1,B=null,m=3,v=!1,b=!1,y=!1,_=!1,C=typeof setTimeout=="function"?setTimeout:null,U=typeof clearTimeout=="function"?clearTimeout:null,E=typeof setImmediate<"u"?setImmediate:null;function O(I){for(var W=A(h);W!==null;){if(W.callback===null)n(h);else if(W.startTime<=I)n(h),W.sortIndex=W.expirationTime,t(c,W);else break;W=A(h)}}function F(I){if(y=!1,O(I),!b)if(A(c)!==null)b=!0,T||(T=!0,nt());else{var W=A(h);W!==null&&ut(F,W.startTime-I)}}var T=!1,k=-1,P=5,N=-1;function J(){return _?!0:!(e.unstable_now()-NI&&J());){var ht=B.callback;if(typeof ht=="function"){B.callback=null,m=B.priorityLevel;var H=ht(B.expirationTime<=I);if(I=e.unstable_now(),typeof H=="function"){B.callback=H,O(I),W=!0;break e}B===A(c)&&n(c),O(I)}else n(c);B=A(c)}if(B!==null)W=!0;else{var X=A(h);X!==null&&ut(F,X.startTime-I),W=!1}}break t}finally{B=null,m=rt,v=!1}W=void 0}}finally{W?nt():T=!1}}}var nt;if(typeof E=="function")nt=function(){E(et)};else if(typeof MessageChannel<"u"){var ct=new MessageChannel,bt=ct.port2;ct.port1.onmessage=et,nt=function(){bt.postMessage(null)}}else nt=function(){C(et,0)};function ut(I,W){k=C(function(){I(e.unstable_now())},W)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(I){I.callback=null},e.unstable_forceFrameRate=function(I){0>I||125ht?(I.sortIndex=rt,t(h,I),A(c)===null&&I===A(h)&&(y?(U(k),k=-1):y=!0,ut(F,rt-ht))):(I.sortIndex=H,t(c,I),b||v||(b=!0,T||(T=!0,nt()))),I},e.unstable_shouldYield=J,e.unstable_wrapCallback=function(I){var W=m;return function(){var rt=m;m=W;try{return I.apply(this,arguments)}finally{m=rt}}}}(Fg)),Fg}var fb;function h1(){return fb||(fb=1,Ug.exports=f1()),Ug.exports}var Eg={exports:{}},Ve={};/**
+ * @license React
+ * react-dom.production.js
+ *
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */var hb;function d1(){if(hb)return Ve;hb=1;var e=Hf();function t(c){var h="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),Eg.exports=d1(),Eg.exports}/**
+ * @license React
+ * react-dom-client.production.js
+ *
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */var gb;function g1(){if(gb)return go;gb=1;var e=h1(),t=Hf(),A=W_();function n(r){var s="https://react.dev/errors/"+r;if(1H||(r.current=ht[H],ht[H]=null,H--)}function q(r,s){H++,ht[H]=r.current,r.current=s}var Y=X(null),ot=X(null),st=X(null),be=X(null);function qt(r,s){switch(q(st,s),q(ot,r),q(Y,null),s.nodeType){case 9:case 11:r=(r=s.documentElement)&&(r=r.namespaceURI)?Mw(r):0;break;default:if(r=s.tagName,s=s.namespaceURI)s=Mw(s),r=Lw(s,r);else switch(r){case"svg":r=1;break;case"math":r=2;break;default:r=0}}tt(Y),q(Y,r)}function FA(){tt(Y),tt(ot),tt(st)}function da(r){r.memoizedState!==null&&q(be,r);var s=Y.current,l=Lw(s,r.type);s!==l&&(q(ot,r),q(Y,l))}function $r(r){ot.current===r&&(tt(Y),tt(ot)),be.current===r&&(tt(be),oo._currentValue=rt)}var ui=Object.prototype.hasOwnProperty,ci=e.unstable_scheduleCallback,ga=e.unstable_cancelCallback,dm=e.unstable_shouldYield,zF=e.unstable_requestPaint,an=e.unstable_now,VF=e.unstable_getCurrentPriorityLevel,gm=e.unstable_ImmediatePriority,pm=e.unstable_UserBlockingPriority,Ul=e.unstable_NormalPriority,PF=e.unstable_LowPriority,Bm=e.unstable_IdlePriority,jF=e.log,GF=e.unstable_setDisableYieldValue,pa=null,fA=null;function fi(r){if(typeof jF=="function"&&GF(r),fA&&typeof fA.setStrictMode=="function")try{fA.setStrictMode(pa,r)}catch{}}var hA=Math.clz32?Math.clz32:YF,XF=Math.log,ZF=Math.LN2;function YF(r){return r>>>=0,r===0?32:31-(XF(r)/ZF|0)|0}var Fl=256,El=4194304;function nr(r){var s=r&42;if(s!==0)return s;switch(r&-r){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return r&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return r&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return r}}function Sl(r,s,l){var f=r.pendingLanes;if(f===0)return 0;var d=0,p=r.suspendedLanes,w=r.pingedLanes;r=r.warmLanes;var Q=f&134217727;return Q!==0?(f=Q&~p,f!==0?d=nr(f):(w&=Q,w!==0?d=nr(w):l||(l=Q&~r,l!==0&&(d=nr(l))))):(Q=f&~p,Q!==0?d=nr(Q):w!==0?d=nr(w):l||(l=f&~r,l!==0&&(d=nr(l)))),d===0?0:s!==0&&s!==d&&(s&p)===0&&(p=d&-d,l=s&-s,p>=l||p===32&&(l&4194048)!==0)?s:d}function Ba(r,s){return(r.pendingLanes&~(r.suspendedLanes&~r.pingedLanes)&s)===0}function WF(r,s){switch(r){case 1:case 2:case 4:case 8:case 64:return s+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return s+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function mm(){var r=Fl;return Fl<<=1,(Fl&4194048)===0&&(Fl=256),r}function vm(){var r=El;return El<<=1,(El&62914560)===0&&(El=4194304),r}function hh(r){for(var s=[],l=0;31>l;l++)s.push(r);return s}function ma(r,s){r.pendingLanes|=s,s!==268435456&&(r.suspendedLanes=0,r.pingedLanes=0,r.warmLanes=0)}function $F(r,s,l,f,d,p){var w=r.pendingLanes;r.pendingLanes=l,r.suspendedLanes=0,r.pingedLanes=0,r.warmLanes=0,r.expiredLanes&=l,r.entangledLanes&=l,r.errorRecoveryDisabledLanes&=l,r.shellSuspendCounter=0;var Q=r.entanglements,S=r.expirationTimes,K=r.hiddenUpdates;for(l=w&~l;0)":-1d||S[f]!==K[d]){var j=`
+`+S[f].replace(" at new "," at ");return r.displayName&&j.includes("")&&(j=j.replace("",r.displayName)),j}while(1<=f&&0<=d);break}}}finally{vh=!1,Error.prepareStackTrace=l}return(l=r?r.displayName||r.name:"")?ns(l):""}function nE(r){switch(r.tag){case 26:case 27:case 5:return ns(r.type);case 16:return ns("Lazy");case 13:return ns("Suspense");case 19:return ns("SuspenseList");case 0:case 15:return wh(r.type,!1);case 11:return wh(r.type.render,!1);case 1:return wh(r.type,!0);case 31:return ns("Activity");default:return""}}function Em(r){try{var s="";do s+=nE(r),r=r.return;while(r);return s}catch(l){return`
+Error generating stack: `+l.message+`
+`+l.stack}}function EA(r){switch(typeof r){case"bigint":case"boolean":case"number":case"string":case"undefined":return r;case"object":return r;default:return""}}function Sm(r){var s=r.type;return(r=r.nodeName)&&r.toLowerCase()==="input"&&(s==="checkbox"||s==="radio")}function iE(r){var s=Sm(r)?"checked":"value",l=Object.getOwnPropertyDescriptor(r.constructor.prototype,s),f=""+r[s];if(!r.hasOwnProperty(s)&&typeof l<"u"&&typeof l.get=="function"&&typeof l.set=="function"){var d=l.get,p=l.set;return Object.defineProperty(r,s,{configurable:!0,get:function(){return d.call(this)},set:function(w){f=""+w,p.call(this,w)}}),Object.defineProperty(r,s,{enumerable:l.enumerable}),{getValue:function(){return f},setValue:function(w){f=""+w},stopTracking:function(){r._valueTracker=null,delete r[s]}}}}function Tl(r){r._valueTracker||(r._valueTracker=iE(r))}function Hm(r){if(!r)return!1;var s=r._valueTracker;if(!s)return!0;var l=s.getValue(),f="";return r&&(f=Sm(r)?r.checked?"true":"false":r.value),r=f,r!==l?(s.setValue(r),!0):!1}function Dl(r){if(r=r||(typeof document<"u"?document:void 0),typeof r>"u")return null;try{return r.activeElement||r.body}catch{return r.body}}var rE=/[\n"\\]/g;function SA(r){return r.replace(rE,function(s){return"\\"+s.charCodeAt(0).toString(16)+" "})}function bh(r,s,l,f,d,p,w,Q){r.name="",w!=null&&typeof w!="function"&&typeof w!="symbol"&&typeof w!="boolean"?r.type=w:r.removeAttribute("type"),s!=null?w==="number"?(s===0&&r.value===""||r.value!=s)&&(r.value=""+EA(s)):r.value!==""+EA(s)&&(r.value=""+EA(s)):w!=="submit"&&w!=="reset"||r.removeAttribute("value"),s!=null?yh(r,w,EA(s)):l!=null?yh(r,w,EA(l)):f!=null&&r.removeAttribute("value"),d==null&&p!=null&&(r.defaultChecked=!!p),d!=null&&(r.checked=d&&typeof d!="function"&&typeof d!="symbol"),Q!=null&&typeof Q!="function"&&typeof Q!="symbol"&&typeof Q!="boolean"?r.name=""+EA(Q):r.removeAttribute("name")}function Om(r,s,l,f,d,p,w,Q){if(p!=null&&typeof p!="function"&&typeof p!="symbol"&&typeof p!="boolean"&&(r.type=p),s!=null||l!=null){if(!(p!=="submit"&&p!=="reset"||s!=null))return;l=l!=null?""+EA(l):"",s=s!=null?""+EA(s):l,Q||s===r.value||(r.value=s),r.defaultValue=s}f=f??d,f=typeof f!="function"&&typeof f!="symbol"&&!!f,r.checked=Q?r.checked:!!f,r.defaultChecked=!!f,w!=null&&typeof w!="function"&&typeof w!="symbol"&&typeof w!="boolean"&&(r.name=w)}function yh(r,s,l){s==="number"&&Dl(r.ownerDocument)===r||r.defaultValue===""+l||(r.defaultValue=""+l)}function is(r,s,l,f){if(r=r.options,s){s={};for(var d=0;d"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Uh=!1;if(Fn)try{var ya={};Object.defineProperty(ya,"passive",{get:function(){Uh=!0}}),window.addEventListener("test",ya,ya),window.removeEventListener("test",ya,ya)}catch{Uh=!1}var di=null,Fh=null,Ll=null;function Nm(){if(Ll)return Ll;var r,s=Fh,l=s.length,f,d="value"in di?di.value:di.textContent,p=d.length;for(r=0;r=Qa),jm=" ",Gm=!1;function Xm(r,s){switch(r){case"keyup":return TE.indexOf(s.keyCode)!==-1;case"keydown":return s.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Zm(r){return r=r.detail,typeof r=="object"&&"data"in r?r.data:null}var os=!1;function ME(r,s){switch(r){case"compositionend":return Zm(s);case"keypress":return s.which!==32?null:(Gm=!0,jm);case"textInput":return r=s.data,r===jm&&Gm?null:r;default:return null}}function LE(r,s){if(os)return r==="compositionend"||!Th&&Xm(r,s)?(r=Nm(),Ll=Fh=di=null,os=!1,r):null;switch(r){case"paste":return null;case"keypress":if(!(s.ctrlKey||s.altKey||s.metaKey)||s.ctrlKey&&s.altKey){if(s.char&&1=s)return{node:l,offset:s-r};r=f}t:{for(;l;){if(l.nextSibling){l=l.nextSibling;break t}l=l.parentNode}l=void 0}l=A0(l)}}function i0(r,s){return r&&s?r===s?!0:r&&r.nodeType===3?!1:s&&s.nodeType===3?i0(r,s.parentNode):"contains"in r?r.contains(s):r.compareDocumentPosition?!!(r.compareDocumentPosition(s)&16):!1:!1}function r0(r){r=r!=null&&r.ownerDocument!=null&&r.ownerDocument.defaultView!=null?r.ownerDocument.defaultView:window;for(var s=Dl(r.document);s instanceof r.HTMLIFrameElement;){try{var l=typeof s.contentWindow.location.href=="string"}catch{l=!1}if(l)r=s.contentWindow;else break;s=Dl(r.document)}return s}function Lh(r){var s=r&&r.nodeName&&r.nodeName.toLowerCase();return s&&(s==="input"&&(r.type==="text"||r.type==="search"||r.type==="tel"||r.type==="url"||r.type==="password")||s==="textarea"||r.contentEditable==="true")}var PE=Fn&&"documentMode"in document&&11>=document.documentMode,ls=null,Ih=null,Ea=null,Rh=!1;function s0(r,s,l){var f=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;Rh||ls==null||ls!==Dl(f)||(f=ls,"selectionStart"in f&&Lh(f)?f={start:f.selectionStart,end:f.selectionEnd}:(f=(f.ownerDocument&&f.ownerDocument.defaultView||window).getSelection(),f={anchorNode:f.anchorNode,anchorOffset:f.anchorOffset,focusNode:f.focusNode,focusOffset:f.focusOffset}),Ea&&Fa(Ea,f)||(Ea=f,f=xu(Ih,"onSelect"),0>=w,d-=w,Sn=1<<32-hA(s)+d|l<p?p:8;var w=I.T,Q={};I.T=Q,Cd(r,!1,s,l);try{var S=d(),K=I.S;if(K!==null&&K(Q,S),S!==null&&typeof S=="object"&&typeof S.then=="function"){var j=qE(S,f);Pa(r,s,j,vA(r))}else Pa(r,s,f,vA(r))}catch($){Pa(r,s,{then:function(){},status:"rejected",reason:$},vA())}finally{W.p=p,I.T=w}}function iS(){}function bd(r,s,l,f){if(r.tag!==5)throw Error(n(476));var d=av(r).queue;sv(r,d,s,rt,l===null?iS:function(){return ov(r),l(f)})}function av(r){var s=r.memoizedState;if(s!==null)return s;s={memoizedState:rt,baseState:rt,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Dn,lastRenderedState:rt},next:null};var l={};return s.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Dn,lastRenderedState:l},next:null},r.memoizedState=s,r=r.alternate,r!==null&&(r.memoizedState=s),s}function ov(r){var s=av(r).next.queue;Pa(r,s,{},vA())}function yd(){return ze(oo)}function lv(){return Ce().memoizedState}function uv(){return Ce().memoizedState}function rS(r){for(var s=r.return;s!==null;){switch(s.tag){case 24:case 3:var l=vA();r=Bi(l);var f=mi(s,r,l);f!==null&&(wA(f,s,l),Ra(f,s,l)),s={cache:Jh()},r.payload=s;return}s=s.return}}function sS(r,s,l){var f=vA();l={lane:f,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null},su(r)?fv(s,l):(l=zh(r,s,l,f),l!==null&&(wA(l,r,f),hv(l,s,f)))}function cv(r,s,l){var f=vA();Pa(r,s,l,f)}function Pa(r,s,l,f){var d={lane:f,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null};if(su(r))fv(s,d);else{var p=r.alternate;if(r.lanes===0&&(p===null||p.lanes===0)&&(p=s.lastRenderedReducer,p!==null))try{var w=s.lastRenderedState,Q=p(w,l);if(d.hasEagerState=!0,d.eagerState=Q,dA(Q,w))return Vl(r,s,d,0),Yt===null&&zl(),!1}catch{}finally{}if(l=zh(r,s,d,f),l!==null)return wA(l,r,f),hv(l,s,f),!0}return!1}function Cd(r,s,l,f){if(f={lane:2,revertLane:eg(),action:f,hasEagerState:!1,eagerState:null,next:null},su(r)){if(s)throw Error(n(479))}else s=zh(r,l,f,2),s!==null&&wA(s,r,2)}function su(r){var s=r.alternate;return r===_t||s!==null&&s===_t}function fv(r,s){vs=tu=!0;var l=r.pending;l===null?s.next=s:(s.next=l.next,l.next=s),r.pending=s}function hv(r,s,l){if((l&4194048)!==0){var f=s.lanes;f&=r.pendingLanes,l|=f,s.lanes=l,bm(r,l)}}var au={readContext:ze,use:Au,useCallback:pe,useContext:pe,useEffect:pe,useImperativeHandle:pe,useLayoutEffect:pe,useInsertionEffect:pe,useMemo:pe,useReducer:pe,useRef:pe,useState:pe,useDebugValue:pe,useDeferredValue:pe,useTransition:pe,useSyncExternalStore:pe,useId:pe,useHostTransitionStatus:pe,useFormState:pe,useActionState:pe,useOptimistic:pe,useMemoCache:pe,useCacheRefresh:pe},dv={readContext:ze,use:Au,useCallback:function(r,s){return AA().memoizedState=[r,s===void 0?null:s],r},useContext:ze,useEffect:$0,useImperativeHandle:function(r,s,l){l=l!=null?l.concat([r]):null,ru(4194308,4,ev.bind(null,s,r),l)},useLayoutEffect:function(r,s){return ru(4194308,4,r,s)},useInsertionEffect:function(r,s){ru(4,2,r,s)},useMemo:function(r,s){var l=AA();s=s===void 0?null:s;var f=r();if(pr){fi(!0);try{r()}finally{fi(!1)}}return l.memoizedState=[f,s],f},useReducer:function(r,s,l){var f=AA();if(l!==void 0){var d=l(s);if(pr){fi(!0);try{l(s)}finally{fi(!1)}}}else d=s;return f.memoizedState=f.baseState=d,r={pending:null,lanes:0,dispatch:null,lastRenderedReducer:r,lastRenderedState:d},f.queue=r,r=r.dispatch=sS.bind(null,_t,r),[f.memoizedState,r]},useRef:function(r){var s=AA();return r={current:r},s.memoizedState=r},useState:function(r){r=Bd(r);var s=r.queue,l=cv.bind(null,_t,s);return s.dispatch=l,[r.memoizedState,l]},useDebugValue:vd,useDeferredValue:function(r,s){var l=AA();return wd(l,r,s)},useTransition:function(){var r=Bd(!1);return r=sv.bind(null,_t,r.queue,!0,!1),AA().memoizedState=r,[!1,r]},useSyncExternalStore:function(r,s,l){var f=_t,d=AA();if(Lt){if(l===void 0)throw Error(n(407));l=l()}else{if(l=s(),Yt===null)throw Error(n(349));(Ht&124)!==0||M0(f,s,l)}d.memoizedState=l;var p={value:l,getSnapshot:s};return d.queue=p,$0(I0.bind(null,f,p,r),[r]),f.flags|=2048,bs(9,iu(),L0.bind(null,f,p,l,s),null),l},useId:function(){var r=AA(),s=Yt.identifierPrefix;if(Lt){var l=Hn,f=Sn;l=(f&~(1<<32-hA(f)-1)).toString(32)+l,s="«"+s+"R"+l,l=eu++,0mt?(He=pt,pt=null):He=pt.sibling;var Tt=z(L,pt,R[mt],Z);if(Tt===null){pt===null&&(pt=He);break}r&&pt&&Tt.alternate===null&&s(L,pt),D=p(Tt,D,mt),Qt===null?lt=Tt:Qt.sibling=Tt,Qt=Tt,pt=He}if(mt===R.length)return l(L,pt),Lt&&ur(L,mt),lt;if(pt===null){for(;mtmt?(He=pt,pt=null):He=pt.sibling;var Mi=z(L,pt,Tt.value,Z);if(Mi===null){pt===null&&(pt=He);break}r&&pt&&Mi.alternate===null&&s(L,pt),D=p(Mi,D,mt),Qt===null?lt=Mi:Qt.sibling=Mi,Qt=Mi,pt=He}if(Tt.done)return l(L,pt),Lt&&ur(L,mt),lt;if(pt===null){for(;!Tt.done;mt++,Tt=R.next())Tt=$(L,Tt.value,Z),Tt!==null&&(D=p(Tt,D,mt),Qt===null?lt=Tt:Qt.sibling=Tt,Qt=Tt);return Lt&&ur(L,mt),lt}for(pt=f(pt);!Tt.done;mt++,Tt=R.next())Tt=V(pt,L,mt,Tt.value,Z),Tt!==null&&(r&&Tt.alternate!==null&&pt.delete(Tt.key===null?mt:Tt.key),D=p(Tt,D,mt),Qt===null?lt=Tt:Qt.sibling=Tt,Qt=Tt);return r&&pt.forEach(function(o1){return s(L,o1)}),Lt&&ur(L,mt),lt}function Vt(L,D,R,Z){if(typeof R=="object"&&R!==null&&R.type===b&&R.key===null&&(R=R.props.children),typeof R=="object"&&R!==null){switch(R.$$typeof){case m:t:{for(var lt=R.key;D!==null;){if(D.key===lt){if(lt=R.type,lt===b){if(D.tag===7){l(L,D.sibling),Z=d(D,R.props.children),Z.return=L,L=Z;break t}}else if(D.elementType===lt||typeof lt=="object"&<!==null&<.$$typeof===P&&pv(lt)===D.type){l(L,D.sibling),Z=d(D,R.props),Ga(Z,R),Z.return=L,L=Z;break t}l(L,D);break}else s(L,D);D=D.sibling}R.type===b?(Z=or(R.props.children,L.mode,Z,R.key),Z.return=L,L=Z):(Z=jl(R.type,R.key,R.props,null,L.mode,Z),Ga(Z,R),Z.return=L,L=Z)}return w(L);case v:t:{for(lt=R.key;D!==null;){if(D.key===lt)if(D.tag===4&&D.stateNode.containerInfo===R.containerInfo&&D.stateNode.implementation===R.implementation){l(L,D.sibling),Z=d(D,R.children||[]),Z.return=L,L=Z;break t}else{l(L,D);break}else s(L,D);D=D.sibling}Z=jh(R,L.mode,Z),Z.return=L,L=Z}return w(L);case P:return lt=R._init,R=lt(R._payload),Vt(L,D,R,Z)}if(ut(R))return vt(L,D,R,Z);if(nt(R)){if(lt=nt(R),typeof lt!="function")throw Error(n(150));return R=lt.call(R),Bt(L,D,R,Z)}if(typeof R.then=="function")return Vt(L,D,ou(R),Z);if(R.$$typeof===E)return Vt(L,D,Yl(L,R),Z);lu(L,R)}return typeof R=="string"&&R!==""||typeof R=="number"||typeof R=="bigint"?(R=""+R,D!==null&&D.tag===6?(l(L,D.sibling),Z=d(D,R),Z.return=L,L=Z):(l(L,D),Z=Ph(R,L.mode,Z),Z.return=L,L=Z),w(L)):l(L,D)}return function(L,D,R,Z){try{ja=0;var lt=Vt(L,D,R,Z);return ys=null,lt}catch(pt){if(pt===La||pt===$l)throw pt;var Qt=gA(29,pt,null,L.mode);return Qt.lanes=Z,Qt.return=L,Qt}finally{}}}var Cs=Bv(!0),mv=Bv(!1),MA=X(null),ln=null;function wi(r){var s=r.alternate;q(Qe,Qe.current&1),q(MA,r),ln===null&&(s===null||ms.current!==null||s.memoizedState!==null)&&(ln=r)}function vv(r){if(r.tag===22){if(q(Qe,Qe.current),q(MA,r),ln===null){var s=r.alternate;s!==null&&s.memoizedState!==null&&(ln=r)}}else bi()}function bi(){q(Qe,Qe.current),q(MA,MA.current)}function Mn(r){tt(MA),ln===r&&(ln=null),tt(Qe)}var Qe=X(0);function uu(r){for(var s=r;s!==null;){if(s.tag===13){var l=s.memoizedState;if(l!==null&&(l=l.dehydrated,l===null||l.data==="$?"||hg(l)))return s}else if(s.tag===19&&s.memoizedProps.revealOrder!==void 0){if((s.flags&128)!==0)return s}else if(s.child!==null){s.child.return=s,s=s.child;continue}if(s===r)break;for(;s.sibling===null;){if(s.return===null||s.return===r)return null;s=s.return}s.sibling.return=s.return,s=s.sibling}return null}function _d(r,s,l,f){s=r.memoizedState,l=l(f,s),l=l==null?s:g({},s,l),r.memoizedState=l,r.lanes===0&&(r.updateQueue.baseState=l)}var Qd={enqueueSetState:function(r,s,l){r=r._reactInternals;var f=vA(),d=Bi(f);d.payload=s,l!=null&&(d.callback=l),s=mi(r,d,f),s!==null&&(wA(s,r,f),Ra(s,r,f))},enqueueReplaceState:function(r,s,l){r=r._reactInternals;var f=vA(),d=Bi(f);d.tag=1,d.payload=s,l!=null&&(d.callback=l),s=mi(r,d,f),s!==null&&(wA(s,r,f),Ra(s,r,f))},enqueueForceUpdate:function(r,s){r=r._reactInternals;var l=vA(),f=Bi(l);f.tag=2,s!=null&&(f.callback=s),s=mi(r,f,l),s!==null&&(wA(s,r,l),Ra(s,r,l))}};function wv(r,s,l,f,d,p,w){return r=r.stateNode,typeof r.shouldComponentUpdate=="function"?r.shouldComponentUpdate(f,p,w):s.prototype&&s.prototype.isPureReactComponent?!Fa(l,f)||!Fa(d,p):!0}function bv(r,s,l,f){r=s.state,typeof s.componentWillReceiveProps=="function"&&s.componentWillReceiveProps(l,f),typeof s.UNSAFE_componentWillReceiveProps=="function"&&s.UNSAFE_componentWillReceiveProps(l,f),s.state!==r&&Qd.enqueueReplaceState(s,s.state,null)}function Br(r,s){var l=s;if("ref"in s){l={};for(var f in s)f!=="ref"&&(l[f]=s[f])}if(r=r.defaultProps){l===s&&(l=g({},l));for(var d in r)l[d]===void 0&&(l[d]=r[d])}return l}var cu=typeof reportError=="function"?reportError:function(r){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var s=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof r=="object"&&r!==null&&typeof r.message=="string"?String(r.message):String(r),error:r});if(!window.dispatchEvent(s))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",r);return}console.error(r)};function yv(r){cu(r)}function Cv(r){console.error(r)}function _v(r){cu(r)}function fu(r,s){try{var l=r.onUncaughtError;l(s.value,{componentStack:s.stack})}catch(f){setTimeout(function(){throw f})}}function Qv(r,s,l){try{var f=r.onCaughtError;f(l.value,{componentStack:l.stack,errorBoundary:s.tag===1?s.stateNode:null})}catch(d){setTimeout(function(){throw d})}}function xd(r,s,l){return l=Bi(l),l.tag=3,l.payload={element:null},l.callback=function(){fu(r,s)},l}function xv(r){return r=Bi(r),r.tag=3,r}function Uv(r,s,l,f){var d=l.type.getDerivedStateFromError;if(typeof d=="function"){var p=f.value;r.payload=function(){return d(p)},r.callback=function(){Qv(s,l,f)}}var w=l.stateNode;w!==null&&typeof w.componentDidCatch=="function"&&(r.callback=function(){Qv(s,l,f),typeof d!="function"&&(Ui===null?Ui=new Set([this]):Ui.add(this));var Q=f.stack;this.componentDidCatch(f.value,{componentStack:Q!==null?Q:""})})}function oS(r,s,l,f,d){if(l.flags|=32768,f!==null&&typeof f=="object"&&typeof f.then=="function"){if(s=l.alternate,s!==null&&Ta(s,l,d,!0),l=MA.current,l!==null){switch(l.tag){case 13:return ln===null?Wd():l.alternate===null&&he===0&&(he=3),l.flags&=-257,l.flags|=65536,l.lanes=d,f===ed?l.flags|=16384:(s=l.updateQueue,s===null?l.updateQueue=new Set([f]):s.add(f),Jd(r,f,d)),!1;case 22:return l.flags|=65536,f===ed?l.flags|=16384:(s=l.updateQueue,s===null?(s={transitions:null,markerInstances:null,retryQueue:new Set([f])},l.updateQueue=s):(l=s.retryQueue,l===null?s.retryQueue=new Set([f]):l.add(f)),Jd(r,f,d)),!1}throw Error(n(435,l.tag))}return Jd(r,f,d),Wd(),!1}if(Lt)return s=MA.current,s!==null?((s.flags&65536)===0&&(s.flags|=256),s.flags|=65536,s.lanes=d,f!==Zh&&(r=Error(n(422),{cause:f}),Oa(HA(r,l)))):(f!==Zh&&(s=Error(n(423),{cause:f}),Oa(HA(s,l))),r=r.current.alternate,r.flags|=65536,d&=-d,r.lanes|=d,f=HA(f,l),d=xd(r.stateNode,f,d),id(r,d),he!==4&&(he=2)),!1;var p=Error(n(520),{cause:f});if(p=HA(p,l),qa===null?qa=[p]:qa.push(p),he!==4&&(he=2),s===null)return!0;f=HA(f,l),l=s;do{switch(l.tag){case 3:return l.flags|=65536,r=d&-d,l.lanes|=r,r=xd(l.stateNode,f,r),id(l,r),!1;case 1:if(s=l.type,p=l.stateNode,(l.flags&128)===0&&(typeof s.getDerivedStateFromError=="function"||p!==null&&typeof p.componentDidCatch=="function"&&(Ui===null||!Ui.has(p))))return l.flags|=65536,d&=-d,l.lanes|=d,d=xv(d),Uv(d,r,l,f),id(l,d),!1}l=l.return}while(l!==null);return!1}var Fv=Error(n(461)),Ee=!1;function Me(r,s,l,f){s.child=r===null?mv(s,null,l,f):Cs(s,r.child,l,f)}function Ev(r,s,l,f,d){l=l.render;var p=s.ref;if("ref"in f){var w={};for(var Q in f)Q!=="ref"&&(w[Q]=f[Q])}else w=f;return dr(s),f=ld(r,s,l,w,p,d),Q=ud(),r!==null&&!Ee?(cd(r,s,d),Ln(r,s,d)):(Lt&&Q&&Gh(s),s.flags|=1,Me(r,s,f,d),s.child)}function Sv(r,s,l,f,d){if(r===null){var p=l.type;return typeof p=="function"&&!Vh(p)&&p.defaultProps===void 0&&l.compare===null?(s.tag=15,s.type=p,Hv(r,s,p,f,d)):(r=jl(l.type,null,f,s,s.mode,d),r.ref=s.ref,r.return=s,s.child=r)}if(p=r.child,!Dd(r,d)){var w=p.memoizedProps;if(l=l.compare,l=l!==null?l:Fa,l(w,f)&&r.ref===s.ref)return Ln(r,s,d)}return s.flags|=1,r=En(p,f),r.ref=s.ref,r.return=s,s.child=r}function Hv(r,s,l,f,d){if(r!==null){var p=r.memoizedProps;if(Fa(p,f)&&r.ref===s.ref)if(Ee=!1,s.pendingProps=f=p,Dd(r,d))(r.flags&131072)!==0&&(Ee=!0);else return s.lanes=r.lanes,Ln(r,s,d)}return Ud(r,s,l,f,d)}function Ov(r,s,l){var f=s.pendingProps,d=f.children,p=r!==null?r.memoizedState:null;if(f.mode==="hidden"){if((s.flags&128)!==0){if(f=p!==null?p.baseLanes|l:l,r!==null){for(d=s.child=r.child,p=0;d!==null;)p=p|d.lanes|d.childLanes,d=d.sibling;s.childLanes=p&~f}else s.childLanes=0,s.child=null;return Tv(r,s,f,l)}if((l&536870912)!==0)s.memoizedState={baseLanes:0,cachePool:null},r!==null&&Wl(s,p!==null?p.cachePool:null),p!==null?H0(s,p):sd(),vv(s);else return s.lanes=s.childLanes=536870912,Tv(r,s,p!==null?p.baseLanes|l:l,l)}else p!==null?(Wl(s,p.cachePool),H0(s,p),bi(),s.memoizedState=null):(r!==null&&Wl(s,null),sd(),bi());return Me(r,s,d,l),s.child}function Tv(r,s,l,f){var d=td();return d=d===null?null:{parent:_e._currentValue,pool:d},s.memoizedState={baseLanes:l,cachePool:d},r!==null&&Wl(s,null),sd(),vv(s),r!==null&&Ta(r,s,f,!0),null}function hu(r,s){var l=s.ref;if(l===null)r!==null&&r.ref!==null&&(s.flags|=4194816);else{if(typeof l!="function"&&typeof l!="object")throw Error(n(284));(r===null||r.ref!==l)&&(s.flags|=4194816)}}function Ud(r,s,l,f,d){return dr(s),l=ld(r,s,l,f,void 0,d),f=ud(),r!==null&&!Ee?(cd(r,s,d),Ln(r,s,d)):(Lt&&f&&Gh(s),s.flags|=1,Me(r,s,l,d),s.child)}function Dv(r,s,l,f,d,p){return dr(s),s.updateQueue=null,l=T0(s,f,l,d),O0(r),f=ud(),r!==null&&!Ee?(cd(r,s,p),Ln(r,s,p)):(Lt&&f&&Gh(s),s.flags|=1,Me(r,s,l,p),s.child)}function Mv(r,s,l,f,d){if(dr(s),s.stateNode===null){var p=hs,w=l.contextType;typeof w=="object"&&w!==null&&(p=ze(w)),p=new l(f,p),s.memoizedState=p.state!==null&&p.state!==void 0?p.state:null,p.updater=Qd,s.stateNode=p,p._reactInternals=s,p=s.stateNode,p.props=f,p.state=s.memoizedState,p.refs={},Ad(s),w=l.contextType,p.context=typeof w=="object"&&w!==null?ze(w):hs,p.state=s.memoizedState,w=l.getDerivedStateFromProps,typeof w=="function"&&(_d(s,l,w,f),p.state=s.memoizedState),typeof l.getDerivedStateFromProps=="function"||typeof p.getSnapshotBeforeUpdate=="function"||typeof p.UNSAFE_componentWillMount!="function"&&typeof p.componentWillMount!="function"||(w=p.state,typeof p.componentWillMount=="function"&&p.componentWillMount(),typeof p.UNSAFE_componentWillMount=="function"&&p.UNSAFE_componentWillMount(),w!==p.state&&Qd.enqueueReplaceState(p,p.state,null),Ka(s,f,p,d),Na(),p.state=s.memoizedState),typeof p.componentDidMount=="function"&&(s.flags|=4194308),f=!0}else if(r===null){p=s.stateNode;var Q=s.memoizedProps,S=Br(l,Q);p.props=S;var K=p.context,j=l.contextType;w=hs,typeof j=="object"&&j!==null&&(w=ze(j));var $=l.getDerivedStateFromProps;j=typeof $=="function"||typeof p.getSnapshotBeforeUpdate=="function",Q=s.pendingProps!==Q,j||typeof p.UNSAFE_componentWillReceiveProps!="function"&&typeof p.componentWillReceiveProps!="function"||(Q||K!==w)&&bv(s,p,f,w),pi=!1;var z=s.memoizedState;p.state=z,Ka(s,f,p,d),Na(),K=s.memoizedState,Q||z!==K||pi?(typeof $=="function"&&(_d(s,l,$,f),K=s.memoizedState),(S=pi||wv(s,l,S,f,z,K,w))?(j||typeof p.UNSAFE_componentWillMount!="function"&&typeof p.componentWillMount!="function"||(typeof p.componentWillMount=="function"&&p.componentWillMount(),typeof p.UNSAFE_componentWillMount=="function"&&p.UNSAFE_componentWillMount()),typeof p.componentDidMount=="function"&&(s.flags|=4194308)):(typeof p.componentDidMount=="function"&&(s.flags|=4194308),s.memoizedProps=f,s.memoizedState=K),p.props=f,p.state=K,p.context=w,f=S):(typeof p.componentDidMount=="function"&&(s.flags|=4194308),f=!1)}else{p=s.stateNode,nd(r,s),w=s.memoizedProps,j=Br(l,w),p.props=j,$=s.pendingProps,z=p.context,K=l.contextType,S=hs,typeof K=="object"&&K!==null&&(S=ze(K)),Q=l.getDerivedStateFromProps,(K=typeof Q=="function"||typeof p.getSnapshotBeforeUpdate=="function")||typeof p.UNSAFE_componentWillReceiveProps!="function"&&typeof p.componentWillReceiveProps!="function"||(w!==$||z!==S)&&bv(s,p,f,S),pi=!1,z=s.memoizedState,p.state=z,Ka(s,f,p,d),Na();var V=s.memoizedState;w!==$||z!==V||pi||r!==null&&r.dependencies!==null&&Zl(r.dependencies)?(typeof Q=="function"&&(_d(s,l,Q,f),V=s.memoizedState),(j=pi||wv(s,l,j,f,z,V,S)||r!==null&&r.dependencies!==null&&Zl(r.dependencies))?(K||typeof p.UNSAFE_componentWillUpdate!="function"&&typeof p.componentWillUpdate!="function"||(typeof p.componentWillUpdate=="function"&&p.componentWillUpdate(f,V,S),typeof p.UNSAFE_componentWillUpdate=="function"&&p.UNSAFE_componentWillUpdate(f,V,S)),typeof p.componentDidUpdate=="function"&&(s.flags|=4),typeof p.getSnapshotBeforeUpdate=="function"&&(s.flags|=1024)):(typeof p.componentDidUpdate!="function"||w===r.memoizedProps&&z===r.memoizedState||(s.flags|=4),typeof p.getSnapshotBeforeUpdate!="function"||w===r.memoizedProps&&z===r.memoizedState||(s.flags|=1024),s.memoizedProps=f,s.memoizedState=V),p.props=f,p.state=V,p.context=S,f=j):(typeof p.componentDidUpdate!="function"||w===r.memoizedProps&&z===r.memoizedState||(s.flags|=4),typeof p.getSnapshotBeforeUpdate!="function"||w===r.memoizedProps&&z===r.memoizedState||(s.flags|=1024),f=!1)}return p=f,hu(r,s),f=(s.flags&128)!==0,p||f?(p=s.stateNode,l=f&&typeof l.getDerivedStateFromError!="function"?null:p.render(),s.flags|=1,r!==null&&f?(s.child=Cs(s,r.child,null,d),s.child=Cs(s,null,l,d)):Me(r,s,l,d),s.memoizedState=p.state,r=s.child):r=Ln(r,s,d),r}function Lv(r,s,l,f){return Ha(),s.flags|=256,Me(r,s,l,f),s.child}var Fd={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Ed(r){return{baseLanes:r,cachePool:C0()}}function Sd(r,s,l){return r=r!==null?r.childLanes&~l:0,s&&(r|=LA),r}function Iv(r,s,l){var f=s.pendingProps,d=!1,p=(s.flags&128)!==0,w;if((w=p)||(w=r!==null&&r.memoizedState===null?!1:(Qe.current&2)!==0),w&&(d=!0,s.flags&=-129),w=(s.flags&32)!==0,s.flags&=-33,r===null){if(Lt){if(d?wi(s):bi(),Lt){var Q=fe,S;if(S=Q){t:{for(S=Q,Q=on;S.nodeType!==8;){if(!Q){Q=null;break t}if(S=ZA(S.nextSibling),S===null){Q=null;break t}}Q=S}Q!==null?(s.memoizedState={dehydrated:Q,treeContext:lr!==null?{id:Sn,overflow:Hn}:null,retryLane:536870912,hydrationErrors:null},S=gA(18,null,null,0),S.stateNode=Q,S.return=s,s.child=S,We=s,fe=null,S=!0):S=!1}S||fr(s)}if(Q=s.memoizedState,Q!==null&&(Q=Q.dehydrated,Q!==null))return hg(Q)?s.lanes=32:s.lanes=536870912,null;Mn(s)}return Q=f.children,f=f.fallback,d?(bi(),d=s.mode,Q=du({mode:"hidden",children:Q},d),f=or(f,d,l,null),Q.return=s,f.return=s,Q.sibling=f,s.child=Q,d=s.child,d.memoizedState=Ed(l),d.childLanes=Sd(r,w,l),s.memoizedState=Fd,f):(wi(s),Hd(s,Q))}if(S=r.memoizedState,S!==null&&(Q=S.dehydrated,Q!==null)){if(p)s.flags&256?(wi(s),s.flags&=-257,s=Od(r,s,l)):s.memoizedState!==null?(bi(),s.child=r.child,s.flags|=128,s=null):(bi(),d=f.fallback,Q=s.mode,f=du({mode:"visible",children:f.children},Q),d=or(d,Q,l,null),d.flags|=2,f.return=s,d.return=s,f.sibling=d,s.child=f,Cs(s,r.child,null,l),f=s.child,f.memoizedState=Ed(l),f.childLanes=Sd(r,w,l),s.memoizedState=Fd,s=d);else if(wi(s),hg(Q)){if(w=Q.nextSibling&&Q.nextSibling.dataset,w)var K=w.dgst;w=K,f=Error(n(419)),f.stack="",f.digest=w,Oa({value:f,source:null,stack:null}),s=Od(r,s,l)}else if(Ee||Ta(r,s,l,!1),w=(l&r.childLanes)!==0,Ee||w){if(w=Yt,w!==null&&(f=l&-l,f=(f&42)!==0?1:dh(f),f=(f&(w.suspendedLanes|l))!==0?0:f,f!==0&&f!==S.retryLane))throw S.retryLane=f,fs(r,f),wA(w,r,f),Fv;Q.data==="$?"||Wd(),s=Od(r,s,l)}else Q.data==="$?"?(s.flags|=192,s.child=r.child,s=null):(r=S.treeContext,fe=ZA(Q.nextSibling),We=s,Lt=!0,cr=null,on=!1,r!==null&&(TA[DA++]=Sn,TA[DA++]=Hn,TA[DA++]=lr,Sn=r.id,Hn=r.overflow,lr=s),s=Hd(s,f.children),s.flags|=4096);return s}return d?(bi(),d=f.fallback,Q=s.mode,S=r.child,K=S.sibling,f=En(S,{mode:"hidden",children:f.children}),f.subtreeFlags=S.subtreeFlags&65011712,K!==null?d=En(K,d):(d=or(d,Q,l,null),d.flags|=2),d.return=s,f.return=s,f.sibling=d,s.child=f,f=d,d=s.child,Q=r.child.memoizedState,Q===null?Q=Ed(l):(S=Q.cachePool,S!==null?(K=_e._currentValue,S=S.parent!==K?{parent:K,pool:K}:S):S=C0(),Q={baseLanes:Q.baseLanes|l,cachePool:S}),d.memoizedState=Q,d.childLanes=Sd(r,w,l),s.memoizedState=Fd,f):(wi(s),l=r.child,r=l.sibling,l=En(l,{mode:"visible",children:f.children}),l.return=s,l.sibling=null,r!==null&&(w=s.deletions,w===null?(s.deletions=[r],s.flags|=16):w.push(r)),s.child=l,s.memoizedState=null,l)}function Hd(r,s){return s=du({mode:"visible",children:s},r.mode),s.return=r,r.child=s}function du(r,s){return r=gA(22,r,null,s),r.lanes=0,r.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null},r}function Od(r,s,l){return Cs(s,r.child,null,l),r=Hd(s,s.pendingProps.children),r.flags|=2,s.memoizedState=null,r}function Rv(r,s,l){r.lanes|=s;var f=r.alternate;f!==null&&(f.lanes|=s),Wh(r.return,s,l)}function Td(r,s,l,f,d){var p=r.memoizedState;p===null?r.memoizedState={isBackwards:s,rendering:null,renderingStartTime:0,last:f,tail:l,tailMode:d}:(p.isBackwards=s,p.rendering=null,p.renderingStartTime=0,p.last=f,p.tail=l,p.tailMode=d)}function Nv(r,s,l){var f=s.pendingProps,d=f.revealOrder,p=f.tail;if(Me(r,s,f.children,l),f=Qe.current,(f&2)!==0)f=f&1|2,s.flags|=128;else{if(r!==null&&(r.flags&128)!==0)t:for(r=s.child;r!==null;){if(r.tag===13)r.memoizedState!==null&&Rv(r,l,s);else if(r.tag===19)Rv(r,l,s);else if(r.child!==null){r.child.return=r,r=r.child;continue}if(r===s)break t;for(;r.sibling===null;){if(r.return===null||r.return===s)break t;r=r.return}r.sibling.return=r.return,r=r.sibling}f&=1}switch(q(Qe,f),d){case"forwards":for(l=s.child,d=null;l!==null;)r=l.alternate,r!==null&&uu(r)===null&&(d=l),l=l.sibling;l=d,l===null?(d=s.child,s.child=null):(d=l.sibling,l.sibling=null),Td(s,!1,d,l,p);break;case"backwards":for(l=null,d=s.child,s.child=null;d!==null;){if(r=d.alternate,r!==null&&uu(r)===null){s.child=d;break}r=d.sibling,d.sibling=l,l=d,d=r}Td(s,!0,l,null,p);break;case"together":Td(s,!1,null,null,void 0);break;default:s.memoizedState=null}return s.child}function Ln(r,s,l){if(r!==null&&(s.dependencies=r.dependencies),xi|=s.lanes,(l&s.childLanes)===0)if(r!==null){if(Ta(r,s,l,!1),(l&s.childLanes)===0)return null}else return null;if(r!==null&&s.child!==r.child)throw Error(n(153));if(s.child!==null){for(r=s.child,l=En(r,r.pendingProps),s.child=l,l.return=s;r.sibling!==null;)r=r.sibling,l=l.sibling=En(r,r.pendingProps),l.return=s;l.sibling=null}return s.child}function Dd(r,s){return(r.lanes&s)!==0?!0:(r=r.dependencies,!!(r!==null&&Zl(r)))}function lS(r,s,l){switch(s.tag){case 3:qt(s,s.stateNode.containerInfo),gi(s,_e,r.memoizedState.cache),Ha();break;case 27:case 5:da(s);break;case 4:qt(s,s.stateNode.containerInfo);break;case 10:gi(s,s.type,s.memoizedProps.value);break;case 13:var f=s.memoizedState;if(f!==null)return f.dehydrated!==null?(wi(s),s.flags|=128,null):(l&s.child.childLanes)!==0?Iv(r,s,l):(wi(s),r=Ln(r,s,l),r!==null?r.sibling:null);wi(s);break;case 19:var d=(r.flags&128)!==0;if(f=(l&s.childLanes)!==0,f||(Ta(r,s,l,!1),f=(l&s.childLanes)!==0),d){if(f)return Nv(r,s,l);s.flags|=128}if(d=s.memoizedState,d!==null&&(d.rendering=null,d.tail=null,d.lastEffect=null),q(Qe,Qe.current),f)break;return null;case 22:case 23:return s.lanes=0,Ov(r,s,l);case 24:gi(s,_e,r.memoizedState.cache)}return Ln(r,s,l)}function Kv(r,s,l){if(r!==null)if(r.memoizedProps!==s.pendingProps)Ee=!0;else{if(!Dd(r,l)&&(s.flags&128)===0)return Ee=!1,lS(r,s,l);Ee=(r.flags&131072)!==0}else Ee=!1,Lt&&(s.flags&1048576)!==0&&p0(s,Xl,s.index);switch(s.lanes=0,s.tag){case 16:t:{r=s.pendingProps;var f=s.elementType,d=f._init;if(f=d(f._payload),s.type=f,typeof f=="function")Vh(f)?(r=Br(f,r),s.tag=1,s=Mv(null,s,f,r,l)):(s.tag=0,s=Ud(null,s,f,r,l));else{if(f!=null){if(d=f.$$typeof,d===O){s.tag=11,s=Ev(null,s,f,r,l);break t}else if(d===k){s.tag=14,s=Sv(null,s,f,r,l);break t}}throw s=bt(f)||f,Error(n(306,s,""))}}return s;case 0:return Ud(r,s,s.type,s.pendingProps,l);case 1:return f=s.type,d=Br(f,s.pendingProps),Mv(r,s,f,d,l);case 3:t:{if(qt(s,s.stateNode.containerInfo),r===null)throw Error(n(387));f=s.pendingProps;var p=s.memoizedState;d=p.element,nd(r,s),Ka(s,f,null,l);var w=s.memoizedState;if(f=w.cache,gi(s,_e,f),f!==p.cache&&$h(s,[_e],l,!0),Na(),f=w.element,p.isDehydrated)if(p={element:f,isDehydrated:!1,cache:w.cache},s.updateQueue.baseState=p,s.memoizedState=p,s.flags&256){s=Lv(r,s,f,l);break t}else if(f!==d){d=HA(Error(n(424)),s),Oa(d),s=Lv(r,s,f,l);break t}else{switch(r=s.stateNode.containerInfo,r.nodeType){case 9:r=r.body;break;default:r=r.nodeName==="HTML"?r.ownerDocument.body:r}for(fe=ZA(r.firstChild),We=s,Lt=!0,cr=null,on=!0,l=mv(s,null,f,l),s.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling}else{if(Ha(),f===d){s=Ln(r,s,l);break t}Me(r,s,f,l)}s=s.child}return s;case 26:return hu(r,s),r===null?(l=Pw(s.type,null,s.pendingProps,null))?s.memoizedState=l:Lt||(l=s.type,r=s.pendingProps,f=Fu(st.current).createElement(l),f[ke]=s,f[tA]=r,Ie(f,l,r),Fe(f),s.stateNode=f):s.memoizedState=Pw(s.type,r.memoizedProps,s.pendingProps,r.memoizedState),null;case 27:return da(s),r===null&&Lt&&(f=s.stateNode=kw(s.type,s.pendingProps,st.current),We=s,on=!0,d=fe,Si(s.type)?(dg=d,fe=ZA(f.firstChild)):fe=d),Me(r,s,s.pendingProps.children,l),hu(r,s),r===null&&(s.flags|=4194304),s.child;case 5:return r===null&&Lt&&((d=f=fe)&&(f=IS(f,s.type,s.pendingProps,on),f!==null?(s.stateNode=f,We=s,fe=ZA(f.firstChild),on=!1,d=!0):d=!1),d||fr(s)),da(s),d=s.type,p=s.pendingProps,w=r!==null?r.memoizedProps:null,f=p.children,ug(d,p)?f=null:w!==null&&ug(d,w)&&(s.flags|=32),s.memoizedState!==null&&(d=ld(r,s,eS,null,null,l),oo._currentValue=d),hu(r,s),Me(r,s,f,l),s.child;case 6:return r===null&&Lt&&((r=l=fe)&&(l=RS(l,s.pendingProps,on),l!==null?(s.stateNode=l,We=s,fe=null,r=!0):r=!1),r||fr(s)),null;case 13:return Iv(r,s,l);case 4:return qt(s,s.stateNode.containerInfo),f=s.pendingProps,r===null?s.child=Cs(s,null,f,l):Me(r,s,f,l),s.child;case 11:return Ev(r,s,s.type,s.pendingProps,l);case 7:return Me(r,s,s.pendingProps,l),s.child;case 8:return Me(r,s,s.pendingProps.children,l),s.child;case 12:return Me(r,s,s.pendingProps.children,l),s.child;case 10:return f=s.pendingProps,gi(s,s.type,f.value),Me(r,s,f.children,l),s.child;case 9:return d=s.type._context,f=s.pendingProps.children,dr(s),d=ze(d),f=f(d),s.flags|=1,Me(r,s,f,l),s.child;case 14:return Sv(r,s,s.type,s.pendingProps,l);case 15:return Hv(r,s,s.type,s.pendingProps,l);case 19:return Nv(r,s,l);case 31:return f=s.pendingProps,l=s.mode,f={mode:f.mode,children:f.children},r===null?(l=du(f,l),l.ref=s.ref,s.child=l,l.return=s,s=l):(l=En(r.child,f),l.ref=s.ref,s.child=l,l.return=s,s=l),s;case 22:return Ov(r,s,l);case 24:return dr(s),f=ze(_e),r===null?(d=td(),d===null&&(d=Yt,p=Jh(),d.pooledCache=p,p.refCount++,p!==null&&(d.pooledCacheLanes|=l),d=p),s.memoizedState={parent:f,cache:d},Ad(s),gi(s,_e,d)):((r.lanes&l)!==0&&(nd(r,s),Ka(s,null,null,l),Na()),d=r.memoizedState,p=s.memoizedState,d.parent!==f?(d={parent:f,cache:f},s.memoizedState=d,s.lanes===0&&(s.memoizedState=s.updateQueue.baseState=d),gi(s,_e,f)):(f=p.cache,gi(s,_e,f),f!==d.cache&&$h(s,[_e],l,!0))),Me(r,s,s.pendingProps.children,l),s.child;case 29:throw s.pendingProps}throw Error(n(156,s.tag))}function In(r){r.flags|=4}function kv(r,s){if(s.type!=="stylesheet"||(s.state.loading&4)!==0)r.flags&=-16777217;else if(r.flags|=16777216,!Yw(s)){if(s=MA.current,s!==null&&((Ht&4194048)===Ht?ln!==null:(Ht&62914560)!==Ht&&(Ht&536870912)===0||s!==ln))throw Ia=ed,_0;r.flags|=8192}}function gu(r,s){s!==null&&(r.flags|=4),r.flags&16384&&(s=r.tag!==22?vm():536870912,r.lanes|=s,Us|=s)}function Xa(r,s){if(!Lt)switch(r.tailMode){case"hidden":s=r.tail;for(var l=null;s!==null;)s.alternate!==null&&(l=s),s=s.sibling;l===null?r.tail=null:l.sibling=null;break;case"collapsed":l=r.tail;for(var f=null;l!==null;)l.alternate!==null&&(f=l),l=l.sibling;f===null?s||r.tail===null?r.tail=null:r.tail.sibling=null:f.sibling=null}}function le(r){var s=r.alternate!==null&&r.alternate.child===r.child,l=0,f=0;if(s)for(var d=r.child;d!==null;)l|=d.lanes|d.childLanes,f|=d.subtreeFlags&65011712,f|=d.flags&65011712,d.return=r,d=d.sibling;else for(d=r.child;d!==null;)l|=d.lanes|d.childLanes,f|=d.subtreeFlags,f|=d.flags,d.return=r,d=d.sibling;return r.subtreeFlags|=f,r.childLanes=l,s}function uS(r,s,l){var f=s.pendingProps;switch(Xh(s),s.tag){case 31:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return le(s),null;case 1:return le(s),null;case 3:return l=s.stateNode,f=null,r!==null&&(f=r.memoizedState.cache),s.memoizedState.cache!==f&&(s.flags|=2048),Tn(_e),FA(),l.pendingContext&&(l.context=l.pendingContext,l.pendingContext=null),(r===null||r.child===null)&&(Sa(s)?In(s):r===null||r.memoizedState.isDehydrated&&(s.flags&256)===0||(s.flags|=1024,v0())),le(s),null;case 26:return l=s.memoizedState,r===null?(In(s),l!==null?(le(s),kv(s,l)):(le(s),s.flags&=-16777217)):l?l!==r.memoizedState?(In(s),le(s),kv(s,l)):(le(s),s.flags&=-16777217):(r.memoizedProps!==f&&In(s),le(s),s.flags&=-16777217),null;case 27:$r(s),l=st.current;var d=s.type;if(r!==null&&s.stateNode!=null)r.memoizedProps!==f&&In(s);else{if(!f){if(s.stateNode===null)throw Error(n(166));return le(s),null}r=Y.current,Sa(s)?B0(s):(r=kw(d,f,l),s.stateNode=r,In(s))}return le(s),null;case 5:if($r(s),l=s.type,r!==null&&s.stateNode!=null)r.memoizedProps!==f&&In(s);else{if(!f){if(s.stateNode===null)throw Error(n(166));return le(s),null}if(r=Y.current,Sa(s))B0(s);else{switch(d=Fu(st.current),r){case 1:r=d.createElementNS("http://www.w3.org/2000/svg",l);break;case 2:r=d.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;default:switch(l){case"svg":r=d.createElementNS("http://www.w3.org/2000/svg",l);break;case"math":r=d.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;case"script":r=d.createElement("div"),r.innerHTML="
-
+
+
diff --git a/vite-app/package.json b/vite-app/package.json
index 5b120e20..bdb187e5 100644
--- a/vite-app/package.json
+++ b/vite-app/package.json
@@ -13,9 +13,12 @@
"test:coverage": "vitest run --coverage"
},
"dependencies": {
+ "chart.js": "^4.5.0",
+ "html2canvas-oklch": "1.5.0-alpha.0",
"mobx": "^6.13.7",
"mobx-react": "^9.2.0",
"react": "^19.1.0",
+ "react-chartjs-2": "^5.3.0",
"react-dom": "^19.1.0",
"react-router-dom": "^7.7.1",
"zod": "^4.0.14"
diff --git a/vite-app/pnpm-lock.yaml b/vite-app/pnpm-lock.yaml
index 60e1ea98..0ec51189 100644
--- a/vite-app/pnpm-lock.yaml
+++ b/vite-app/pnpm-lock.yaml
@@ -8,6 +8,12 @@ importers:
.:
dependencies:
+ chart.js:
+ specifier: ^4.5.0
+ version: 4.5.0
+ html2canvas-oklch:
+ specifier: 1.5.0-alpha.0
+ version: 1.5.0-alpha.0
mobx:
specifier: ^6.13.7
version: 6.13.7
@@ -17,6 +23,9 @@ importers:
react:
specifier: ^19.1.0
version: 19.1.1
+ react-chartjs-2:
+ specifier: ^5.3.0
+ version: 5.3.0(chart.js@4.5.0)(react@19.1.1)
react-dom:
specifier: ^19.1.0
version: 19.1.1(react@19.1.1)
@@ -408,6 +417,9 @@ packages:
'@jridgewell/trace-mapping@0.3.29':
resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
+ '@kurkle/color@0.3.4':
+ resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
+
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -797,6 +809,10 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ base64-arraybuffer@1.0.2:
+ resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
+ engines: {node: '>= 0.6.0'}
+
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
@@ -831,6 +847,10 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
+ chart.js@4.5.0:
+ resolution: {integrity: sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==}
+ engines: {pnpm: '>=8'}
+
check-error@2.1.1:
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
engines: {node: '>= 16'}
@@ -860,6 +880,9 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
+ css-line-break@2.1.0:
+ resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
+
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
@@ -1064,6 +1087,10 @@ packages:
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+ html2canvas-oklch@1.5.0-alpha.0:
+ resolution: {integrity: sha512-7cp1ODcbd+lkwi+t3igDIMf7TzV8YIRgG7Nt3XzjSkVCxUDUB94m/RPtb/wO2/EhX80tUTFFbVf0Ap75uQQx8w==}
+ engines: {node: '>=8.0.0'}
+
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@@ -1383,6 +1410,12 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ react-chartjs-2@5.3.0:
+ resolution: {integrity: sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==}
+ peerDependencies:
+ chart.js: ^4.1.1
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
react-dom@19.1.1:
resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==}
peerDependencies:
@@ -1511,6 +1544,9 @@ packages:
resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==}
engines: {node: '>=18'}
+ text-segmentation@1.0.3:
+ resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
+
tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
@@ -1576,6 +1612,9 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ utrie@1.0.2:
+ resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
+
vite-node@3.2.4:
resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -1970,6 +2009,8 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.4
+ '@kurkle/color@0.3.4': {}
+
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -2362,6 +2403,8 @@ snapshots:
balanced-match@1.0.2: {}
+ base64-arraybuffer@1.0.2: {}
+
brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
@@ -2401,6 +2444,10 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
+ chart.js@4.5.0:
+ dependencies:
+ '@kurkle/color': 0.3.4
+
check-error@2.1.1: {}
chownr@3.0.0: {}
@@ -2423,6 +2470,10 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
+ css-line-break@2.1.0:
+ dependencies:
+ utrie: 1.0.2
+
csstype@3.1.3: {}
debug@4.4.1:
@@ -2647,6 +2698,11 @@ snapshots:
html-escaper@2.0.2: {}
+ html2canvas-oklch@1.5.0-alpha.0:
+ dependencies:
+ css-line-break: 2.1.0
+ text-segmentation: 1.0.3
+
ignore@5.3.2: {}
ignore@7.0.5: {}
@@ -2902,6 +2958,11 @@ snapshots:
queue-microtask@1.2.3: {}
+ react-chartjs-2@5.3.0(chart.js@4.5.0)(react@19.1.1):
+ dependencies:
+ chart.js: 4.5.0
+ react: 19.1.1
+
react-dom@19.1.1(react@19.1.1):
dependencies:
react: 19.1.1
@@ -3032,6 +3093,10 @@ snapshots:
glob: 10.4.5
minimatch: 9.0.5
+ text-segmentation@1.0.3:
+ dependencies:
+ utrie: 1.0.2
+
tinybench@2.9.0: {}
tinyexec@0.3.2: {}
@@ -3088,6 +3153,10 @@ snapshots:
dependencies:
react: 19.1.1
+ utrie@1.0.2:
+ dependencies:
+ base64-arraybuffer: 1.0.2
+
vite-node@3.2.4(@types/node@24.2.1)(jiti@2.5.1)(lightningcss@1.30.1):
dependencies:
cac: 6.7.14
diff --git a/vite-app/src/components/ChartExport.tsx b/vite-app/src/components/ChartExport.tsx
new file mode 100644
index 00000000..755f7201
--- /dev/null
+++ b/vite-app/src/components/ChartExport.tsx
@@ -0,0 +1,304 @@
+import { useRef, useCallback, useState } from "react";
+import { Chart as ChartJS, registerables } from "chart.js";
+import { Chart } from "react-chartjs-2";
+import html2canvas from "html2canvas-oklch";
+import Button from "./Button";
+import Select from "./Select";
+
+// Register Chart.js components
+ChartJS.register(...registerables);
+
+interface ChartExportProps> {
+ /**
+ * Pivot table data structure
+ */
+ pivotData: {
+ rowKeyTuples: unknown[][];
+ colKeyTuples: unknown[][];
+ cells: Record>;
+ rowTotals: Record;
+ colTotals: Record;
+ grandTotal: number;
+ };
+ /**
+ * Row fields configuration
+ */
+ rowFields: (keyof T)[];
+ /**
+ * Column fields configuration
+ */
+ columnFields: (keyof T)[];
+ /**
+ * Value field configuration
+ */
+ valueField?: keyof T;
+ /**
+ * Aggregator type
+ */
+ aggregator: string;
+ /**
+ * Chart type to render
+ */
+ chartType?: "bar" | "line" | "doughnut" | "pie";
+ /**
+ * Whether to show row totals
+ */
+ showRowTotals?: boolean;
+ /**
+ * Whether to show column totals
+ */
+ showColumnTotals?: boolean;
+}
+
+type ChartType = "bar" | "line" | "doughnut" | "pie";
+
+const ChartExport = >({
+ pivotData,
+ rowFields,
+ columnFields,
+ valueField,
+ aggregator,
+ chartType = "bar",
+}: ChartExportProps) => {
+ const chartRef = useRef(null);
+ const [selectedChartType, setSelectedChartType] =
+ useState(chartType);
+ const [isExporting, setIsExporting] = useState(false);
+
+ // Convert pivot data to Chart.js format
+ const getChartData = useCallback(() => {
+ const { rowKeyTuples, colKeyTuples, cells } = pivotData;
+
+ if (selectedChartType === "bar" || selectedChartType === "line") {
+ // For bar/line charts, use row groups as labels and columns as datasets
+ const labels = rowKeyTuples.map((tuple) =>
+ tuple.map((v) => String(v ?? "")).join(" / ")
+ );
+
+ const datasets = colKeyTuples.map((colTuple, colIdx) => {
+ const colKey = colTuple.map((v) => String(v ?? "")).join(" / ");
+ const colLabel =
+ columnFields.length > 0 ? colKey : `Column ${colIdx + 1}`;
+
+ const data = rowKeyTuples.map((rowTuple) => {
+ const rowKey = rowTuple.map((v) => String(v ?? "")).join("||");
+ const cell = cells[rowKey]?.[colKey];
+ return cell ? cell.value : 0;
+ });
+
+ // Generate a color for each dataset
+ const hue = (colIdx * 137.5) % 360;
+ const color = `hsl(${hue}, 70%, 60%)`;
+
+ return {
+ label: colLabel,
+ data,
+ backgroundColor: selectedChartType === "line" ? "transparent" : color,
+ borderColor: color,
+ borderWidth: selectedChartType === "line" ? 2 : 1,
+ fill: selectedChartType === "line" ? false : true,
+ tension: selectedChartType === "line" ? 0.1 : undefined,
+ };
+ });
+
+ return { labels, datasets };
+ } else {
+ // For pie/doughnut charts, aggregate all data into a single dataset
+ const aggregatedData: { [key: string]: number } = {};
+
+ // Sum up all cell values
+ Object.values(cells).forEach((colCells) => {
+ Object.values(colCells).forEach((cell) => {
+ const colKey = Object.keys(colCells).find(
+ (key) => colCells[key] === cell
+ );
+ if (colKey) {
+ const label = colKey || "Unknown";
+ aggregatedData[label] = (aggregatedData[label] || 0) + cell.value;
+ }
+ });
+ });
+
+ const labels = Object.keys(aggregatedData);
+ const data = Object.values(aggregatedData);
+ const backgroundColor = labels.map((_, idx) => {
+ const hue = (idx * 137.5) % 360;
+ return `hsl(${hue}, 60%, 60%)`;
+ });
+
+ return {
+ labels,
+ datasets: [
+ {
+ data,
+ backgroundColor,
+ borderColor: backgroundColor.map((color) => color),
+ borderWidth: 1,
+ },
+ ],
+ };
+ }
+ }, [pivotData, rowFields, columnFields, selectedChartType]);
+
+ const chartData = getChartData();
+
+ // Don't render chart if no data
+ if (!chartData.labels.length || !chartData.datasets.length) {
+ return (
+
+
+ No data available for chart visualization. Please select row and
+ column fields.
+
+
+ );
+ }
+
+ // Additional safety check for line charts
+ if (
+ selectedChartType === "line" &&
+ chartData.datasets.some((dataset) => dataset.data.length === 0)
+ ) {
+ return (
+
+
+ Line charts require data in all datasets. Please check your pivot
+ configuration.
+
+
+ );
+ }
+
+ const chartOptions = {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ title: {
+ display: true,
+ text: `Pivot Table: ${aggregator} of ${String(
+ valueField || "records"
+ )}`,
+ font: {
+ size: 16,
+ weight: "bold" as const,
+ },
+ },
+ legend: {
+ display: true,
+ position: "top" as const,
+ },
+ tooltip: {
+ enabled: true,
+ },
+ },
+ scales:
+ selectedChartType === "bar" || selectedChartType === "line"
+ ? {
+ y: {
+ type: "linear" as const,
+ beginAtZero: true,
+ title: {
+ display: true,
+ text: aggregator === "count" ? "Count" : "Value",
+ },
+ },
+ x: {
+ type: "category" as const,
+ title: {
+ display: true,
+ text: rowFields.map((f) => String(f)).join(" / "),
+ },
+ },
+ }
+ : undefined,
+ elements:
+ selectedChartType === "line"
+ ? {
+ line: {
+ tension: 0.1,
+ },
+ point: {
+ radius: 3,
+ hoverRadius: 5,
+ },
+ }
+ : undefined,
+ };
+
+ const exportChartAsImage = useCallback(async () => {
+ if (!chartRef.current) return;
+
+ setIsExporting(true);
+ try {
+ const canvas = await html2canvas(chartRef.current, {
+ backgroundColor: "#ffffff",
+ scale: 2, // Higher resolution
+ useCORS: true,
+ allowTaint: true,
+ });
+
+ // Create download link
+ const link = document.createElement("a");
+ link.download = `pivot-chart-${selectedChartType}-${Date.now()}.png`;
+ link.href = canvas.toDataURL("image/png");
+ link.click();
+ } catch (error) {
+ console.error("Error exporting chart:", error);
+ } finally {
+ setIsExporting(false);
+ }
+ }, [selectedChartType]);
+
+ const chartTypes: { value: ChartType; label: string }[] = [
+ { value: "bar", label: "Bar Chart" },
+ { value: "line", label: "Line Chart" },
+ { value: "doughnut", label: "Doughnut Chart" },
+ { value: "pie", label: "Pie Chart" },
+ ];
+
+ return (
+
+
+
Chart Export
+
+ setSelectedChartType(e.target.value as ChartType)}
+ size="sm"
+ className="min-w-32"
+ >
+ {chartTypes.map((type) => (
+
+ {type.label}
+
+ ))}
+
+
+ {isExporting ? "Exporting..." : "Export as Image"}
+
+
+
+
+
+ Visualize your pivot table data as a chart and export it as a
+ high-resolution PNG image. You can adjust your browser window size to
+ change the exported image dimensions.
+
+
+
+
+
+
+ );
+};
+
+export default ChartExport;
diff --git a/vite-app/src/components/PivotTab.tsx b/vite-app/src/components/PivotTab.tsx
index 0daa4fc3..4a928998 100644
--- a/vite-app/src/components/PivotTab.tsx
+++ b/vite-app/src/components/PivotTab.tsx
@@ -1,11 +1,22 @@
import { observer } from "mobx-react";
import PivotTable from "./PivotTable";
+import ChartExport from "./ChartExport";
import SearchableSelect from "./SearchableSelect";
import Button from "./Button";
import FilterSelector from "./FilterSelector";
-import { state } from "../App";
import { type FilterGroup } from "../types/filters";
-import { createFilterFunction } from "../util/filter-utils";
+import { usePivotData } from "../hooks/usePivotData";
+import {
+ createFieldHandlerSet,
+ getAvailableKeys,
+ getPivotConfig,
+ updatePivotConfig,
+ resetPivotConfig,
+ updateFilterConfig,
+ getFlattenedDataset,
+ createFilterFunction,
+ getFilterConfig,
+} from "../util/field-processors";
interface FieldSelectorProps {
title: string;
@@ -125,62 +136,36 @@ const AggregatorSelector = ({
);
const PivotTab = observer(() => {
- const { pivotConfig } = state;
+ const pivotConfig = getPivotConfig();
+ const availableKeys = getAvailableKeys();
- const updateRowFields = (index: number, value: string) => {
- const newRowFields = [...pivotConfig.selectedRowFields];
- newRowFields[index] = value;
- state.updatePivotConfig({ selectedRowFields: newRowFields });
- };
-
- const updateColumnFields = (index: number, value: string) => {
- const newColumnFields = [...pivotConfig.selectedColumnFields];
- newColumnFields[index] = value;
- state.updatePivotConfig({ selectedColumnFields: newColumnFields });
- };
+ // Use the pivot data hook
+ const pivotData = usePivotData({
+ rowFields: pivotConfig.selectedRowFields,
+ columnFields: pivotConfig.selectedColumnFields,
+ valueField: pivotConfig.selectedValueField,
+ aggregator: pivotConfig.selectedAggregator as
+ | "count"
+ | "sum"
+ | "avg"
+ | "min"
+ | "max",
+ showRowTotals: true,
+ showColumnTotals: true,
+ });
const updateValueField = (value: string) => {
- state.updatePivotConfig({ selectedValueField: value });
+ updatePivotConfig({ selectedValueField: value });
};
const updateAggregator = (value: string) => {
- state.updatePivotConfig({ selectedAggregator: value });
+ updatePivotConfig({ selectedAggregator: value });
};
const updateFilters = (filters: FilterGroup[]) => {
- state.updateFilterConfig(filters);
- };
-
- const createFieldHandler = (
- updater: (index: number, value: string) => void
- ) => {
- return (index: number, value: string) => {
- updater(index, value);
- };
- };
-
- const createAddHandler = (
- fields: string[],
- updater: (fields: string[]) => void
- ) => {
- return () => {
- if (fields.length < 3) {
- updater([...fields, ""]);
- }
- };
+ updateFilterConfig(filters);
};
- const createRemoveHandler = (
- fields: string[],
- updater: (fields: string[]) => void
- ) => {
- return (index: number) => {
- updater(fields.filter((_, i) => i !== index));
- };
- };
-
- const availableKeys = state.flattenedDatasetKeys;
-
return (
@@ -194,7 +179,7 @@ const PivotTab = observer(() => {
{/* Controls Section with Reset Button */}
state.resetPivotConfig()}
+ onClick={() => resetPivotConfig()}
variant="secondary"
size="sm"
>
@@ -205,13 +190,8 @@ const PivotTab = observer(() => {
- state.updatePivotConfig({ selectedRowFields: fields })
- )}
- onRemoveField={createRemoveHandler(
- pivotConfig.selectedRowFields,
- (fields) => state.updatePivotConfig({ selectedRowFields: fields })
+ {...createFieldHandlerSet(pivotConfig.selectedRowFields, (fields) =>
+ updatePivotConfig({ selectedRowFields: fields })
)}
availableKeys={availableKeys}
variant="row"
@@ -220,14 +200,8 @@ const PivotTab = observer(() => {
state.updatePivotConfig({ selectedColumnFields: fields })
- )}
- onRemoveField={createRemoveHandler(
- pivotConfig.selectedColumnFields,
- (fields) => state.updatePivotConfig({ selectedColumnFields: fields })
+ {...createFieldHandlerSet(pivotConfig.selectedColumnFields, (fields) =>
+ updatePivotConfig({ selectedColumnFields: fields })
)}
availableKeys={availableKeys}
variant="column"
@@ -246,7 +220,7 @@ const PivotTab = observer(() => {
/>
{
- Result: (field1 = "value1" AND field2 > 10) AND (field3 = "value3" OR field4 = "value4")
*/}
+ {/* Chart Export Component */}
+ {pivotData.hasValidConfiguration && (
+
+ )}
+
field !== ""
- ) as (keyof (typeof state.flattenedDataset)[number])[]
- }
- columnFields={
- pivotConfig.selectedColumnFields.filter(
- (field) => field !== ""
- ) as (keyof (typeof state.flattenedDataset)[number])[]
- }
- valueField={
- pivotConfig.selectedValueField as keyof (typeof state.flattenedDataset)[number]
- }
- aggregator={
- pivotConfig.selectedAggregator as
- | "count"
- | "sum"
- | "avg"
- | "min"
- | "max"
- }
+ data={getFlattenedDataset()}
+ rowFields={pivotData.rowFields}
+ columnFields={pivotData.columnFields}
+ valueField={pivotData.valueField}
+ aggregator={pivotData.aggregator}
showRowTotals
showColumnTotals
- filter={createFilterFunction(state.filterConfig)}
+ filter={createFilterFunction()}
/>
);
diff --git a/vite-app/src/components/PivotTable.tsx b/vite-app/src/components/PivotTable.tsx
index 52b2213d..af6063de 100644
--- a/vite-app/src/components/PivotTable.tsx
+++ b/vite-app/src/components/PivotTable.tsx
@@ -106,7 +106,6 @@ export function PivotTable
>({
filter,
});
- debugger;
return (
diff --git a/vite-app/src/hooks/usePivotData.ts b/vite-app/src/hooks/usePivotData.ts
new file mode 100644
index 00000000..ad170a15
--- /dev/null
+++ b/vite-app/src/hooks/usePivotData.ts
@@ -0,0 +1,87 @@
+import { useMemo } from 'react';
+import { computePivot } from '../util/pivot';
+import { createFilterFunction } from '../util/filter-utils';
+import { state } from '../App';
+
+export interface PivotDataConfig {
+ rowFields: string[];
+ columnFields: string[];
+ valueField?: string;
+ aggregator: 'count' | 'sum' | 'avg' | 'min' | 'max';
+ showRowTotals?: boolean;
+ showColumnTotals?: boolean;
+}
+
+export interface ProcessedPivotData {
+ rowFields: string[];
+ columnFields: string[];
+ valueField?: string;
+ aggregator: 'count' | 'sum' | 'avg' | 'min' | 'max';
+ pivotResult: ReturnType>;
+ hasValidConfiguration: boolean;
+}
+
+/**
+ * Custom hook that processes pivot configuration and computes pivot data
+ * Centralizes all pivot-related logic to avoid duplication
+ */
+export function usePivotData(
+ config: PivotDataConfig
+): ProcessedPivotData {
+ const { rowFields, columnFields, valueField, aggregator, showRowTotals = true, showColumnTotals = true } = config;
+
+ // Filter out empty fields and cast to proper types
+ const processedRowFields = useMemo(
+ () => rowFields.filter((field) => field !== '') as string[],
+ [rowFields]
+ );
+
+ const processedColumnFields = useMemo(
+ () => columnFields.filter((field) => field !== '') as string[],
+ [columnFields]
+ );
+
+ const processedValueField = useMemo(
+ () => (valueField && valueField !== '' ? valueField : undefined) as string | undefined,
+ [valueField]
+ );
+
+ // Check if we have a valid configuration for pivot computation
+ const hasValidConfiguration = useMemo(
+ () => processedRowFields.length > 0 && processedColumnFields.length > 0,
+ [processedRowFields, processedColumnFields]
+ );
+
+ // Compute pivot data only when configuration is valid
+ const pivotResult = useMemo(() => {
+ if (!hasValidConfiguration) {
+ // Return empty pivot result structure
+ return {
+ rowKeyTuples: [],
+ colKeyTuples: [],
+ cells: {},
+ rowTotals: {},
+ colTotals: {},
+ grandTotal: 0,
+ } as ReturnType>;
+ }
+
+ return computePivot({
+ data: state.flattenedDataset,
+ rowFields: processedRowFields,
+ columnFields: processedColumnFields,
+ valueField: processedValueField,
+ aggregator,
+ filter: createFilterFunction(state.filterConfig),
+ });
+ }, [hasValidConfiguration, processedRowFields, processedColumnFields, processedValueField, aggregator, state.filterConfig]);
+
+ return {
+ rowFields: processedRowFields,
+ columnFields: processedColumnFields,
+ valueField: processedValueField,
+ aggregator,
+ pivotResult,
+ hasValidConfiguration,
+ };
+}
diff --git a/vite-app/src/util/field-processors.ts b/vite-app/src/util/field-processors.ts
new file mode 100644
index 00000000..e1851f15
--- /dev/null
+++ b/vite-app/src/util/field-processors.ts
@@ -0,0 +1,121 @@
+import { state } from '../App';
+import { createFilterFunction as createFilterFunctionUtil } from '../util/filter-utils';
+import { type FilterGroup } from '../types/filters';
+
+/**
+ * Utility functions for processing field configurations and creating handlers
+ * Centralizes common field manipulation logic
+ */
+
+/**
+ * Creates a field change handler for a specific index
+ */
+export function createFieldHandler(
+ updater: (index: number, value: string) => void
+) {
+ return (index: number, value: string) => {
+ updater(index, value);
+ };
+}
+
+/**
+ * Creates an add field handler that respects the maximum limit
+ */
+export function createAddHandler(
+ fields: string[],
+ updater: (fields: string[]) => void,
+ maxFields: number = 3
+) {
+ return () => {
+ if (fields.length < maxFields) {
+ updater([...fields, '']);
+ }
+ };
+}
+
+/**
+ * Creates a remove field handler
+ */
+export function createRemoveHandler(
+ fields: string[],
+ updater: (fields: string[]) => void
+) {
+ return (index: number) => {
+ updater(fields.filter((_, i) => i !== index));
+ };
+}
+
+/**
+ * Creates a complete field handler set for a field array
+ */
+export function createFieldHandlerSet(
+ fields: string[],
+ updater: (fields: string[]) => void,
+ maxFields: number = 3
+) {
+ return {
+ onFieldChange: createFieldHandler((index: number, value: string) => {
+ const newFields = [...fields];
+ newFields[index] = value;
+ updater(newFields);
+ }),
+ onAddField: createAddHandler(fields, updater, maxFields),
+ onRemoveField: createRemoveHandler(fields, updater),
+ };
+}
+
+/**
+ * Gets available keys from the current dataset state
+ */
+export function getAvailableKeys(): string[] {
+ return state.flattenedDatasetKeys;
+}
+
+/**
+ * Processes pivot configuration from state
+ */
+export function getPivotConfig() {
+ return state.pivotConfig;
+}
+
+/**
+ * Updates pivot configuration
+ */
+export function updatePivotConfig(updates: Partial) {
+ state.updatePivotConfig(updates);
+}
+
+/**
+ * Resets pivot configuration to defaults
+ */
+export function resetPivotConfig() {
+ state.resetPivotConfig();
+}
+
+/**
+ * Updates filter configuration
+ */
+export function updateFilterConfig(filters: FilterGroup[]) {
+ state.updateFilterConfig(filters);
+}
+
+/**
+ * Gets the flattened dataset from state
+ */
+export function getFlattenedDataset() {
+ return state.flattenedDataset;
+}
+
+/**
+ * Creates a filter function using the current filter config
+ */
+export function createFilterFunction() {
+ return createFilterFunctionUtil(state.filterConfig);
+}
+
+/**
+ * Gets the current filter configuration
+ */
+export function getFilterConfig() {
+ return state.filterConfig;
+}