{"version":3,"file":"StoragePage.9b93a99e75f7f2842c11.js","mappings":"gUAUO,SAASA,EAAY,CAAE,aAAAC,CAAa,EAAU,CACnD,OACE,gBAAC,WACC,gBAAC,WAAI,kBAAgB,EACrB,gBAACC,EAAA,GAAM,CAAC,QAAQ,YAAY,QAAS,IAAMD,EAAa,GAAG,GAAG,QAE9D,CACF,CAEJ,C,yBCNO,SAASE,EAAW,CAAE,SAAAC,EAAU,aAAAH,EAAc,SAAAI,CAAS,EAAU,CACtE,MAAMC,KAAS,MAAWC,CAAS,EAC7BC,EAAQJ,EAAS,MAAM,GAAG,EAAE,OAAO,OAAO,EAEhD,OACE,gBAAC,MAAG,UAAWE,EAAO,YACnBD,GACC,gBAAC,UACC,gBAACI,EAAA,EAAI,CAAC,KAAMJ,EAAU,QAAS,IAAMJ,EAAa,EAAE,EAAG,CACzD,EAEDO,EAAM,IAAI,CAACE,EAAMC,IAAU,CAC1B,IAAIC,EAAM,IAAMJ,EAAM,MAAM,EAAGG,EAAQ,CAAC,EAAE,KAAK,GAAG,EAClD,MAAME,EAAoB,IAAMZ,EAAaW,CAAG,EAC1CE,EAAmBH,IAAUH,EAAM,OAAS,EAClD,OAGE,gBAAC,MAAG,OAAK,YAASE,CAAI,EAAG,QAASI,EAAmB,OAAYD,CAAA,EAC9DH,CACH,CAEJ,CAAC,CACH,CAEJ,CAEA,SAASH,EAAUQ,EAAsB,CACvC,MAAO,CACL,cAAY;AAAA;AAAA,iBAECA,EAAM,QAAQ,EAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAMhBA,EAAM,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,qBAKhBA,EAAM,QAAQ,CAAC;AAAA,mBACjBA,EAAM,OAAO,KAAK;AAAA;AAAA;AAAA,KAInC,CACF,C,mDChDA,MAAMC,GAAmB,CAAE,WAAY,EAAG,EAEnC,SAASC,GAAqB,CAAE,SAAAC,EAAU,UAAAC,EAAW,SAAAC,CAAS,EAAU,CAC7E,OACE,gBAACC,EAAA,EAAK,CAAC,UAAAF,EAAsB,OAAQ,GAAM,MAAM,cAC/C,gBAACG,GAAA,EAAI,CAAC,cAAeN,GAAkB,SAAAI,EAAoB,SAAU,QAClE,CAAC,CAAE,SAAAG,EAAU,OAAAC,CAAO,IACnB,gCACE,gBAACC,GAAA,GACC,MAAM,cACN,QAAS,CAAC,CAACD,EAAO,WAClB,MAAOA,EAAO,YAAcA,EAAO,WAAW,SAE9C,gBAACE,GAAA,GACC,GAAG,oBACF,GAAGH,EAAS,aAAc,CACzB,SAAU,2BACV,SAAU,CAAE,SAAAL,CAAS,CACvB,CAAC,EACH,CACF,EAEA,gBAACG,EAAA,EAAM,UAAN,KACC,gBAACnB,EAAA,GAAM,CAAC,KAAK,UAAS,QAAM,CAC9B,CACF,CAEJ,CACF,CAEJ,C,kDCzCYyB,GAAAA,IACVA,EAAA,KAAO,OACPA,EAAA,OAAS,SACTA,EAAA,MAAQ,QACRA,EAAA,QAAU,UACVA,EAAA,QAAU,MALAA,IAAAA,GAAA,IA4CAC,IAAAA,IACVA,EAAA,KAAO,OACPA,EAAA,GAAK,KACLA,EAAA,KAAO,OAHGA,IAAAA,IAAA,ICrBL,SAASC,GAAS,CAAE,QAAAC,EAAS,KAAApB,EAAM,aAAAT,EAAc,KAAA8B,CAAK,EAAU,CACrE,MAAMzB,KAAS,MAAW,EAAS,EAC7B0B,KAAO,WAAQ,IAAMC,GAAmBvB,CAAI,EAAG,CAACA,CAAI,CAAC,EACrDwB,KAAOC,EAAA,GAAS,SAAY,CAChC,GAAIH,EAAK,WAAa,OAAQ,CAC5B,MAAMI,EAAM,QAAM,KAAkB,EAAE,IAAI1B,CAAI,EAC9C,SAAI,YAAS0B,CAAG,EACPA,EAEF,KAAK,UAAUA,EAAK,KAAM,CAAC,EAEpC,OAAO,IACT,EAAG,CAACJ,EAAMtB,CAAI,CAAC,EAEf,OAAQqB,EAAM,CACZ,KAAKJ,EAAY,OACf,OAAO,gBAAC,WAAI,YAAU,EACxB,KAAKA,EAAY,MACf,OAAO,gBAAC,WAAI,aAAW,EACzB,KAAKA,EAAY,QACf,OAAO,gBAAC,WAAI,iBAAe,CAC/B,CAEA,IAAIU,EAAM,oBAAoB3B,IAK9B,OAJI2B,EAAI,SAAS,GAAG,IAClBA,EAAMA,EAAI,UAAU,EAAGA,EAAI,OAAS,CAAC,GAG/BL,EAAK,SAAU,CACrB,IAAK,MACH,OACE,gBAAC,WACC,gBAACM,GAAA,EAAY,CAAC,IAAAD,EAAU,UAAW/B,EAAO,KAAM,CAClD,EAEJ,IAAK,QACH,OACE,gBAAC,WACC,gBAAC,KAAE,OAAQ,QAAS,KAAM+B,CAAA,EACxB,gBAAC,OAAI,IAAAA,EAAU,IAAI,eAAe,UAAW/B,EAAO,IAAK,CAC3D,CACF,EAEJ,IAAK,OACH,OACE,gBAAC,OAAI,UAAWA,EAAO,cACrB,gBAAC,KAAS,KACP,CAAC,CAAE,MAAAiC,EAAO,OAAAC,CAAO,IAChB,gBAACC,GAAA,GACC,MAAAF,EACA,OAAAC,EACA,MAAON,EAAK,OAAS,GACrB,gBAAiB,GACjB,SAAU,GACV,SAAUF,EAAK,UAAY,OAC3B,YAAa,GACb,OAASU,GAAiB,CACxB,QAAQ,IAAI,WAAYA,CAAI,CAC9B,EACF,CAEJ,CACF,CAEN,CAEA,OACE,gBAAC,WAAI,SACG,gBAAC,KAAE,KAAML,CAAA,EAAM3B,CAAK,CAC5B,CAEJ,CAEA,SAASuB,GAAmBvB,EAA+B,CACzD,MAAMiC,EAAMjC,EAAK,YAAY,GAAG,EAChC,GAAIiC,EAAM,EACR,MAAO,CAAC,EAGV,OADejC,EAAK,UAAUiC,EAAM,CAAC,EAAE,YAAY,EACnC,CACd,IAAK,MACH,MAAO,CAAE,SAAU,KAAM,EAC3B,IAAK,MACL,IAAK,OACL,IAAK,MACL,IAAK,OACL,IAAK,MACH,MAAO,CAAE,SAAU,OAAQ,EAE7B,IAAK,UACL,IAAK,OACH,MAAO,CAAE,SAAU,OAAQ,SAAU,MAAO,EAC9C,IAAK,OACL,IAAK,KACL,IAAK,KACH,MAAO,CAAE,SAAU,MAAO,CAC9B,CACA,MAAO,CAAC,CACV,CAEA,MAAM,GAAa5B,IAA0B,CAE3C,WAAS;AAAA;AAAA;AAAA;AAAA,IAKT,0BAAwB;AAAA;AAAA;AAAA;AAAA,qBAILA,EAAM,QAAQ,CAAC;AAAA,IAGlC,gBAAc;AAAA,wBACQA,EAAM,OAAO,OAAO;AAAA;AAAA,IAG1C,cAAY;AAAA,mBACKA,EAAM,QAAQ,CAAC;AAAA,IAEhC,UAAQ;AAAA,wBACcA,EAAM,OAAO,OAAO;AAAA,eAC7BA,EAAM,QAAQ,CAAC;AAAA,IAE5B,OAAK;AAAA;AAAA;AAAA,eAGQA,EAAM,OAAO,KAAK;AAAA,IAE/B,QAAM;AAAA;AAAA;AAAA,eAGOA,EAAM,OAAO,KAAK;AAAA,GAEjC,G,gBClJO,SAAS6B,GAAW,CAAE,QAAAd,EAAS,KAAAC,CAAK,EAAU,CACnD,MAAMzB,KAAS,MAAW,EAAS,EAEnC,OAAQyB,EAAM,CACZ,KAAKJ,EAAY,OACf,OAAO,gBAAC,WAAI,YAAU,EACxB,KAAKA,EAAY,MACf,OAAO,gBAAC,WAAI,aAAW,CAC3B,CAEA,OACE,gBAAC,OAAI,UAAWrB,EAAO,cACrB,gBAAC,KAAS,KACP,CAAC,CAAE,MAAAiC,EAAO,OAAAC,CAAO,IAChB,gBAAC,OAAI,MAAO,CAAE,MAAO,GAAGD,MAAW,OAAQ,GAAGC,KAAW,GACvD,gBAACK,GAAA,GACC,OAAAL,EACA,MAAAD,EACA,KAAMT,EACN,SAAU,GACV,cAAe,GACf,UAAW,GACb,CACF,CAEJ,CACF,CAEJ,CAEA,MAAM,GAAaf,IAA0B,CAE3C,WAAS;AAAA;AAAA;AAAA;AAAA,IAKT,0BAAwB;AAAA;AAAA;AAAA;AAAA,qBAILA,EAAM,QAAQ,CAAC;AAAA,IAGlC,gBAAc;AAAA,wBACQA,EAAM,OAAO,OAAO;AAAA;AAAA,IAG1C,cAAY;AAAA,mBACKA,EAAM,QAAQ,CAAC;AAAA,IAEhC,UAAQ;AAAA,wBACcA,EAAM,OAAO,OAAO;AAAA,eAC7BA,EAAM,QAAQ,CAAC;AAAA,GAE9B,G,mDC1CO,SAAS+B,GAAS,CAAE,KAAAC,EAAM,aAAA9C,CAAa,EAAU,CACtD,MAAMK,KAAS,MAAW,EAAS,EAC7B0C,KAAUb,EAAA,MAAS,KAAkB,EAAE,SAAS,EAChD,CAACc,EAAaC,CAAc,KAAI,YAAiB,EAAE,EACzD,IAAIC,EAAO,SAAS,SACfA,EAAK,SAAS,GAAG,IACpBA,GAAQ,KAGV,MAAMC,KAAQ,WAAQ,IAAM,CAC1B,IAAIC,EAAOL,EAAQ,OAAS,CAAC,EAC7B,GAAIC,GAAa,OAAQ,CACvB,MAAMK,EAAQL,EAAY,YAAY,EACtCI,EAAOA,EAAK,OAAQE,GAAM,CACxB,MAAMC,EAAID,EAAE,OAEZ,OADgBC,EAAE,KAAK,YAAY,EAAE,QAAQF,CAAK,GAAK,GAAKE,EAAE,YAAY,YAAY,EAAE,QAAQF,CAAK,GAAK,CAK5G,CAAC,EAGH,MAAMH,EAAsB,CAAC,EACvBM,EAAyB,CAAC,EAChC,UAAWF,KAAKF,GAAQ,CAAC,EACnBE,EAAE,OAAO,iBACXE,EAAQ,KAAKF,CAAC,EACLA,EAAE,OAAO,SAAW,WAC7BJ,EAAK,KAAKI,CAAC,EAGf,MAAO,CAAE,KAAAJ,EAAM,QAAAM,CAAQ,CACzB,EAAG,CAACR,EAAaD,CAAO,CAAC,EAEnBU,EAAc,CAACC,EAAcP,IAE/B,gBAAC,KAAa,KACXA,EAAM,IAAKQ,GAAM,CAChB,MAAMC,EAAKD,EAAE,MACb,OACE,gBAACE,EAAA,EAAI,CAAC,IAAKF,EAAE,OAAO,OAAQ,KAAMC,EAAK,iBAAiBF,IAAOC,EAAE,OAAO,UAAY,QAClF,gBAACE,EAAA,EAAK,QAAL,KAAcF,EAAE,OAAO,IAAK,EAC7B,gBAACE,EAAA,EAAK,KAAL,CAAU,UAAWxD,EAAO,WAC1BsD,EAAE,OAAO,YACTA,EAAE,OAAO,KAAK,QAAU,gBAAC,KAAE,KAAMA,EAAE,OAAO,KAAK,QAASA,EAAE,OAAO,KAAK,MAAO,CAChF,EACCA,EAAE,QAAQ,IAAKG,GAAW,gBAACC,EAAA,EAAK,CAAC,IAAKD,EAAO,KAAM,SAAUA,EAAO,SAAU,MAAOA,EAAO,KAAM,CAAE,EAErG,gBAACD,EAAA,EAAK,KAAL,CAAU,UAAWxD,EAAO,WAC3B,gBAAC,KAAe,KACd,gBAAC2D,GAAA,EAAO,CAAC,KAAMC,GAAQN,CAAC,EAAG,CAC7B,CACF,EACA,gBAACE,EAAA,EAAK,OAAL,CAAY,UAAWxD,EAAO,WAC7B,gBAACG,EAAA,EAAI,CAAC,KAAM0D,GAAYP,EAAE,OAAO,IAAI,EAAG,KAAK,OAAO,UAAWtD,EAAO,mBAAoB,CAC5F,CACF,CAEJ,CAAC,CACH,EAIJ,OACE,gBAAC,WACC,gBAAC,OAAI,UAAU,mBACb,gBAAC8D,GAAA,EAAW,CAAC,KAAI,IACf,gBAACC,GAAA,EAAW,CAAC,YAAY,iBAAiB,MAAOpB,EAAa,SAAUC,CAAA,CAAgB,CAC1F,EACA,gBAAChD,EAAA,GAAM,CAAC,UAAU,aAAa,QAAS,IAAMD,EAAa,GAAI0B,EAAY,OAAO,GAAG,UAErF,CACF,EAEA,gBAAC,WAAK+B,EAAY,GAAIN,EAAM,IAAI,CAAE,EAElC,gBAAC,WACC,gBAAC,UAAG,SAAO,EACVM,EAAY,WAAYN,EAAM,OAAO,CACxC,CACF,CAEJ,CAEA,SAAS,GAAUrC,EAAsB,CACvC,MAAO,CACL,sBAAoB;AAAA,eACTA,EAAM,OAAO,KAAK;AAAA,MAE7B,aAAW;AAAA;AAAA,KAGb,CACF,CAEA,SAASmD,GAAQV,EAAgB,CAC/B,MAAMc,EAAiB,CAAC,EACxB,OAAId,EAAE,SACJc,EAAK,KAAK,SAAS,EAIhBd,EAAE,OACLc,EAAK,KAAK,WAAW,EAEhBA,CACT,CAEO,SAASH,GAAYI,EAAwB,CAClD,OAAQA,EAAM,CACZ,IAAK,MACH,MAAO,cACT,IAAK,OACH,MAAO,cACT,IAAK,MACH,MAAO,WACT,QACE,MAAO,aACX,CACF,C,4BCnIA,MAAMC,GAAc,0DAEb,SAASC,GAAa,CAAE,iBAAAC,EAAkB,QAAAC,EAAS,KAAAjE,EAAM,UAAAkE,CAAU,EAAU,CAClF,MAAMtE,KAAS,MAAW,EAAS,EAE7B,CAACuE,EAAMC,CAAO,KAAI,YAA2B,MAAS,EACtD,CAACC,EAAgBC,CAAiB,KAAI,YAAS,EAAK,EACpD,CAACC,EAAeC,CAAgB,KAAI,YAAS,CAAC,EAC9C,CAACC,EAAeC,CAAgB,KAAI,YAAS,EAAI,KAEvD,aAAU,IAAM,CACdF,EAAkBG,GAASA,EAAO,CAAC,CACrC,EAAG,CAACR,CAAI,CAAC,EAET,MAAMS,EAAYlD,GAAwB,CACxC,QAAQ,IAAI,aAAe1B,CAAI,EAC3B0B,EAAI,KACNuC,EAAQvC,EAAI,IAAI,EAEhBuC,EAAQjE,CAAI,CAEhB,EAEM6E,EAAW,MAAOC,EAAoBC,IAAmC,CAC7E,GAAI,CAACD,EAAc,CACjBd,EAAiB,CAAC,uBAAuB,CAAC,EAC1C,OAGF,MAAMtC,EAAM,QAAM,KAAkB,EAAE,OAAO1B,EAAM8E,EAAcC,CAAqB,EAClFrD,EAAI,SAAW,IACjBsC,EAAiB,CAACtC,EAAI,OAAO,CAAC,EAE9BkD,EAASlD,CAAG,CAEhB,EAEMsD,EAAgBC,GAAuC,CAC3DjB,EAAiB,CAAC,CAAC,EAEnB,MAAMc,EACJG,EAAM,cAAc,OAASA,EAAM,cAAc,MAAM,OAAS,GAAKA,EAAM,cAAc,MAAM,CAAC,EAC5FA,EAAM,cAAc,MAAM,CAAC,EAC3B,OACFH,IACFV,EAAQU,CAAY,KAED,KAAsBA,EAAa,KAAMZ,CAAS,GAKnEI,EAAkB,EAAI,EACtBI,EAAiB,EAAI,IAJrBJ,EAAkB,EAAK,EACvBO,EAASC,EAAc,EAAK,EAAE,KAAMjC,GAAM,CAAC,CAAC,GAMlD,EAEMqC,EAAqB,IAAM,CAC3Bf,IACFU,EAASV,EAAM,EAAI,EAAE,KAAMtB,GAAM,CAAC,CAAC,EACnC6B,EAAiB,EAAK,EAE1B,EAEMS,EAAqB,IAAM,CAC/Bf,EAAQ,MAAS,EACjBE,EAAkB,EAAK,EACvBI,EAAiB,EAAK,CACxB,EAEA,OACE,gCACE,gBAACU,GAAA,EAAU,CAAC,OAAQtB,GAAa,aAAAkB,EAA4B,IAAKT,EAAe,UAAW3E,EAAO,cAAc,QAEjH,EAECuE,GAAQE,GACP,gBAACgB,GAAA,GACC,OAAQZ,EACR,KACE,gBAAC,WACC,gBAAC,SAAGN,GAAM,IAAK,EACf,gBAAC,SAAE,uCAAqC,EACxC,gBAAC,SAAE,4BAA0B,CAC/B,EAEF,MAAO,2BACP,YAAa,UACb,UAAWe,EACX,UAAWC,CAAA,CACb,CAEJ,CAEJ,CAEA,MAAM,GAAa9E,IAA0B,CAC3C,gBAAc;AAAA,oBACIA,EAAM,QAAQ,CAAC;AAAA,GAEnC,GCtFMiF,GAAkB,uBAClBC,EAAsB,IAItBC,GAAiBxF,GAAiB,CACtC,MAAMyF,EAAezF,EAAK,YAAY,GAAG,EACzC,OAAIyF,EAAe,EACV,GAGFzF,EAAK,UAAU,EAAGyF,CAAY,CACvC,EAEe,SAASC,GAAYC,EAAc,CAChD,MAAM/F,KAAS,MAAW,EAAS,EAC7BgG,KAAWC,EAAA,GAAY,SAAS,EAChC7F,EAAO2F,EAAM,MAAM,OAAO,MAAQ,GAClCtE,EAAOsE,EAAM,YAAY,MAAQ1E,EAAY,KAC7CgD,EAAU,CAAC6B,EAAWzE,IAAuB,CACjD,IAAInB,GAAO,kBAAoB4F,GAAG,QAAQ,KAAM,GAAG,EAC/CzE,GAAQA,IAASJ,EAAY,OAC/Bf,GAAO,SAAWmB,GAEpB,KAAgB,KAAKnB,CAAG,CAC1B,EAEM,CAAC6F,EAAmBC,CAAoB,KAAI,YAAS,EAAK,EAC1D,CAACC,EAAejC,CAAgB,KAAI,YAAmB,CAAC,CAAC,EAEzD5C,KAAUK,EAAA,GAAS,OAChB,KAAkB,EACtB,KAAKzB,CAAI,EACT,KAAMkG,GAAU,CACf,GAAIA,EAAO,CACT,MAAMC,EAAOD,EAAM,OAAO,CAAC,EAC3BA,EAAM,OAAO,CAAC,EAAI,CAChB,GAAGC,EACH,SAAWC,GAAyB,CAClC,MAAMC,EAAIF,EAAK,OAAOC,EAAI,eAAiB,CAAC,EACtCN,EAAI9F,EAAO,IAAMqG,EACvB,MAAO,CACL,CACE,MAAO,QAAQA,IACf,KAAM,kBAAkBP,IACxB,OAAQ,QACR,OAAQK,EACR,QAAS,IAAM,CACblC,EAAQ6B,CAAC,CACX,CACF,CACF,CACF,CACF,EAEF,OAAOI,CACT,CAAC,EACF,CAAClG,CAAI,CAAC,EAEHsG,KAAW,WAAQ,IAAM,CAC7B,IAAIA,EAAWtG,GAAM,QAAQ,GAAG,EAAI,EACpC,GAAIoB,EAAQ,MAEV,GADeA,EAAQ,MAAM,SACd,EAAG,CAChB,MAAMmF,EAAgBnF,EAAQ,MAAM,OAAO,CAAC,EAAE,OAAO,CAAC,EACtDkF,EAAW,CAACtG,EAAK,SAASuG,CAAK,OAG/BD,EAAW,GAGf,OAAOA,CACT,EAAG,CAACtG,EAAMoB,CAAO,CAAC,EAEZ8C,KAAY,WAAQ,IACjB9C,EAAQ,OAAO,QAAQ,KAAMoF,GAAMA,EAAE,OAAS,MAAM,GAAG,OAAO,OAAQ1D,GAAM,OAAOA,GAAM,QAAQ,GAAK,CAAC,EAC7G,CAAC1B,CAAO,CAAC,EAENqF,EAAa,IAAM,CACvB,MAAMC,EAAS,CAAC1G,GAAM,QAAUA,IAAS,IACzC,OAAQqB,EAAM,CACZ,KAAKJ,EAAY,QACf,OAAKyF,EAIE,gBAACpH,EAAW,CAAC,aAAc2E,CAAA,CAAS,GAHzCA,EAAQ,EAAE,EACH,gBAAC0C,EAAA,EAAO,IAAC,EAGtB,CAEA,MAAMT,EAAQ9E,EAAQ,MACtB,GAAI,IAAC,MAAY8E,CAAK,EACpB,OAAO,+BAAE,EAGX,GAAIQ,EACF,OAAO,gBAACtE,GAAQ,CAAC,KAAM8D,EAAO,aAAcjC,CAAA,CAAS,EAGvD,MAAM2C,EAAO,CAAC,CAAE,KAAM3F,EAAY,KAAM,KAAM,MAAO,CAAC,EAGlDjB,EAAK,QAAQ,GAAG,EAAI,GACtB4G,EAAK,KAAK,CAAE,KAAM3F,EAAY,OAAQ,KAAM,WAAY,CAAC,EAIvDqF,GAIFM,EAAK,KAAK,CAAE,KAAM3F,EAAY,QAAS,KAAM,SAAU,CAAC,EAG1D,MAAM4F,EAAeP,IAAatG,EAAK,WAAW,WAAW,GAAKA,EAAK,WAAW,SAAS,GACrF8G,EAAY9G,EAAK,WAAW,YAAY,GAAKA,EAAK,WAAW,UAAU,EAEvE+G,EAAmB,IAErB,gBAAC,OAAI,UAAWnH,EAAO,YACrB,gBAAC0D,EAAA,EAAK,CAAC,MAAM,gBAAgB,SAAS,QAAQ,SAAU0D,CAAA,EACrDf,EAAc,IAAKgB,GACX,gBAAC,OAAI,IAAKA,CAAA,EAAQA,CAAM,CAChC,CACH,CACF,EAIED,EAAa,IAAM,CACvBhD,EAAiB,CAAC,CAAC,CACrB,EAEA,OACE,gBAAC,OAAI,UAAWpE,EAAO,SACrB,gBAAC,KAAe,CAAC,MAAM,OAAO,QAAQ,gBAAgB,QAAS,KAAM,OAAQ,IAC3E,gBAACH,EAAU,CAAC,SAAUO,EAAM,aAAciE,EAAS,YAAU,cAAW2B,EAAS,KAAK,MAAQ,EAAE,EAAG,EACnG,gBAAC,KAAe,KACbiB,GACC,gCACE,gBAAC9C,GAAY,CAAC,KAAA/D,EAAY,iBAAAgE,EAAoC,UAAAE,EAAsB,QAAAD,CAAA,CAAkB,EACtG,gBAACzE,EAAA,GAAM,CAAC,QAAS,IAAMwG,EAAqB,EAAI,GAAG,YAAU,CAC/D,EAEDc,GACC,gBAACtH,EAAA,IACC,QAAQ,cACR,QAAS,IAAM,CACb,MAAMwC,EAAOsE,EACT,oEACA,6CAEEY,EAAa1B,GAAcxF,CAAI,EACrC,IAAU,QACR,IAAI,KAAsB,CACxB,MAAO,UAAUsG,EAAW,SAAW,SACvC,KAAAtE,EACA,KAAM,YACN,QAAS,SACT,UAAW,OACT,KAAkB,EACf,OAAO,CAAE,KAAAhC,EAAM,SAAAsG,CAAS,CAAC,EACzB,KAAK,IAAM,CACVrC,EAAQiD,CAAU,CACpB,CAAC,CACP,CAAC,CACH,CACF,GACD,QAED,CAEJ,CACF,EAECjB,EAAc,OAAS,GAAKc,EAAiB,EAE9C,gBAACI,EAAA,EAAO,KACLP,EAAK,IAAKQ,GACT,gBAACC,EAAA,GACC,IAAKD,EAAI,KACT,MAAOA,EAAI,KACX,OAAQA,EAAI,OAAS/F,EACrB,YAAa,IAAM4C,EAAQjE,EAAMoH,EAAI,IAAI,EAC3C,CACD,CACH,EACCd,EACC,gBAACpE,GAAU,CAAC,QAASgE,EAAO,KAAA7E,CAAA,CAAY,EAExC,gBAACF,GAAQ,CAAC,KAAAnB,EAAY,QAASkG,EAAO,aAAcjC,EAAS,KAAA5C,CAAA,CAAY,EAG1E0E,GACC,gBAACxF,GAAA,CACC,SAAU,MAAO,CAAE,WAAA+G,CAAW,IAAM,CAClC,MAAMC,EAAa,GAAGvH,KAAQsH,IAE1B,OADQ,QAAM,KAAkB,EAAE,aAAaC,CAAU,IAC7C,OAAU,WACxBtD,EAAQsD,CAAU,EAClBvB,EAAqB,EAAK,EAE9B,EACA,UAAW,IAAM,CACfA,EAAqB,EAAK,CAC5B,EACA,SAAWsB,GAAe,CACxB,MAAME,EAAYF,EAAW,YAAY,EAEzC,SAAI,KAAsBA,EAAYpD,CAAS,EACtC,uDAGJoB,GAAgB,KAAKkC,CAAS,EAI/BF,EAAW,OAAS/B,EACf,qCAAqCA,eAGvC,GAPE,kCAQX,EACF,CAEJ,CAEJ,EAEA,OACE,gBAACkC,EAAA,EAAI,CAAC,SAAA7B,CAAA,EACJ,gBAAC6B,EAAA,EAAK,SAAL,CAAc,UAAWrG,EAAQ,SAAUqF,EAAW,CAAE,CAC3D,CAEJ,CAEA,MAAM,GAAapG,IAA0B,CAE3C,WAAS;AAAA;AAAA;AAAA;AAAA,IAKT,0BAAwB;AAAA;AAAA;AAAA;AAAA,qBAILA,EAAM,QAAQ,CAAC;AAAA,IAGlC,gBAAc;AAAA,wBACQA,EAAM,OAAO,OAAO;AAAA;AAAA,IAG1C,UAAQ;AAAA,wBACcA,EAAM,OAAO,OAAO;AAAA,eAC7BA,EAAM,QAAQ,CAAC;AAAA,IAE5B,cAAY;AAAA;AAAA,IAGZ,gBAAc;AAAA,oBACIA,EAAM,QAAQ,CAAC;AAAA,GAEnC,E,sGC9QA,MAAMqH,CAAwC,CAC5C,aAAc,CAAC,CAEf,MAAM,IAAa1H,EAA0B,CAC3C,MAAM2H,EAAc,oBAAoB3H,IAAO,QAAQ,KAAM,GAAG,EAChE,SAAO,MAAc,EAAE,IAAO2H,CAAW,CAC3C,CAEA,MAAM,KAAK3H,EAA8C,CACvD,IAAIE,EAAM,oBACNF,IACFE,GAAOF,EAAO,KAEhB,MAAM0B,EAAM,QAAM,MAAc,EAAE,IAAmBxB,CAAG,EACxD,GAAIwB,GAAK,KAAM,CACb,MAAM8E,KAAI,MAAkB9E,CAAG,EAC/B,UAAWkG,KAASpB,EAAE,OACpBoB,EAAM,WAAU,KAAoB,CAAE,MAAAA,EAAO,MAAO,IAAO,MAAO,CAAC,EAErE,OAAOpB,EAGX,CAEA,MAAM,aAAaxG,EAA2C,CAC5D,MAAM6H,EAAM,QAAM,MAAc,EAAE,KAChC,4BACA,KAAK,UAAU,CAAE,KAAA7H,CAAK,CAAC,CACzB,EAEA,OAAK6H,EAAI,QAMF,CAAC,EALC,CACL,MAAOA,EAAI,SAAW,eACxB,CAIJ,CAEA,MAAM,aAAaC,EAAoE,CACrF,MAAMD,EAAM,QAAM,MAAc,EAAE,KAChC,4BACA,KAAK,UAAUC,CAAG,CACpB,EAEA,OAAKD,EAAI,QAMF,CAAC,EALC,CACL,MAAOA,EAAI,SAAW,eACxB,CAIJ,CAEA,MAAM,WAAWC,EAAoD,CACnE,MAAMD,EAAM,QAAM,MAAc,EAAE,KAA4C,uBAAuBC,EAAI,MAAM,EAE/G,OAAKD,EAAI,QAMF,CAAC,EALC,CACL,MAAOA,EAAI,SAAW,eACxB,CAIJ,CAEA,MAAM,OAAOC,EAAuE,CAClF,OAAOA,EAAI,SAAW,KAAK,aAAa,CAAE,KAAMA,EAAI,KAAM,MAAO,EAAK,CAAC,EAAI,KAAK,WAAW,CAAE,KAAMA,EAAI,IAAK,CAAC,CAC/G,CAEA,MAAM,OAAOC,EAAgB5D,EAAYY,EAAyD,CAChG,MAAMiD,EAAW,IAAI,SACrBA,EAAS,OAAO,SAAUD,CAAM,EAChCC,EAAS,OAAO,OAAQ7D,CAAI,EAC5B6D,EAAS,OAAO,wBAAyB,OAAOjD,CAAqB,CAAC,EACtE,MAAM8C,EAAM,MAAM,MAAM,sBAAuB,CAC7C,OAAQ,OACR,KAAMG,CACR,CAAC,EAED,IAAIxG,EAAO,MAAMqG,EAAI,KAAK,EAC1B,OAAKrG,IACHA,EAAO,CAAC,GAEVA,EAAK,OAASqG,EAAI,OAClBrG,EAAK,WAAaqG,EAAI,WAClBA,EAAI,SAAW,KAAO,CAACrG,EAAK,MAC9BA,EAAK,IAAM,IAENA,CACT,CAEA,MAAM,MAAMxB,EAAciI,EAAyD,CACjF,OAAO,KAAW,KAAyB,sBAAsBjI,IAAQiI,CAAO,CAClF,CAEA,MAAM,WAAY,CAChB,SAAO,MAAc,EAAE,IAAmB,qBAAqB,CACjE,CAEA,MAAM,WAAWjI,EAAc,CAC7B,SAAO,MAAc,EAAE,IAAiB,wBAAwBA,GAAM,CACxE,CACF,CAEO,SAASkI,EAAsBZ,EAAoBpD,EAAqB,CAE7E,MAAMiE,EADYb,EAAW,YAAY,EACN,KAAK,EAGxC,OAFsCpD,EAAU,IAAKsC,GAAMA,EAAE,KAAK,EAAE,YAAY,CAAC,EAE5C,SAAS2B,CAAgB,CAChE,CAEA,IAAI7F,EAEG,SAAS8F,GAAoB,CAClC,OAAK9F,IACHA,EAAU,IAAIoF,GAETpF,CACT,C","sources":["webpack://grafana/./public/app/features/storage/AddRootView.tsx","webpack://grafana/./public/app/features/storage/Breadcrumb.tsx","webpack://grafana/./public/app/features/storage/CreateNewFolderModal.tsx","webpack://grafana/./public/app/features/storage/types.ts","webpack://grafana/./public/app/features/storage/FileView.tsx","webpack://grafana/./public/app/features/storage/FolderView.tsx","webpack://grafana/./public/app/features/storage/RootView.tsx","webpack://grafana/./public/app/features/storage/UploadButton.tsx","webpack://grafana/./public/app/features/storage/StoragePage.tsx","webpack://grafana/./public/app/features/storage/storage.ts"],"sourcesContent":["import React from 'react';\n\nimport { Button } from '@grafana/ui';\n\nimport { StorageView } from './types';\n\ninterface Props {\n onPathChange: (p: string, v?: StorageView) => void;\n}\n\nexport function AddRootView({ onPathChange }: Props) {\n return (\n
\n
TODO... Add ROOT
\n \n
\n );\n}\n","import { css } from '@emotion/css';\nimport { uniqueId } from 'lodash';\nimport React from 'react';\n\nimport { GrafanaTheme2 } from '@grafana/data';\nimport { Icon, IconName, useStyles2 } from '@grafana/ui';\n\ninterface Props {\n rootIcon?: IconName;\n pathName: string;\n onPathChange: (path: string) => void;\n}\n\nexport function Breadcrumb({ pathName, onPathChange, rootIcon }: Props) {\n const styles = useStyles2(getStyles);\n const paths = pathName.split('/').filter(Boolean);\n\n return (\n \n );\n}\n\nfunction getStyles(theme: GrafanaTheme2) {\n return {\n breadCrumb: css`\n list-style: none;\n padding: ${theme.spacing(2, 1)};\n\n li {\n display: inline;\n\n :not(:last-child) {\n color: ${theme.colors.text.link};\n cursor: pointer;\n }\n + li:before {\n content: '>';\n padding: ${theme.spacing(1)};\n color: ${theme.colors.text.secondary};\n }\n }\n `,\n };\n}\n","import React from 'react';\nimport { SubmitHandler, Validate } from 'react-hook-form';\n\nimport { Button, Field, Form, Input, Modal } from '@grafana/ui';\n\ntype FormModel = { folderName: string };\n\ninterface Props {\n onSubmit: SubmitHandler;\n onDismiss: () => void;\n validate: Validate;\n}\n\nconst initialFormModel = { folderName: '' };\n\nexport function CreateNewFolderModal({ validate, onDismiss, onSubmit }: Props) {\n return (\n \n
\n {({ register, errors }) => (\n <>\n \n \n \n\n \n \n \n \n )}\n \n
\n );\n}\n","import { QueryResultMetaNotice, SelectableValue } from '@grafana/data';\n\nexport enum StorageView {\n Data = 'data',\n Config = 'config',\n Perms = 'perms',\n History = 'history',\n AddRoot = 'add',\n}\n\nexport interface UploadResponse {\n status: number;\n statusText: string;\n\n err?: boolean;\n message: string;\n path: string;\n}\n\nexport interface StorageInfo {\n editable?: boolean;\n builtin?: boolean;\n ready?: boolean;\n notice?: QueryResultMetaNotice[];\n config: StorageConfig;\n}\n\nexport interface StorageConfig {\n type: string;\n prefix: string;\n name: string;\n description: string;\n underContentRoot: string;\n disk?: {\n path: string;\n };\n git?: {\n remote: string;\n branch: string;\n root: string;\n requirePullRequest: boolean;\n accessToken: string;\n };\n sql?: {};\n}\n\nexport enum WorkflowID {\n Save = 'save',\n PR = 'pr',\n Push = 'push',\n}\n\nexport interface WriteValueRequest {\n kind: string;\n body: {}; // json body\n message?: string;\n title?: string;\n workflow: WorkflowID;\n}\n\nexport interface WriteValueResponse {\n code: number;\n message?: string;\n url?: string;\n hash?: string;\n branch?: string;\n pending?: boolean;\n size?: number;\n}\n\nexport interface ItemOptions {\n path: string;\n workflows: Array>;\n}\n","import { css } from '@emotion/css';\nimport { isString } from 'lodash';\nimport React, { useMemo } from 'react';\nimport { useAsync } from 'react-use';\nimport AutoSizer from 'react-virtualized-auto-sizer';\n\nimport { DataFrame, GrafanaTheme2 } from '@grafana/data';\nimport { CodeEditor, useStyles2 } from '@grafana/ui';\nimport { SanitizedSVG } from 'app/core/components/SVG/SanitizedSVG';\n\nimport { getGrafanaStorage } from './storage';\nimport { StorageView } from './types';\n\ninterface FileDisplayInfo {\n category?: 'svg' | 'image' | 'text';\n language?: string; // match code editor\n}\n\ninterface Props {\n listing: DataFrame;\n path: string;\n onPathChange: (p: string, view?: StorageView) => void;\n view: StorageView;\n}\n\nexport function FileView({ listing, path, onPathChange, view }: Props) {\n const styles = useStyles2(getStyles);\n const info = useMemo(() => getFileDisplayInfo(path), [path]);\n const body = useAsync(async () => {\n if (info.category === 'text') {\n const rsp = await getGrafanaStorage().get(path);\n if (isString(rsp)) {\n return rsp;\n }\n return JSON.stringify(rsp, null, 2);\n }\n return null;\n }, [info, path]);\n\n switch (view) {\n case StorageView.Config:\n return
CONFIGURE?
;\n case StorageView.Perms:\n return
Permissions
;\n case StorageView.History:\n return
TODO... history
;\n }\n\n let src = `api/storage/read/${path}`;\n if (src.endsWith('/')) {\n src = src.substring(0, src.length - 1);\n }\n\n switch (info.category) {\n case 'svg':\n return (\n
\n \n
\n );\n case 'image':\n return (\n
\n \n \"File\n \n
\n );\n case 'text':\n return (\n
\n \n {({ width, height }) => (\n {\n console.log('CHANGED!', text);\n }}\n />\n )}\n \n
\n );\n }\n\n return (\n
\n FILE: {path}\n
\n );\n}\n\nfunction getFileDisplayInfo(path: string): FileDisplayInfo {\n const idx = path.lastIndexOf('.');\n if (idx < 0) {\n return {};\n }\n const suffix = path.substring(idx + 1).toLowerCase();\n switch (suffix) {\n case 'svg':\n return { category: 'svg' };\n case 'jpg':\n case 'jpeg':\n case 'png':\n case 'webp':\n case 'gif':\n return { category: 'image' };\n\n case 'geojson':\n case 'json':\n return { category: 'text', language: 'json' };\n case 'text':\n case 'go':\n case 'md':\n return { category: 'text' };\n }\n return {};\n}\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n // TODO: remove `height: 90%`\n wrapper: css`\n display: flex;\n flex-direction: column;\n height: 100%;\n `,\n tableControlRowWrapper: css`\n display: flex;\n flex-direction: row;\n align-items: center;\n margin-bottom: ${theme.spacing(2)};\n `,\n // TODO: remove `height: 100%`\n tableWrapper: css`\n border: 1px solid ${theme.colors.border.medium};\n height: 100%;\n `,\n uploadSpot: css`\n margin-left: ${theme.spacing(2)};\n `,\n border: css`\n border: 1px solid ${theme.colors.border.medium};\n padding: ${theme.spacing(2)};\n `,\n img: css`\n max-width: 100%;\n // max-height: 147px;\n // fill: ${theme.colors.text.primary};\n `,\n icon: css`\n // max-width: 100%;\n // max-height: 147px;\n // fill: ${theme.colors.text.primary};\n `,\n});\n","import { css } from '@emotion/css';\nimport React from 'react';\nimport AutoSizer from 'react-virtualized-auto-sizer';\n\nimport { DataFrame, GrafanaTheme2 } from '@grafana/data';\nimport { Table, useStyles2 } from '@grafana/ui';\n\nimport { StorageView } from './types';\n\ninterface Props {\n listing: DataFrame;\n view: StorageView;\n}\n\nexport function FolderView({ listing, view }: Props) {\n const styles = useStyles2(getStyles);\n\n switch (view) {\n case StorageView.Config:\n return
CONFIGURE?
;\n case StorageView.Perms:\n return
Permissions
;\n }\n\n return (\n
\n \n {({ width, height }) => (\n
\n \n
\n )}\n
\n
\n );\n}\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n // TODO: remove `height: 90%`\n wrapper: css`\n display: flex;\n flex-direction: column;\n height: 100%;\n `,\n tableControlRowWrapper: css`\n display: flex;\n flex-direction: row;\n align-items: center;\n margin-bottom: ${theme.spacing(2)};\n `,\n // TODO: remove `height: 100%`\n tableWrapper: css`\n border: 1px solid ${theme.colors.border.medium};\n height: 100%;\n `,\n uploadSpot: css`\n margin-left: ${theme.spacing(2)};\n `,\n border: css`\n border: 1px solid ${theme.colors.border.medium};\n padding: ${theme.spacing(2)};\n `,\n});\n","import { css } from '@emotion/css';\nimport React, { useMemo, useState } from 'react';\nimport { useAsync } from 'react-use';\n\nimport { DataFrame, GrafanaTheme2 } from '@grafana/data';\nimport {\n Alert,\n Button,\n Card,\n FilterInput,\n HorizontalGroup,\n Icon,\n IconName,\n TagList,\n useStyles2,\n VerticalGroup,\n InlineField,\n} from '@grafana/ui';\n\nimport { getGrafanaStorage } from './storage';\nimport { StorageInfo, StorageView } from './types';\n\ninterface Props {\n root: DataFrame;\n onPathChange: (p: string, v?: StorageView) => void;\n}\n\nexport function RootView({ root, onPathChange }: Props) {\n const styles = useStyles2(getStyles);\n const storage = useAsync(getGrafanaStorage().getConfig);\n const [searchQuery, setSearchQuery] = useState('');\n let base = location.pathname;\n if (!base.endsWith('/')) {\n base += '/';\n }\n\n const roots = useMemo(() => {\n let show = storage.value ?? [];\n if (searchQuery?.length) {\n const lower = searchQuery.toLowerCase();\n show = show.filter((r) => {\n const v = r.config;\n const isMatch = v.name.toLowerCase().indexOf(lower) >= 0 || v.description.toLowerCase().indexOf(lower) >= 0;\n if (isMatch) {\n return true;\n }\n return false;\n });\n }\n\n const base: StorageInfo[] = [];\n const content: StorageInfo[] = [];\n for (const r of show ?? []) {\n if (r.config.underContentRoot) {\n content.push(r);\n } else if (r.config.prefix !== 'content') {\n base.push(r);\n }\n }\n return { base, content };\n }, [searchQuery, storage]);\n\n const renderRoots = (pfix: string, roots: StorageInfo[]) => {\n return (\n \n {roots.map((s) => {\n const ok = s.ready;\n return (\n \n {s.config.name}\n \n {s.config.description}\n {s.config.git?.remote && {s.config.git?.remote}}\n \n {s.notice?.map((notice) => )}\n\n \n \n \n \n \n \n \n \n \n );\n })}\n \n );\n };\n\n return (\n
\n
\n \n \n \n \n
\n\n
{renderRoots('', roots.base)}
\n\n
\n

Content

\n {renderRoots('content/', roots.content)}\n
\n
\n );\n}\n\nfunction getStyles(theme: GrafanaTheme2) {\n return {\n secondaryTextColor: css`\n color: ${theme.colors.text.secondary};\n `,\n clickable: css`\n pointer-events: none;\n `,\n };\n}\n\nfunction getTags(v: StorageInfo) {\n const tags: string[] = [];\n if (v.builtin) {\n tags.push('Builtin');\n }\n\n // Error\n if (!v.ready) {\n tags.push('Not ready');\n }\n return tags;\n}\n\nexport function getIconName(type: string): IconName {\n switch (type) {\n case 'git':\n return 'code-branch';\n case 'disk':\n return 'folder-open';\n case 'sql':\n return 'database';\n default:\n return 'folder-open';\n }\n}\n","import { css } from '@emotion/css';\nimport React, { FormEvent, useEffect, useState } from 'react';\n\nimport { GrafanaTheme2 } from '@grafana/data';\nimport { ConfirmModal, FileUpload, useStyles2 } from '@grafana/ui';\n\nimport { filenameAlreadyExists, getGrafanaStorage } from './storage';\nimport { StorageView, UploadResponse } from './types';\n\ninterface Props {\n setErrorMessages: (errors: string[]) => void;\n setPath: (p: string, view?: StorageView) => void;\n path: string;\n fileNames: string[];\n}\n\nconst fileFormats = 'image/jpg, image/jpeg, image/png, image/gif, image/webp';\n\nexport function UploadButton({ setErrorMessages, setPath, path, fileNames }: Props) {\n const styles = useStyles2(getStyles);\n\n const [file, setFile] = useState(undefined);\n const [filenameExists, setFilenameExists] = useState(false);\n const [fileUploadKey, setFileUploadKey] = useState(1);\n const [isConfirmOpen, setIsConfirmOpen] = useState(true);\n\n useEffect(() => {\n setFileUploadKey((prev) => prev + 1);\n }, [file]);\n\n const onUpload = (rsp: UploadResponse) => {\n console.log('Uploaded: ' + path);\n if (rsp.path) {\n setPath(rsp.path);\n } else {\n setPath(path); // back to data\n }\n };\n\n const doUpload = async (fileToUpload: File, overwriteExistingFile: boolean) => {\n if (!fileToUpload) {\n setErrorMessages(['Please select a file.']);\n return;\n }\n\n const rsp = await getGrafanaStorage().upload(path, fileToUpload, overwriteExistingFile);\n if (rsp.status !== 200) {\n setErrorMessages([rsp.message]);\n } else {\n onUpload(rsp);\n }\n };\n\n const onFileUpload = (event: FormEvent) => {\n setErrorMessages([]);\n\n const fileToUpload =\n event.currentTarget.files && event.currentTarget.files.length > 0 && event.currentTarget.files[0]\n ? event.currentTarget.files[0]\n : undefined;\n if (fileToUpload) {\n setFile(fileToUpload);\n\n const fileExists = filenameAlreadyExists(fileToUpload.name, fileNames);\n if (!fileExists) {\n setFilenameExists(false);\n doUpload(fileToUpload, false).then((r) => {});\n } else {\n setFilenameExists(true);\n setIsConfirmOpen(true);\n }\n }\n };\n\n const onOverwriteConfirm = () => {\n if (file) {\n doUpload(file, true).then((r) => {});\n setIsConfirmOpen(false);\n }\n };\n\n const onOverwriteDismiss = () => {\n setFile(undefined);\n setFilenameExists(false);\n setIsConfirmOpen(false);\n };\n\n return (\n <>\n \n Upload\n \n\n {file && filenameExists && (\n \n

{file?.name}

\n

A file with this name already exists.

\n

What would you like to do?

\n \n }\n title={'This file already exists'}\n confirmText={'Replace'}\n onConfirm={onOverwriteConfirm}\n onDismiss={onOverwriteDismiss}\n />\n )}\n \n );\n}\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n uploadButton: css`\n margin-right: ${theme.spacing(2)};\n `,\n});\n","import { css } from '@emotion/css';\nimport React, { useMemo, useState } from 'react';\nimport { useAsync } from 'react-use';\n\nimport { DataFrame, GrafanaTheme2, isDataFrame, ValueLinkConfig } from '@grafana/data';\nimport { locationService } from '@grafana/runtime';\nimport { useStyles2, Spinner, TabsBar, Tab, Button, HorizontalGroup, Alert, toIconName } from '@grafana/ui';\nimport appEvents from 'app/core/app_events';\nimport { Page } from 'app/core/components/Page/Page';\nimport { useNavModel } from 'app/core/hooks/useNavModel';\nimport { GrafanaRouteComponentProps } from 'app/core/navigation/types';\nimport { ShowConfirmModalEvent } from 'app/types/events';\n\nimport { AddRootView } from './AddRootView';\nimport { Breadcrumb } from './Breadcrumb';\nimport { CreateNewFolderModal } from './CreateNewFolderModal';\nimport { FileView } from './FileView';\nimport { FolderView } from './FolderView';\nimport { RootView } from './RootView';\nimport { UploadButton } from './UploadButton';\nimport { getGrafanaStorage, filenameAlreadyExists } from './storage';\nimport { StorageView } from './types';\n\ninterface RouteParams {\n path: string;\n}\n\ninterface QueryParams {\n view: StorageView;\n}\n\nconst folderNameRegex = /^[a-z\\d!\\-_.*'() ]+$/;\nconst folderNameMaxLength = 256;\n\ninterface Props extends GrafanaRouteComponentProps {}\n\nconst getParentPath = (path: string) => {\n const lastSlashIdx = path.lastIndexOf('/');\n if (lastSlashIdx < 1) {\n return '';\n }\n\n return path.substring(0, lastSlashIdx);\n};\n\nexport default function StoragePage(props: Props) {\n const styles = useStyles2(getStyles);\n const navModel = useNavModel('storage');\n const path = props.match.params.path ?? '';\n const view = props.queryParams.view ?? StorageView.Data;\n const setPath = (p: string, view?: StorageView) => {\n let url = ('/admin/storage/' + p).replace('//', '/');\n if (view && view !== StorageView.Data) {\n url += '?view=' + view;\n }\n locationService.push(url);\n };\n\n const [isAddingNewFolder, setIsAddingNewFolder] = useState(false);\n const [errorMessages, setErrorMessages] = useState([]);\n\n const listing = useAsync((): Promise => {\n return getGrafanaStorage()\n .list(path)\n .then((frame) => {\n if (frame) {\n const name = frame.fields[0];\n frame.fields[0] = {\n ...name,\n getLinks: (cfg: ValueLinkConfig) => {\n const n = name.values[cfg.valueRowIndex ?? 0];\n const p = path + '/' + n;\n return [\n {\n title: `Open ${n}`,\n href: `/admin/storage/${p}`,\n target: '_self',\n origin: name,\n onClick: () => {\n setPath(p);\n },\n },\n ];\n },\n };\n }\n return frame;\n });\n }, [path]);\n\n const isFolder = useMemo(() => {\n let isFolder = path?.indexOf('/') < 0;\n if (listing.value) {\n const length = listing.value.length;\n if (length === 1) {\n const first: string = listing.value.fields[0].values[0];\n isFolder = !path.endsWith(first);\n } else {\n // TODO: handle files/folders which do not exist\n isFolder = true;\n }\n }\n return isFolder;\n }, [path, listing]);\n\n const fileNames = useMemo(() => {\n return listing.value?.fields?.find((f) => f.name === 'name')?.values.filter((v) => typeof v === 'string') ?? [];\n }, [listing]);\n\n const renderView = () => {\n const isRoot = !path?.length || path === '/';\n switch (view) {\n case StorageView.AddRoot:\n if (!isRoot) {\n setPath('');\n return ;\n }\n return ;\n }\n\n const frame = listing.value;\n if (!isDataFrame(frame)) {\n return <>;\n }\n\n if (isRoot) {\n return ;\n }\n\n const opts = [{ what: StorageView.Data, text: 'Data' }];\n\n // Root folders have a config page\n if (path.indexOf('/') < 0) {\n opts.push({ what: StorageView.Config, text: 'Configure' });\n }\n\n // Lets only apply permissions to folders (for now)\n if (isFolder) {\n // opts.push({ what: StorageView.Perms, text: 'Permissions' });\n } else {\n // TODO: only if the file exists in a storage engine with\n opts.push({ what: StorageView.History, text: 'History' });\n }\n\n const canAddFolder = isFolder && (path.startsWith('resources') || path.startsWith('content'));\n const canDelete = path.startsWith('resources/') || path.startsWith('content/');\n\n const getErrorMessages = () => {\n return (\n
\n \n {errorMessages.map((error) => {\n return
{error}
;\n })}\n
\n
\n );\n };\n\n const clearAlert = () => {\n setErrorMessages([]);\n };\n\n return (\n
\n \n \n \n {canAddFolder && (\n <>\n \n \n \n )}\n {canDelete && (\n {\n const text = isFolder\n ? 'Are you sure you want to delete this folder and all its contents?'\n : 'Are you sure you want to delete this file?';\n\n const parentPath = getParentPath(path);\n appEvents.publish(\n new ShowConfirmModalEvent({\n title: `Delete ${isFolder ? 'folder' : 'file'}`,\n text,\n icon: 'trash-alt',\n yesText: 'Delete',\n onConfirm: () =>\n getGrafanaStorage()\n .delete({ path, isFolder })\n .then(() => {\n setPath(parentPath);\n }),\n })\n );\n }}\n >\n Delete\n \n )}\n \n \n\n {errorMessages.length > 0 && getErrorMessages()}\n\n \n {opts.map((opt) => (\n setPath(path, opt.what)}\n />\n ))}\n \n {isFolder ? (\n \n ) : (\n \n )}\n\n {isAddingNewFolder && (\n {\n const folderPath = `${path}/${folderName}`;\n const res = await getGrafanaStorage().createFolder(folderPath);\n if (typeof res?.error !== 'string') {\n setPath(folderPath);\n setIsAddingNewFolder(false);\n }\n }}\n onDismiss={() => {\n setIsAddingNewFolder(false);\n }}\n validate={(folderName) => {\n const lowerCase = folderName.toLowerCase();\n\n if (filenameAlreadyExists(folderName, fileNames)) {\n return 'A file or a folder with the same name already exists';\n }\n\n if (!folderNameRegex.test(lowerCase)) {\n return 'Name contains illegal characters';\n }\n\n if (folderName.length > folderNameMaxLength) {\n return `Name is too long, maximum length: ${folderNameMaxLength} characters`;\n }\n\n return true;\n }}\n />\n )}\n
\n );\n };\n\n return (\n \n {renderView()}\n \n );\n}\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n // TODO: remove `height: 90%`\n wrapper: css`\n display: flex;\n flex-direction: column;\n height: 100%;\n `,\n tableControlRowWrapper: css`\n display: flex;\n flex-direction: row;\n align-items: center;\n margin-bottom: ${theme.spacing(2)};\n `,\n // TODO: remove `height: 100%`\n tableWrapper: css`\n border: 1px solid ${theme.colors.border.medium};\n height: 100%;\n `,\n border: css`\n border: 1px solid ${theme.colors.border.medium};\n padding: ${theme.spacing(2)};\n `,\n errorAlert: css`\n padding-top: 20px;\n `,\n uploadButton: css`\n margin-right: ${theme.spacing(2)};\n `,\n});\n","import { DataFrame, dataFrameFromJSON, DataFrameJSON, getDisplayProcessor } from '@grafana/data';\nimport { config, getBackendSrv } from '@grafana/runtime';\nimport { backendSrv } from 'app/core/services/backend_srv';\n\nimport { UploadResponse, StorageInfo, ItemOptions, WriteValueRequest, WriteValueResponse } from './types';\n\n// Likely should be built into the search interface!\nexport interface GrafanaStorage {\n get: (path: string) => Promise;\n list: (path: string) => Promise;\n upload: (folder: string, file: File, overwriteExistingFile: boolean) => Promise;\n createFolder: (path: string) => Promise<{ error?: string }>;\n delete: (path: { isFolder: boolean; path: string }) => Promise<{ error?: string }>;\n\n /** Admin only */\n getConfig: () => Promise;\n\n /** Called before save */\n getOptions: (path: string) => Promise;\n\n /** Saves dashboards */\n write: (path: string, options: WriteValueRequest) => Promise;\n}\n\nclass SimpleStorage implements GrafanaStorage {\n constructor() {}\n\n async get(path: string): Promise {\n const storagePath = `api/storage/read/${path}`.replace('//', '/');\n return getBackendSrv().get(storagePath);\n }\n\n async list(path: string): Promise {\n let url = 'api/storage/list/';\n if (path) {\n url += path + '/';\n }\n const rsp = await getBackendSrv().get(url);\n if (rsp?.data) {\n const f = dataFrameFromJSON(rsp);\n for (const field of f.fields) {\n field.display = getDisplayProcessor({ field, theme: config.theme2 });\n }\n return f;\n }\n return undefined;\n }\n\n async createFolder(path: string): Promise<{ error?: string }> {\n const res = await getBackendSrv().post<{ success: boolean; message: string }>(\n '/api/storage/createFolder',\n JSON.stringify({ path })\n );\n\n if (!res.success) {\n return {\n error: res.message ?? 'unknown error',\n };\n }\n\n return {};\n }\n\n async deleteFolder(req: { path: string; force: boolean }): Promise<{ error?: string }> {\n const res = await getBackendSrv().post<{ success: boolean; message: string }>(\n `/api/storage/deleteFolder`,\n JSON.stringify(req)\n );\n\n if (!res.success) {\n return {\n error: res.message ?? 'unknown error',\n };\n }\n\n return {};\n }\n\n async deleteFile(req: { path: string }): Promise<{ error?: string }> {\n const res = await getBackendSrv().post<{ success: boolean; message: string }>(`/api/storage/delete/${req.path}`);\n\n if (!res.success) {\n return {\n error: res.message ?? 'unknown error',\n };\n }\n\n return {};\n }\n\n async delete(req: { isFolder: boolean; path: string }): Promise<{ error?: string }> {\n return req.isFolder ? this.deleteFolder({ path: req.path, force: true }) : this.deleteFile({ path: req.path });\n }\n\n async upload(folder: string, file: File, overwriteExistingFile: boolean): Promise {\n const formData = new FormData();\n formData.append('folder', folder);\n formData.append('file', file);\n formData.append('overwriteExistingFile', String(overwriteExistingFile));\n const res = await fetch('/api/storage/upload', {\n method: 'POST',\n body: formData,\n });\n\n let body = await res.json();\n if (!body) {\n body = {};\n }\n body.status = res.status;\n body.statusText = res.statusText;\n if (res.status !== 200 && !body.err) {\n body.err = true;\n }\n return body;\n }\n\n async write(path: string, options: WriteValueRequest): Promise {\n return backendSrv.post(`/api/storage/write/${path}`, options);\n }\n\n async getConfig() {\n return getBackendSrv().get('/api/storage/config');\n }\n\n async getOptions(path: string) {\n return getBackendSrv().get(`/api/storage/options/${path}`);\n }\n}\n\nexport function filenameAlreadyExists(folderName: string, fileNames: string[]) {\n const lowerCase = folderName.toLowerCase();\n const trimmedLowerCase = lowerCase.trim();\n const existingTrimmedLowerCaseNames = fileNames.map((f) => f.trim().toLowerCase());\n\n return existingTrimmedLowerCaseNames.includes(trimmedLowerCase);\n}\n\nlet storage: GrafanaStorage | undefined;\n\nexport function getGrafanaStorage() {\n if (!storage) {\n storage = new SimpleStorage();\n }\n return storage;\n}\n"],"names":["AddRootView","onPathChange","Button","Breadcrumb","pathName","rootIcon","styles","getStyles","paths","Icon","path","index","url","onClickBreadcrumb","isLastBreadcrumb","theme","initialFormModel","CreateNewFolderModal","validate","onDismiss","onSubmit","Modal","Form","register","errors","Field","Input","StorageView","WorkflowID","FileView","listing","view","info","getFileDisplayInfo","body","useAsync","rsp","src","SanitizedSVG","width","height","CodeEditor","text","idx","FolderView","Table","RootView","root","storage","searchQuery","setSearchQuery","base","roots","show","lower","r","v","content","renderRoots","pfix","s","ok","Card","notice","Alert","TagList","getTags","getIconName","InlineField","FilterInput","tags","type","fileFormats","UploadButton","setErrorMessages","setPath","fileNames","file","setFile","filenameExists","setFilenameExists","fileUploadKey","setFileUploadKey","isConfirmOpen","setIsConfirmOpen","prev","onUpload","doUpload","fileToUpload","overwriteExistingFile","onFileUpload","event","onOverwriteConfirm","onOverwriteDismiss","FileUpload","ConfirmModal","folderNameRegex","folderNameMaxLength","getParentPath","lastSlashIdx","StoragePage","props","navModel","useNavModel","p","isAddingNewFolder","setIsAddingNewFolder","errorMessages","frame","name","cfg","n","isFolder","first","f","renderView","isRoot","Spinner","opts","canAddFolder","canDelete","getErrorMessages","clearAlert","error","parentPath","TabsBar","opt","Tab","folderName","folderPath","lowerCase","Page","SimpleStorage","storagePath","field","res","req","folder","formData","options","filenameAlreadyExists","trimmedLowerCase","getGrafanaStorage"],"sourceRoot":""}