@infuro/cms-core 1.0.14 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin.cjs +2067 -740
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +4 -0
- package/dist/admin.d.ts +4 -0
- package/dist/admin.js +2019 -677
- package/dist/admin.js.map +1 -1
- package/dist/api.cjs +967 -87
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/api.js +962 -84
- package/dist/api.js.map +1 -1
- package/dist/hooks.cjs +159 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +24 -1
- package/dist/hooks.d.ts +24 -1
- package/dist/hooks.js +165 -0
- package/dist/hooks.js.map +1 -1
- package/dist/{index-CjBf9dAb.d.ts → index-C85X7cc7.d.ts} +16 -2
- package/dist/{index-Be8NLxu-.d.cts → index-h42MoUNq.d.cts} +16 -2
- package/dist/index.cjs +5676 -4456
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +104 -12
- package/dist/index.d.ts +104 -12
- package/dist/index.js +5670 -4467
- package/dist/index.js.map +1 -1
- package/dist/migrations/1775000000000-ProductUomTypeOrderItemSnapshots.ts +29 -0
- package/dist/migrations/1775200000000-MediaDriveFolders.ts +38 -0
- package/package.json +10 -12
package/dist/admin.cjs
CHANGED
|
@@ -73,13 +73,14 @@ __export(admin_exports, {
|
|
|
73
73
|
module.exports = __toCommonJS(admin_exports);
|
|
74
74
|
|
|
75
75
|
// src/admin/pages/AdminLayout.tsx
|
|
76
|
-
var
|
|
77
|
-
var
|
|
76
|
+
var import_react7 = __toESM(require("react"), 1);
|
|
77
|
+
var import_react8 = require("next-auth/react");
|
|
78
78
|
var import_navigation3 = require("next/navigation");
|
|
79
79
|
var import_sonner = require("sonner");
|
|
80
80
|
|
|
81
81
|
// src/components/Admin/Header.tsx
|
|
82
82
|
var import_react2 = require("next-auth/react");
|
|
83
|
+
var import_react3 = require("react");
|
|
83
84
|
|
|
84
85
|
// src/components/ui/button.tsx
|
|
85
86
|
var React = __toESM(require("react"), 1);
|
|
@@ -301,25 +302,44 @@ function useIsMobile() {
|
|
|
301
302
|
return !!isMobile;
|
|
302
303
|
}
|
|
303
304
|
|
|
305
|
+
// src/lib/infuro-favicon.ts
|
|
306
|
+
var INFURO_FAVICON_BASE64 = "data:image/x-icon;base64,AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAKBEAAJ4EAAAwMAAAAQAgAGgmAADGFQAAKAAAABAAAAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8V////bv///7D////R////0f///7D///9u////FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wL///9s0qun7q9qYv/DkYv/6tfV///+/v///////////////+7///9r////AgAAAAAAAAAAAAAAAP///wL///+Q/////sqcl/+WOzH/ljsx/5c9M//AiYP/+/j4/////////////////v///5D///8CAAAAAAAAAAD///9s/////v/////Oo5//lz00/5Y7Mf+WOzH/ljsx/7Z3cP/9+/v////////////////+////bAAAAAD///8V////7v///////////v39//Tq6f/QqKP/m0U8/5Y7Mf+WOzH/z6ah/////////////////////+7///8V////bv///////////////////////////////+XOy/+ZQTf/ljsx/59MQ//69fX/////////////////////bv///7D/////////////////////////////////////wYyG/5Y7Mf+WOzH/4MTC/////////////////////7D////R//////////////////7+//79/f/+/f3//v39/+DGw/+WOzH/ljsx/82inf/////////////////////R////0f///////////////9Svq/+mWVH/pllR/6ZZUf+iUUj/ljsx/5Y7Mf/KnJf/////////////////////0f///7D////////////////KnJf/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/062p/////////////////////7D///9u////////////////0amk/6FQSP+hUEj/oVBI/6JRSP+mWVH/plpS/+7g3v////////////////////9u////Ff///+7///////////7+/v/9/Pz/9u7t/8SSjP/Dj4r/9ezr//79/f/////////////////////u////FQAAAAD///9s/////v///////////////8OPif+WOzH/ljsx/7+Jgv/////////////////////+////bAAAAAAAAAAA////Av///5D////+//////////+9hH7/ljsx/5Y7Mf+8g3z////////////////+////kP///wIAAAAAAAAAAAAAAAD///8C////bP///+7/////8OTi/7R0bP+zcmr/8OLh///////////u////bP///wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8V////bv///7D////R////0f///7D///9u////FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8C////Hf///0n///95////mf///67///+u////mf///3n///9J////Hf///wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8I////Tf///7D////p/////v////////////////////////////////////7////p////sP///03///8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8C////Pfjz8r7HlpH7wY2H/86lof/iysf/9e3s///+/v//////////////////////////////////////////+////77///89////AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////Cf///37////z9Ovq/6BORf+WOzH/ljsx/5Y8Mv+hUEf/v4iC/+rY1v/+/f3///////////////////////////////////////////H///99////CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wr///+d/////P/////06+r/oE5F/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/m0Q7/8WTjf/38PD///////////////////////////////////////////z///+d////CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8J////nf////3///////////Tr6v+gTkX/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/61mX//w4+L///////////////////////////////////////////3///+d////CQAAAAAAAAAAAAAAAAAAAAAAAAAA////Av///37////8////////////////9Ovq/6BORf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/6daUv/v4uD///////////////////////////////////////////z///9+////AgAAAAAAAAAAAAAAAAAAAAD///89////8//////////////////////48fD/rGRc/5pCOf+YPjT/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/6xkXf/37+////////////////////////////////////////////P///89AAAAAAAAAAAAAAAA////CP///77////////////////////////////////7+Pf/8eXj/+DGw/++hn//m0U8/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/8GLhf/9/Pz//////////////////////////////////////////77///8IAAAAAAAAAAD///9N////+/////////////////////////////////////////////////7+/v/p1tP/rGRd/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/mkI4/+XPzf//////////////////////////////////////////+////00AAAAA////Av///7D////////////////////////////////////////////////////////////////06+r/r2pi/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/tndw//38+///////////////////////////////////////////sP///wL///8d////6f/////////////////////////////////////////////////////////////////////x5eP/olJJ/5Y7Mf+WOzH/ljsx/5Y7Mf+aQjn/7dza///////////////////////////////////////////p////Hf///0n////+///////////////////////////////////////////////////////////////////////////Ur6v/ljwy/5Y7Mf+WOzH/ljsx/5Y7Mf/Oo5////////////////////////////////////////////7///9J////ef////////////////////////////////////////////////////////////////////////////////fw7/+jVEv/ljsx/5Y7Mf+WOzH/ljsx/7Nxav///////////////////////////////////////////////3n///+Z/////////////////////////////////////////////////////////////////////////////////////7yCfP+WOzH/ljsx/5Y7Mf+WOzH/pllQ//r29f//////////////////////////////////////////mf///67///////////////////////////////////////////79/f/9/Pz//fz8//38/P/9/Pz//fz8//38/P/9/Pz/yZuW/5Y7Mf+WOzH/ljsx/5Y7Mf+gT0b/9evr//////////////////////////////////////////+u////rv/////////////////////////////////////69vb/wo2I/7Z3cf+2d3H/tndx/7Z3cf+2d3H/tndx/7Z3cf+lV0//ljsx/5Y7Mf+WOzH/ljsx/6BORf/06+r//////////////////////////////////////////67///+Z//////////////////////////////////////Tr6v+gTkX/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/oE5F//Tr6v//////////////////////////////////////////mf///3n/////////////////////////////////////9Ovq/6BORf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+lVk7/+fPz//////////////////////////////////////////95////Sf////7////////////////////////////////06+r/oE5F/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/7FuZ////v7//////////////////////////////////////v///0n///8d////6f////////////////////////////////Tr6v+gTkX/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/zKCb///////////////////////////////////////////p////Hf///wL///+w////////////////////////////////+PLx/7d4cf+tZl//rWZf/61mX/+tZl//rWZf/61mX/+tZl//rmdg/7Z3cP+2d3H/tndx/7h7df/w4uH//////////////////////////////////////////7D///8CAAAAAP///03////7/////////////////////////////////Pv6//z6+f/8+vn//Pr5//z6+f/z6un/48zJ/+LKx//y5+b//fz8//38/P/9/Pz//fz8///////////////////////////////////////////7////TQAAAAAAAAAA////CP///77/////////////////////////////////////////////////////4MXC/6VXTv+WPDL/ljwy/6JRSP/aubb///7+/////////////////////////////////////////////////////77///8IAAAAAAAAAAAAAAAA////Pf////P///////////////////////////////////////////Xs6/+jU0r/ljsx/5Y7Mf+WOzH/ljsx/59MQv/w5OL////////////////////////////////////////////////z////PQAAAAAAAAAAAAAAAAAAAAD///8C////fv////z/////////////////////////////////////3sK//5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/9m5tf///////////////////////////////////////////P///37///8CAAAAAAAAAAAAAAAAAAAAAAAAAAD///8J////nf////3////////////////////////////////ZuLX/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/17Sw//////////////////////////////////////3///+d////CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8K////nf////z//////////////////////////+zc2v+aQzn/ljsx/5Y7Mf+WOzH/ljsx/5pDOf/r2tj////////////////////////////////8////nf///woAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8J////fv////P//////////////////////v39/8eYkv+YPjT/ljsx/5Y7Mf+YPjT/xpSP//79/f//////////////////////////8////37///8JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8C////Pf///77////7/////////////////fv6/+DEwf/Ekoz/wY2H/97Bvv/8+vr/////////////////////+////77///89////AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////CP///03///+w////6f////7////////////////////////////////////+////6f///7D///9N////CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wL///8d////Sf///3n///+Z////rv///67///+Z////ef///0n///8d////AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAADAAAABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8E////DP///xr///80////Rv///1n///9q////gP///4D///9q////Wf///0b///80////Gv///wz///8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////CP///y7///9g////lP///7v////V////6P////r////8/////P////z////8////+v///+j////V////u////5T///9g////Lv///wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wf///81////k////8/////v////+v////////////////////////////////////////////////////////////////////r////v////z////5P///81////BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8G////Kv37+3zt3Nrd27y5/Nm6t//fxMH/59LQ//Dk4v/48vH//fv6//////////////////////////////////////////////////////////////////////z////d////fP///yr///8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///xH///9g////wvz5+ffNop3+m0U8/5c+NP+cRz7/pFVN/65oYP+8gnv/0qun/+jU0v/59fT//v7+///////////////////////////////////////////////////////////+////9////8L///9f////EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////GP///4n////m/////fv4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+XPTT/m0Q6/6ZaUf/BjIX/6NXT//z5+f////////////////////////////////////////////////////////////////3////k////if///xgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wH///8i////pP////f///////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+XPjT/oVFI/8yhnP/x5OP//vz8////////////////////////////////////////////////////////////////9////6T///8i////AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///yL///+d////8/////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5tEOv+3eHL/48rH//37+/////////////////////////////////////////////////////////////////P///+d////IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////GP///6T////z//////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+YPjT/qFxT/9y+uv/9+vr////////////////////////////////////////////////////////////////z////pP///xgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8R////if////f///////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsy/6NUS//Yt7P//Pn4////////////////////////////////////////////////////////////////9////4n///8RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wb///9g////5v////////////////////////////////v4+P/KnZj/mUI4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5c8Mv+pXlb/38PA//38/P///////////////////////////////////////////////////////////////+b///9g////BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///yr////C/////f////////////////////////////////38+//dwLz/qF1V/51JP/+bRDv/mD40/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+XPTP/qWBY/+vZ2P///v7///////////////////////////////////////////////////////////3////C////KgAAAAAAAAAAAAAAAAAAAAAAAAAA////B////3z////3///////////////////////////////////////////9+/r/9/Dv/+zc2f/dwLz/x5iT/7BrY/+eS0H/lz0z/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/lzwy/7d6c//06un////////////////////////////////////////////////////////////////3////fP///wcAAAAAAAAAAAAAAAAAAAAA////Nf///93////+//////////////////////////////////////////////////////7+/v/+/v7//fz8//Hm5f/XtLH/s3Fq/5lCOP+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/51JP//Sq6b//Pn5///////////////////////////////////////////////////////////+////3f///zUAAAAAAAAAAAAAAAD///8I////k/////z////////////////////////////////////////////////////////////////////////////////9+/v/8eTj/8aVkP+dRz7/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5c8Mv+vamL/7uDf/////////////////////////////////////////////////////////////////P///5P///8IAAAAAAAAAAD///8u////z/////////////////////////////////////////////////////////////////////////////////////////////////fx8P/ImZP/nUg//5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+ZQTj/0qyo//38/P///////////////////////////////////////////////////////////////8////8uAAAAAP///wT///9g////7//////////////////////////////////////////////////////////////////////////////////////////////////////07Or/xJGL/5hAN/+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/rGRd//Tq6P///////////////////////////////////////////////////////////////+////9g////BP///wz///+U////+v////////////////////////////////////////////////////////////////////////////////////////////////////////7/8+jm/7Nya/+WPDP/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/mkQ7/9m3s////v7///////////////////////////////////////////////////////////r///+U////DP///xr///+7/////////////////////////////////////////////////////////////////////////////////////////////////////////////////v7+/+HHxP+hUEf/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljwy/7yBe//69/f///////////////////////////////////////////////////////////////+7////Gv///zT////V//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////bu7f+7gHr/mD40/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/6xkXP/u4N/////////////////////////////////////////////////////////////////V////NP///0b////o//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////38/P/XtK//nEY9/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/6BNRP/iycb////////////////////////////////////////////////////////////////o////Rv///1n////6///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////o1NL/pVhQ/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5xGPP/Yt7L//v39///////////////////////////////////////////////////////////6////Wf///2r////8///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////06un/sW1m/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5pDOf/PpKD//Pr5///////////////////////////////////////////////////////////8////av///4D////8/////////////////////////////////////////////////////////////v7//fv7//37+v/9+/r//fv6//37+v/9+/r//fv6//37+v/9+/r//fv6//37+v/06un/snBp/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5lBOP/KnJj/+/j4///////////////////////////////////////////////////////////8////gP///4D////8//////////////////////////////////////////////////////79/f/s3Nr/z6ej/8uemv/Lnpr/y56a/8uemv/Lnpr/y56a/8uemv/Lnpr/y56a/8uemv/GlI//o1VM/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5lBOP/KnJf/+/j4///////////////////////////////////////////////////////////8////gP///2r////8//////////////////////////////////////////////////////z5+f/Nop3/m0Q7/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5lBOP/KnJf/+/j4///////////////////////////////////////////////////////////8////av///1n////6//////////////////////////////////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5lCOP/KnZj/+/j4///////////////////////////////////////////////////////////6////Wf///0b////o//////////////////////////////////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5pDOv/Qp6P//Pr6///////////////////////////////////////////////////////////o////Rv///zT////V//////////////////////////////////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/51IP//bvbr//v7+///////////////////////////////////////////////////////////V////NP///xr///+7//////////////////////////////////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/6RVTf/n0c////////////////////////////////////////////////////////////////+7////Gv///wz///+U////+v////////////////////////////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/7FuZ//z6ej///////////////////////////////////////////////////////////r///+U////DP///wT///9g////7/////////////////////////////////////////////////z4+P/LnZj/mkI5/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/mUE3/8mblf/9+/r//////////////////////////////////////////////////////////+////9g////BAAAAAD///8u////z/////////////////////////////////////////////////38/P/iycb/v4iC/7yCfP+8gnz/vIJ8/7yCfP+8gnz/vIJ8/7yCfP+8gnz/vIJ8/7yCfP+8gnz/voR+/8qcl//Lnpr/y56a/8uemv/Lnpr/zqOf//Hl5P///v7//////////////////////////////////////////////////////////8////8uAAAAAAAAAAD///8I////k/////z////////////////////////////////////////////////9/Pz/+vf2//r39v/69/b/+vf2//r39v/69/b/+vf2//j08//07Ov/7+Df/+7f3v/z6+n/+PPy//z6+v/9+/r//fv6//37+v/9+/r//fv7///+/v///////////////////////////////////////////////////////////P///5P///8IAAAAAAAAAAAAAAAA////Nf///93////+/////////////////////////////////////////////////////////////////////////////v7/9u7t/9i3s/++hn//sW1m/7BsZf+7gnv/066q//Ln5v/+/v7////////////////////////////////////////////////////////////////////////////////+////3f///zUAAAAAAAAAAAAAAAAAAAAA////B////3z////3///////////////////////////////////////////////////////////////////////////17Ov/wo6I/55KQf+XPjT/ljsx/5Y7Mf+XPTP/nEY9/7uBe//x5eP////////////////////////////////////////////////////////////////////////////////3////fP///wcAAAAAAAAAAAAAAAAAAAAAAAAAAP///yr////C/////f////////////////////////////////////////////////////////////////37+//UsKz/nUg//5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5tEO//No57/+/j4//////////////////////////////////////////////////////////////////////3////C////KgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wb///9g////5v////////////////////////////////////////////////////////////////bv7/+5fHb/lz0z/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y8Mv+ycGj/8ujm/////////////////////////////////////////////////////////////////////+b///9g////BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8R////if////f//////////////////////////////////////////////////////////+3e3P+qYlr/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+oXVT/6tnX////////////////////////////////////////////////////////////////9////4n///8RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////GP///6T////z/////////////////////////////////////////////////////+vZ1/+oXVT/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+nWlL/6dbU///////////////////////////////////////////////////////////z////pP///xgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///yL///+d////8/////////////////////////////////////////////////Hm5f+wa2T/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+uaGD/8OPh//////////////////////////////////////////////////////P///+d////IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wH///8i////pP////f///////////////////////////////////////////r19P/CjYf/mD81/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5g/Nf/BjIb/+fTz////////////////////////////////////////////////9////6T///8i////AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////GP///4n////m/////f////////////////////////////////7+/v/m0M7/qmFZ/5c9M/+WOzH/ljsx/5Y7Mf+WOzH/lz0z/6leV//lz8z//v7+//////////////////////////////////////3////m////if///xgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///xH///9g////wv////f////+///////////////////////////8+vr/4snG/7d4cf+iUUj/mUA3/5c+NP+gTkT/tXVu/+DFw//8+fn////////////////////////////////+////9////8L///9g////EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8G////Kv///3z////d/////P///////////////////////////v38//Tr6v/kzcv/2726/9m6t//jysf/8uno//78/P////////////////////////////////z////d////fP///yr///8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wf///81////k////8/////v////+v////////////////////////////////////////////////////////////////////r////v////z////5P///81////BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////CP///y7///9g////lP///7v////V////6P////r////8/////P////z////8////+v///+j////V////u////5T///9g////Lv///wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8E////DP///xr///80////Rv///1n///9q////gP///4D///9q////Wf///0b///80////Gv///wz///8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
|
|
307
|
+
|
|
304
308
|
// src/components/Admin/Header.tsx
|
|
305
309
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
310
|
+
var DEFAULT_ADMIN_LOGO = "/images/logo/logo.png";
|
|
306
311
|
function AdminHeader() {
|
|
307
312
|
const { data: session } = (0, import_react2.useSession)();
|
|
308
313
|
const isMobile = useIsMobile();
|
|
314
|
+
const configuredLogo = process.env.NEXT_PUBLIC_ADMIN_LOGO_URL || DEFAULT_ADMIN_LOGO;
|
|
315
|
+
const [logoSrc, setLogoSrc] = (0, import_react3.useState)(configuredLogo);
|
|
316
|
+
const [logoFailed, setLogoFailed] = (0, import_react3.useState)(false);
|
|
317
|
+
const isDataLogo = (0, import_react3.useMemo)(() => logoSrc.startsWith("data:"), [logoSrc]);
|
|
309
318
|
const handleLogout = () => {
|
|
310
319
|
(0, import_react2.signOut)({ callbackUrl: "/admin/signin" });
|
|
311
320
|
};
|
|
312
321
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("header", { className: "bg-white border-b border-gray-200 px-4 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
313
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex items-center max-h-9 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
322
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex items-center max-h-9 shrink-0", children: !logoFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
314
323
|
import_image.default,
|
|
315
324
|
{
|
|
316
|
-
src:
|
|
325
|
+
src: logoSrc,
|
|
317
326
|
alt: "Admin",
|
|
318
327
|
width: 120,
|
|
319
328
|
height: 34,
|
|
320
|
-
className: "max-h-9 w-auto object-contain object-left"
|
|
329
|
+
className: "max-h-9 w-auto object-contain object-left",
|
|
330
|
+
unoptimized: isDataLogo,
|
|
331
|
+
onError: () => {
|
|
332
|
+
if (logoSrc !== DEFAULT_ADMIN_LOGO) {
|
|
333
|
+
setLogoSrc(DEFAULT_ADMIN_LOGO);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
setLogoFailed(true);
|
|
337
|
+
}
|
|
321
338
|
}
|
|
322
|
-
)
|
|
339
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
340
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_image.default, { src: INFURO_FAVICON_BASE64, alt: "Infuro", width: 18, height: 18, unoptimized: true }),
|
|
341
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-sm font-semibold text-gray-800", children: "Infuro" })
|
|
342
|
+
] }) }),
|
|
323
343
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex items-center space-x-3", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(DropdownMenu, { children: [
|
|
324
344
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
325
345
|
Button,
|
|
@@ -368,23 +388,20 @@ function AdminHeader() {
|
|
|
368
388
|
var import_link2 = __toESM(require("next/link"), 1);
|
|
369
389
|
var import_image2 = __toESM(require("next/image"), 1);
|
|
370
390
|
var import_navigation = require("next/navigation");
|
|
371
|
-
var
|
|
391
|
+
var import_react5 = require("react");
|
|
372
392
|
var import_lucide_react3 = require("lucide-react");
|
|
373
393
|
|
|
374
394
|
// src/admin/admin-config-context.tsx
|
|
375
|
-
var
|
|
395
|
+
var import_react4 = require("react");
|
|
376
396
|
var defaultValue = {
|
|
377
397
|
customNavItems: [],
|
|
378
398
|
customNavSections: [],
|
|
379
399
|
customCrudConfigs: {}
|
|
380
400
|
};
|
|
381
|
-
var AdminConfigContext = (0,
|
|
382
|
-
|
|
383
|
-
// src/lib/infuro-favicon.ts
|
|
384
|
-
var INFURO_FAVICON_BASE64 = "data:image/x-icon;base64,AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAKBEAAJ4EAAAwMAAAAQAgAGgmAADGFQAAKAAAABAAAAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8V////bv///7D////R////0f///7D///9u////FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wL///9s0qun7q9qYv/DkYv/6tfV///+/v///////////////+7///9r////AgAAAAAAAAAAAAAAAP///wL///+Q/////sqcl/+WOzH/ljsx/5c9M//AiYP/+/j4/////////////////v///5D///8CAAAAAAAAAAD///9s/////v/////Oo5//lz00/5Y7Mf+WOzH/ljsx/7Z3cP/9+/v////////////////+////bAAAAAD///8V////7v///////////v39//Tq6f/QqKP/m0U8/5Y7Mf+WOzH/z6ah/////////////////////+7///8V////bv///////////////////////////////+XOy/+ZQTf/ljsx/59MQ//69fX/////////////////////bv///7D/////////////////////////////////////wYyG/5Y7Mf+WOzH/4MTC/////////////////////7D////R//////////////////7+//79/f/+/f3//v39/+DGw/+WOzH/ljsx/82inf/////////////////////R////0f///////////////9Svq/+mWVH/pllR/6ZZUf+iUUj/ljsx/5Y7Mf/KnJf/////////////////////0f///7D////////////////KnJf/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/062p/////////////////////7D///9u////////////////0amk/6FQSP+hUEj/oVBI/6JRSP+mWVH/plpS/+7g3v////////////////////9u////Ff///+7///////////7+/v/9/Pz/9u7t/8SSjP/Dj4r/9ezr//79/f/////////////////////u////FQAAAAD///9s/////v///////////////8OPif+WOzH/ljsx/7+Jgv/////////////////////+////bAAAAAAAAAAA////Av///5D////+//////////+9hH7/ljsx/5Y7Mf+8g3z////////////////+////kP///wIAAAAAAAAAAAAAAAD///8C////bP///+7/////8OTi/7R0bP+zcmr/8OLh///////////u////bP///wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8V////bv///7D////R////0f///7D///9u////FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8C////Hf///0n///95////mf///67///+u////mf///3n///9J////Hf///wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8I////Tf///7D////p/////v////////////////////////////////////7////p////sP///03///8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8C////Pfjz8r7HlpH7wY2H/86lof/iysf/9e3s///+/v//////////////////////////////////////////+////77///89////AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////Cf///37////z9Ovq/6BORf+WOzH/ljsx/5Y8Mv+hUEf/v4iC/+rY1v/+/f3///////////////////////////////////////////H///99////CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wr///+d/////P/////06+r/oE5F/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/m0Q7/8WTjf/38PD///////////////////////////////////////////z///+d////CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8J////nf////3///////////Tr6v+gTkX/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/61mX//w4+L///////////////////////////////////////////3///+d////CQAAAAAAAAAAAAAAAAAAAAAAAAAA////Av///37////8////////////////9Ovq/6BORf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/6daUv/v4uD///////////////////////////////////////////z///9+////AgAAAAAAAAAAAAAAAAAAAAD///89////8//////////////////////48fD/rGRc/5pCOf+YPjT/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/6xkXf/37+////////////////////////////////////////////P///89AAAAAAAAAAAAAAAA////CP///77////////////////////////////////7+Pf/8eXj/+DGw/++hn//m0U8/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/8GLhf/9/Pz//////////////////////////////////////////77///8IAAAAAAAAAAD///9N////+/////////////////////////////////////////////////7+/v/p1tP/rGRd/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/mkI4/+XPzf//////////////////////////////////////////+////00AAAAA////Av///7D////////////////////////////////////////////////////////////////06+r/r2pi/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/tndw//38+///////////////////////////////////////////sP///wL///8d////6f/////////////////////////////////////////////////////////////////////x5eP/olJJ/5Y7Mf+WOzH/ljsx/5Y7Mf+aQjn/7dza///////////////////////////////////////////p////Hf///0n////+///////////////////////////////////////////////////////////////////////////Ur6v/ljwy/5Y7Mf+WOzH/ljsx/5Y7Mf/Oo5////////////////////////////////////////////7///9J////ef////////////////////////////////////////////////////////////////////////////////fw7/+jVEv/ljsx/5Y7Mf+WOzH/ljsx/7Nxav///////////////////////////////////////////////3n///+Z/////////////////////////////////////////////////////////////////////////////////////7yCfP+WOzH/ljsx/5Y7Mf+WOzH/pllQ//r29f//////////////////////////////////////////mf///67///////////////////////////////////////////79/f/9/Pz//fz8//38/P/9/Pz//fz8//38/P/9/Pz/yZuW/5Y7Mf+WOzH/ljsx/5Y7Mf+gT0b/9evr//////////////////////////////////////////+u////rv/////////////////////////////////////69vb/wo2I/7Z3cf+2d3H/tndx/7Z3cf+2d3H/tndx/7Z3cf+lV0//ljsx/5Y7Mf+WOzH/ljsx/6BORf/06+r//////////////////////////////////////////67///+Z//////////////////////////////////////Tr6v+gTkX/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/oE5F//Tr6v//////////////////////////////////////////mf///3n/////////////////////////////////////9Ovq/6BORf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+lVk7/+fPz//////////////////////////////////////////95////Sf////7////////////////////////////////06+r/oE5F/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/7FuZ////v7//////////////////////////////////////v///0n///8d////6f////////////////////////////////Tr6v+gTkX/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/zKCb///////////////////////////////////////////p////Hf///wL///+w////////////////////////////////+PLx/7d4cf+tZl//rWZf/61mX/+tZl//rWZf/61mX/+tZl//rmdg/7Z3cP+2d3H/tndx/7h7df/w4uH//////////////////////////////////////////7D///8CAAAAAP///03////7/////////////////////////////////Pv6//z6+f/8+vn//Pr5//z6+f/z6un/48zJ/+LKx//y5+b//fz8//38/P/9/Pz//fz8///////////////////////////////////////////7////TQAAAAAAAAAA////CP///77/////////////////////////////////////////////////////4MXC/6VXTv+WPDL/ljwy/6JRSP/aubb///7+/////////////////////////////////////////////////////77///8IAAAAAAAAAAAAAAAA////Pf////P///////////////////////////////////////////Xs6/+jU0r/ljsx/5Y7Mf+WOzH/ljsx/59MQv/w5OL////////////////////////////////////////////////z////PQAAAAAAAAAAAAAAAAAAAAD///8C////fv////z/////////////////////////////////////3sK//5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/9m5tf///////////////////////////////////////////P///37///8CAAAAAAAAAAAAAAAAAAAAAAAAAAD///8J////nf////3////////////////////////////////ZuLX/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/17Sw//////////////////////////////////////3///+d////CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8K////nf////z//////////////////////////+zc2v+aQzn/ljsx/5Y7Mf+WOzH/ljsx/5pDOf/r2tj////////////////////////////////8////nf///woAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8J////fv////P//////////////////////v39/8eYkv+YPjT/ljsx/5Y7Mf+YPjT/xpSP//79/f//////////////////////////8////37///8JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8C////Pf///77////7/////////////////fv6/+DEwf/Ekoz/wY2H/97Bvv/8+vr/////////////////////+////77///89////AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////CP///03///+w////6f////7////////////////////////////////////+////6f///7D///9N////CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wL///8d////Sf///3n///+Z////rv///67///+Z////ef///0n///8d////AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAADAAAABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8E////DP///xr///80////Rv///1n///9q////gP///4D///9q////Wf///0b///80////Gv///wz///8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////CP///y7///9g////lP///7v////V////6P////r////8/////P////z////8////+v///+j////V////u////5T///9g////Lv///wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wf///81////k////8/////v////+v////////////////////////////////////////////////////////////////////r////v////z////5P///81////BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8G////Kv37+3zt3Nrd27y5/Nm6t//fxMH/59LQ//Dk4v/48vH//fv6//////////////////////////////////////////////////////////////////////z////d////fP///yr///8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///xH///9g////wvz5+ffNop3+m0U8/5c+NP+cRz7/pFVN/65oYP+8gnv/0qun/+jU0v/59fT//v7+///////////////////////////////////////////////////////////+////9////8L///9f////EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////GP///4n////m/////fv4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+XPTT/m0Q6/6ZaUf/BjIX/6NXT//z5+f////////////////////////////////////////////////////////////////3////k////if///xgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wH///8i////pP////f///////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+XPjT/oVFI/8yhnP/x5OP//vz8////////////////////////////////////////////////////////////////9////6T///8i////AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///yL///+d////8/////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5tEOv+3eHL/48rH//37+/////////////////////////////////////////////////////////////////P///+d////IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////GP///6T////z//////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+YPjT/qFxT/9y+uv/9+vr////////////////////////////////////////////////////////////////z////pP///xgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8R////if////f///////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsy/6NUS//Yt7P//Pn4////////////////////////////////////////////////////////////////9////4n///8RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wb///9g////5v////////////////////////////////v4+P/KnZj/mUI4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5c8Mv+pXlb/38PA//38/P///////////////////////////////////////////////////////////////+b///9g////BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///yr////C/////f////////////////////////////////38+//dwLz/qF1V/51JP/+bRDv/mD40/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+XPTP/qWBY/+vZ2P///v7///////////////////////////////////////////////////////////3////C////KgAAAAAAAAAAAAAAAAAAAAAAAAAA////B////3z////3///////////////////////////////////////////9+/r/9/Dv/+zc2f/dwLz/x5iT/7BrY/+eS0H/lz0z/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/lzwy/7d6c//06un////////////////////////////////////////////////////////////////3////fP///wcAAAAAAAAAAAAAAAAAAAAA////Nf///93////+//////////////////////////////////////////////////////7+/v/+/v7//fz8//Hm5f/XtLH/s3Fq/5lCOP+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/51JP//Sq6b//Pn5///////////////////////////////////////////////////////////+////3f///zUAAAAAAAAAAAAAAAD///8I////k/////z////////////////////////////////////////////////////////////////////////////////9+/v/8eTj/8aVkP+dRz7/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5c8Mv+vamL/7uDf/////////////////////////////////////////////////////////////////P///5P///8IAAAAAAAAAAD///8u////z/////////////////////////////////////////////////////////////////////////////////////////////////fx8P/ImZP/nUg//5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+ZQTj/0qyo//38/P///////////////////////////////////////////////////////////////8////8uAAAAAP///wT///9g////7//////////////////////////////////////////////////////////////////////////////////////////////////////07Or/xJGL/5hAN/+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/rGRd//Tq6P///////////////////////////////////////////////////////////////+////9g////BP///wz///+U////+v////////////////////////////////////////////////////////////////////////////////////////////////////////7/8+jm/7Nya/+WPDP/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/mkQ7/9m3s////v7///////////////////////////////////////////////////////////r///+U////DP///xr///+7/////////////////////////////////////////////////////////////////////////////////////////////////////////////////v7+/+HHxP+hUEf/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljwy/7yBe//69/f///////////////////////////////////////////////////////////////+7////Gv///zT////V//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////bu7f+7gHr/mD40/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/6xkXP/u4N/////////////////////////////////////////////////////////////////V////NP///0b////o//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////38/P/XtK//nEY9/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/6BNRP/iycb////////////////////////////////////////////////////////////////o////Rv///1n////6///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////o1NL/pVhQ/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5xGPP/Yt7L//v39///////////////////////////////////////////////////////////6////Wf///2r////8///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////06un/sW1m/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5pDOf/PpKD//Pr5///////////////////////////////////////////////////////////8////av///4D////8/////////////////////////////////////////////////////////////v7//fv7//37+v/9+/r//fv6//37+v/9+/r//fv6//37+v/9+/r//fv6//37+v/06un/snBp/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5lBOP/KnJj/+/j4///////////////////////////////////////////////////////////8////gP///4D////8//////////////////////////////////////////////////////79/f/s3Nr/z6ej/8uemv/Lnpr/y56a/8uemv/Lnpr/y56a/8uemv/Lnpr/y56a/8uemv/GlI//o1VM/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5lBOP/KnJf/+/j4///////////////////////////////////////////////////////////8////gP///2r////8//////////////////////////////////////////////////////z5+f/Nop3/m0Q7/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5lBOP/KnJf/+/j4///////////////////////////////////////////////////////////8////av///1n////6//////////////////////////////////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5lCOP/KnZj/+/j4///////////////////////////////////////////////////////////6////Wf///0b////o//////////////////////////////////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5pDOv/Qp6P//Pr6///////////////////////////////////////////////////////////o////Rv///zT////V//////////////////////////////////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/51IP//bvbr//v7+///////////////////////////////////////////////////////////V////NP///xr///+7//////////////////////////////////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/6RVTf/n0c////////////////////////////////////////////////////////////////+7////Gv///wz///+U////+v////////////////////////////////////////////////v4+P/KnJf/mUE4/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/7FuZ//z6ej///////////////////////////////////////////////////////////r///+U////DP///wT///9g////7/////////////////////////////////////////////////z4+P/LnZj/mkI5/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/ljwy/5Y8Mv+WPDL/mUE3/8mblf/9+/r//////////////////////////////////////////////////////////+////9g////BAAAAAD///8u////z/////////////////////////////////////////////////38/P/iycb/v4iC/7yCfP+8gnz/vIJ8/7yCfP+8gnz/vIJ8/7yCfP+8gnz/vIJ8/7yCfP+8gnz/voR+/8qcl//Lnpr/y56a/8uemv/Lnpr/zqOf//Hl5P///v7//////////////////////////////////////////////////////////8////8uAAAAAAAAAAD///8I////k/////z////////////////////////////////////////////////9/Pz/+vf2//r39v/69/b/+vf2//r39v/69/b/+vf2//j08//07Ov/7+Df/+7f3v/z6+n/+PPy//z6+v/9+/r//fv6//37+v/9+/r//fv7///+/v///////////////////////////////////////////////////////////P///5P///8IAAAAAAAAAAAAAAAA////Nf///93////+/////////////////////////////////////////////////////////////////////////////v7/9u7t/9i3s/++hn//sW1m/7BsZf+7gnv/066q//Ln5v/+/v7////////////////////////////////////////////////////////////////////////////////+////3f///zUAAAAAAAAAAAAAAAAAAAAA////B////3z////3///////////////////////////////////////////////////////////////////////////17Ov/wo6I/55KQf+XPjT/ljsx/5Y7Mf+XPTP/nEY9/7uBe//x5eP////////////////////////////////////////////////////////////////////////////////3////fP///wcAAAAAAAAAAAAAAAAAAAAAAAAAAP///yr////C/////f////////////////////////////////////////////////////////////////37+//UsKz/nUg//5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5tEO//No57/+/j4//////////////////////////////////////////////////////////////////////3////C////KgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wb///9g////5v////////////////////////////////////////////////////////////////bv7/+5fHb/lz0z/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y8Mv+ycGj/8ujm/////////////////////////////////////////////////////////////////////+b///9g////BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8R////if////f//////////////////////////////////////////////////////////+3e3P+qYlr/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+oXVT/6tnX////////////////////////////////////////////////////////////////9////4n///8RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////GP///6T////z/////////////////////////////////////////////////////+vZ1/+oXVT/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+nWlL/6dbU///////////////////////////////////////////////////////////z////pP///xgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///yL///+d////8/////////////////////////////////////////////////Hm5f+wa2T/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5Y7Mf+uaGD/8OPh//////////////////////////////////////////////////////P///+d////IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wH///8i////pP////f///////////////////////////////////////////r19P/CjYf/mD81/5Y7Mf+WOzH/ljsx/5Y7Mf+WOzH/ljsx/5g/Nf/BjIb/+fTz////////////////////////////////////////////////9////6T///8i////AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////GP///4n////m/////f////////////////////////////////7+/v/m0M7/qmFZ/5c9M/+WOzH/ljsx/5Y7Mf+WOzH/lz0z/6leV//lz8z//v7+//////////////////////////////////////3////m////if///xgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///xH///9g////wv////f////+///////////////////////////8+vr/4snG/7d4cf+iUUj/mUA3/5c+NP+gTkT/tXVu/+DFw//8+fn////////////////////////////////+////9////8L///9g////EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8G////Kv///3z////d/////P///////////////////////////v38//Tr6v/kzcv/2726/9m6t//jysf/8uno//78/P////////////////////////////////z////d////fP///yr///8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wf///81////k////8/////v////+v////////////////////////////////////////////////////////////////////r////v////z////5P///81////BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////CP///y7///9g////lP///7v////V////6P////r////8/////P////z////8////+v///+j////V////u////5T///9g////Lv///wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8E////DP///xr///80////Rv///1n///9q////gP///4D///9q////Wf///0b///80////Gv///wz///8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
|
|
401
|
+
var AdminConfigContext = (0, import_react4.createContext)(defaultValue);
|
|
385
402
|
|
|
386
403
|
// src/lib/cms-version.ts
|
|
387
|
-
var CMS_VERSION = true ? "1.0.
|
|
404
|
+
var CMS_VERSION = true ? "1.0.16" : "0.0.0";
|
|
388
405
|
|
|
389
406
|
// src/components/Admin/Sidebar.tsx
|
|
390
407
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
@@ -404,7 +421,7 @@ function getIconForItem(iconName) {
|
|
|
404
421
|
}
|
|
405
422
|
function AdminSidebar({ variant = "sidebar" }) {
|
|
406
423
|
const pathname = (0, import_navigation.usePathname)();
|
|
407
|
-
const { customNavItems, customNavSections = [], storeEnabled } = (0,
|
|
424
|
+
const { customNavItems, customNavSections = [], storeEnabled } = (0, import_react5.useContext)(AdminConfigContext);
|
|
408
425
|
const isDrawer = variant === "drawer";
|
|
409
426
|
const isActive = (path) => {
|
|
410
427
|
if (path === "/admin/dashboard") {
|
|
@@ -565,12 +582,12 @@ function AdminSidebar({ variant = "sidebar" }) {
|
|
|
565
582
|
// src/components/Admin/MobileBottomNav.tsx
|
|
566
583
|
var import_link3 = __toESM(require("next/link"), 1);
|
|
567
584
|
var import_navigation2 = require("next/navigation");
|
|
568
|
-
var
|
|
585
|
+
var import_react6 = require("react");
|
|
569
586
|
var import_lucide_react4 = require("lucide-react");
|
|
570
587
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
571
588
|
function MobileBottomNav({ onOpenMore }) {
|
|
572
589
|
const pathname = (0, import_navigation2.usePathname)();
|
|
573
|
-
const { storeEnabled } = (0,
|
|
590
|
+
const { storeEnabled } = (0, import_react6.useContext)(AdminConfigContext);
|
|
574
591
|
const isActive = (path) => {
|
|
575
592
|
if (path === "/admin/dashboard") return pathname === path;
|
|
576
593
|
return pathname.startsWith(path);
|
|
@@ -770,7 +787,7 @@ var PUBLIC_ADMIN_PATHS = [
|
|
|
770
787
|
];
|
|
771
788
|
var AdminStyle = () => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("style", { dangerouslySetInnerHTML: { __html: ADMIN_THEME_CSS } });
|
|
772
789
|
function useAdminViewSettings() {
|
|
773
|
-
(0,
|
|
790
|
+
(0, import_react7.useEffect)(() => {
|
|
774
791
|
fetch("/api/settings/admin_view").then((r) => r.ok ? r.json() : null).then((data) => {
|
|
775
792
|
if (!data) return;
|
|
776
793
|
const root = document.getElementById("admin-root");
|
|
@@ -781,18 +798,18 @@ function useAdminViewSettings() {
|
|
|
781
798
|
}, []);
|
|
782
799
|
}
|
|
783
800
|
function AdminLayoutInner({ children }) {
|
|
784
|
-
const { data: session, status } = (0,
|
|
801
|
+
const { data: session, status } = (0, import_react8.useSession)();
|
|
785
802
|
const router = (0, import_navigation3.useRouter)();
|
|
786
803
|
const pathname = (0, import_navigation3.usePathname)();
|
|
787
|
-
const [loadingTimeout, setLoadingTimeout] = (0,
|
|
788
|
-
const [moreSheetOpen, setMoreSheetOpen] = (0,
|
|
804
|
+
const [loadingTimeout, setLoadingTimeout] = (0, import_react7.useState)(false);
|
|
805
|
+
const [moreSheetOpen, setMoreSheetOpen] = (0, import_react7.useState)(false);
|
|
789
806
|
const isMobile = useIsMobile();
|
|
790
807
|
useAdminViewSettings();
|
|
791
808
|
const isPublicPath = PUBLIC_ADMIN_PATHS.includes(pathname);
|
|
792
|
-
(0,
|
|
809
|
+
(0, import_react7.useEffect)(() => {
|
|
793
810
|
if (isMobile) setMoreSheetOpen(false);
|
|
794
811
|
}, [isMobile, pathname]);
|
|
795
|
-
(0,
|
|
812
|
+
(0, import_react7.useEffect)(() => {
|
|
796
813
|
if (isPublicPath) return;
|
|
797
814
|
if (status === "loading") {
|
|
798
815
|
const timeout = setTimeout(() => setLoadingTimeout(true), 1e4);
|
|
@@ -847,8 +864,8 @@ function AdminLayoutInner({ children }) {
|
|
|
847
864
|
] });
|
|
848
865
|
}
|
|
849
866
|
function useResolvedTheme(theme, themeRegistry) {
|
|
850
|
-
const [activeThemeId, setActiveThemeId] = (0,
|
|
851
|
-
(0,
|
|
867
|
+
const [activeThemeId, setActiveThemeId] = (0, import_react7.useState)(null);
|
|
868
|
+
(0, import_react7.useEffect)(() => {
|
|
852
869
|
if (!themeRegistry || themeRegistry.length === 0) return;
|
|
853
870
|
fetch("/api/settings/theme").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
854
871
|
const id2 = data?.activeThemeId?.trim();
|
|
@@ -862,8 +879,8 @@ function useResolvedTheme(theme, themeRegistry) {
|
|
|
862
879
|
return found?.config ?? theme ?? themeRegistry[0]?.config;
|
|
863
880
|
}
|
|
864
881
|
function useStoreEnabled() {
|
|
865
|
-
const [storeEnabled, setStoreEnabled] = (0,
|
|
866
|
-
(0,
|
|
882
|
+
const [storeEnabled, setStoreEnabled] = (0, import_react7.useState)(false);
|
|
883
|
+
(0, import_react7.useEffect)(() => {
|
|
867
884
|
fetch("/api/settings/store").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
868
885
|
setStoreEnabled(data?.enabled === "true");
|
|
869
886
|
}).catch(() => {
|
|
@@ -874,7 +891,7 @@ function useStoreEnabled() {
|
|
|
874
891
|
function AdminLayout({ children, customNavItems = [], customNavSections = [], customCrudConfigs = {}, theme, themeRegistry, pluginDescriptors = [] }) {
|
|
875
892
|
const resolvedTheme = useResolvedTheme(theme, themeRegistry);
|
|
876
893
|
const storeEnabled = useStoreEnabled();
|
|
877
|
-
const configValue =
|
|
894
|
+
const configValue = import_react7.default.useMemo(
|
|
878
895
|
() => ({
|
|
879
896
|
customNavItems,
|
|
880
897
|
customNavSections,
|
|
@@ -904,10 +921,10 @@ function AdminShell({ children }) {
|
|
|
904
921
|
}
|
|
905
922
|
|
|
906
923
|
// src/components/Admin/CRUD.tsx
|
|
907
|
-
var
|
|
924
|
+
var import_react13 = require("react");
|
|
908
925
|
|
|
909
926
|
// src/components/Admin/CreateEditForm.tsx
|
|
910
|
-
var
|
|
927
|
+
var import_react11 = require("react");
|
|
911
928
|
var import_lucide_react8 = require("lucide-react");
|
|
912
929
|
|
|
913
930
|
// src/components/ui/input.tsx
|
|
@@ -1105,7 +1122,7 @@ var Label3 = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ *
|
|
|
1105
1122
|
Label3.displayName = LabelPrimitive.Root.displayName;
|
|
1106
1123
|
|
|
1107
1124
|
// src/components/ui/file-upload.tsx
|
|
1108
|
-
var
|
|
1125
|
+
var import_react9 = require("react");
|
|
1109
1126
|
|
|
1110
1127
|
// src/components/ui/progress.tsx
|
|
1111
1128
|
var React11 = __toESM(require("react"), 1);
|
|
@@ -1134,10 +1151,10 @@ Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
|
1134
1151
|
// src/components/ui/file-upload.tsx
|
|
1135
1152
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1136
1153
|
function FileUpload({ onUploadSuccess }) {
|
|
1137
|
-
const [selectedFile, setSelectedFile] = (0,
|
|
1138
|
-
const [uploading, setUploading] = (0,
|
|
1139
|
-
const [progress, setProgress] = (0,
|
|
1140
|
-
const [fileUrl, setFileUrl] = (0,
|
|
1154
|
+
const [selectedFile, setSelectedFile] = (0, import_react9.useState)(null);
|
|
1155
|
+
const [uploading, setUploading] = (0, import_react9.useState)(false);
|
|
1156
|
+
const [progress, setProgress] = (0, import_react9.useState)(0);
|
|
1157
|
+
const [fileUrl, setFileUrl] = (0, import_react9.useState)("");
|
|
1141
1158
|
const handleFileChange = (event) => {
|
|
1142
1159
|
setSelectedFile(event.target.files[0]);
|
|
1143
1160
|
};
|
|
@@ -1178,7 +1195,7 @@ function FileUpload({ onUploadSuccess }) {
|
|
|
1178
1195
|
}
|
|
1179
1196
|
|
|
1180
1197
|
// src/components/Admin/RelationAutocomplete.tsx
|
|
1181
|
-
var
|
|
1198
|
+
var import_react10 = require("react");
|
|
1182
1199
|
var import_lucide_react7 = require("lucide-react");
|
|
1183
1200
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1184
1201
|
var DEBOUNCE_MS = 300;
|
|
@@ -1192,21 +1209,21 @@ function RelationAutocomplete({
|
|
|
1192
1209
|
placeholder = "Select\u2026",
|
|
1193
1210
|
disabled = false
|
|
1194
1211
|
}) {
|
|
1195
|
-
const [open, setOpen] = (0,
|
|
1196
|
-
const [search, setSearch] = (0,
|
|
1197
|
-
const [searchInput, setSearchInput] = (0,
|
|
1198
|
-
const [items, setItems] = (0,
|
|
1199
|
-
const [total, setTotal] = (0,
|
|
1200
|
-
const [page, setPage] = (0,
|
|
1201
|
-
const [loading, setLoading] = (0,
|
|
1202
|
-
const [loadingMore, setLoadingMore] = (0,
|
|
1203
|
-
const [selectedLabel, setSelectedLabel] = (0,
|
|
1204
|
-
const [initialLabelLoaded, setInitialLabelLoaded] = (0,
|
|
1205
|
-
const containerRef = (0,
|
|
1206
|
-
const listRef = (0,
|
|
1207
|
-
const debounceRef = (0,
|
|
1212
|
+
const [open, setOpen] = (0, import_react10.useState)(false);
|
|
1213
|
+
const [search, setSearch] = (0, import_react10.useState)("");
|
|
1214
|
+
const [searchInput, setSearchInput] = (0, import_react10.useState)("");
|
|
1215
|
+
const [items, setItems] = (0, import_react10.useState)([]);
|
|
1216
|
+
const [total, setTotal] = (0, import_react10.useState)(0);
|
|
1217
|
+
const [page, setPage] = (0, import_react10.useState)(1);
|
|
1218
|
+
const [loading, setLoading] = (0, import_react10.useState)(false);
|
|
1219
|
+
const [loadingMore, setLoadingMore] = (0, import_react10.useState)(false);
|
|
1220
|
+
const [selectedLabel, setSelectedLabel] = (0, import_react10.useState)(null);
|
|
1221
|
+
const [initialLabelLoaded, setInitialLabelLoaded] = (0, import_react10.useState)(false);
|
|
1222
|
+
const containerRef = (0, import_react10.useRef)(null);
|
|
1223
|
+
const listRef = (0, import_react10.useRef)(null);
|
|
1224
|
+
const debounceRef = (0, import_react10.useRef)(null);
|
|
1208
1225
|
const hasMore = items.length < total;
|
|
1209
|
-
const fetchPage = (0,
|
|
1226
|
+
const fetchPage = (0, import_react10.useCallback)(
|
|
1210
1227
|
async (pageNum, searchQ, append) => {
|
|
1211
1228
|
const params = new URLSearchParams({
|
|
1212
1229
|
page: String(pageNum),
|
|
@@ -1227,13 +1244,13 @@ function RelationAutocomplete({
|
|
|
1227
1244
|
},
|
|
1228
1245
|
[apiEndpoint]
|
|
1229
1246
|
);
|
|
1230
|
-
(0,
|
|
1247
|
+
(0, import_react10.useEffect)(() => {
|
|
1231
1248
|
if (!open) return;
|
|
1232
1249
|
setPage(1);
|
|
1233
1250
|
setLoading(true);
|
|
1234
1251
|
fetchPage(1, search, false).finally(() => setLoading(false));
|
|
1235
1252
|
}, [open, search, fetchPage]);
|
|
1236
|
-
(0,
|
|
1253
|
+
(0, import_react10.useEffect)(() => {
|
|
1237
1254
|
if (!open) return;
|
|
1238
1255
|
const onSearchInput = () => {
|
|
1239
1256
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
@@ -1248,7 +1265,13 @@ function RelationAutocomplete({
|
|
|
1248
1265
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
1249
1266
|
};
|
|
1250
1267
|
}, [searchInput, open]);
|
|
1251
|
-
|
|
1268
|
+
(0, import_react10.useEffect)(() => {
|
|
1269
|
+
if (open) return;
|
|
1270
|
+
setSearchInput("");
|
|
1271
|
+
setSearch("");
|
|
1272
|
+
setPage(1);
|
|
1273
|
+
}, [open]);
|
|
1274
|
+
const loadInitialLabel = (0, import_react10.useCallback)(() => {
|
|
1252
1275
|
if (initialLabelLoaded || value == null || value === "") return;
|
|
1253
1276
|
setInitialLabelLoaded(true);
|
|
1254
1277
|
fetch(`${apiEndpoint}/${value}`).then((r) => r.ok ? r.json() : null).then((item) => {
|
|
@@ -1258,14 +1281,14 @@ function RelationAutocomplete({
|
|
|
1258
1281
|
}).catch(() => {
|
|
1259
1282
|
});
|
|
1260
1283
|
}, [apiEndpoint, value, labelField, initialLabelLoaded]);
|
|
1261
|
-
(0,
|
|
1284
|
+
(0, import_react10.useEffect)(() => {
|
|
1262
1285
|
if (value != null && value !== "") loadInitialLabel();
|
|
1263
1286
|
else {
|
|
1264
1287
|
setSelectedLabel(null);
|
|
1265
1288
|
setInitialLabelLoaded(false);
|
|
1266
1289
|
}
|
|
1267
1290
|
}, [value, loadInitialLabel]);
|
|
1268
|
-
(0,
|
|
1291
|
+
(0, import_react10.useEffect)(() => {
|
|
1269
1292
|
if (!open) return;
|
|
1270
1293
|
const el = listRef.current;
|
|
1271
1294
|
if (!el) return;
|
|
@@ -1281,7 +1304,7 @@ function RelationAutocomplete({
|
|
|
1281
1304
|
el.addEventListener("scroll", onScroll);
|
|
1282
1305
|
return () => el.removeEventListener("scroll", onScroll);
|
|
1283
1306
|
}, [open, hasMore, page, search, loadingMore, fetchPage]);
|
|
1284
|
-
(0,
|
|
1307
|
+
(0, import_react10.useEffect)(() => {
|
|
1285
1308
|
if (!open) return;
|
|
1286
1309
|
const onPointerDown = (e) => {
|
|
1287
1310
|
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
@@ -1375,9 +1398,9 @@ var import_sonner3 = require("sonner");
|
|
|
1375
1398
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1376
1399
|
function CreateEditForm({ isOpen, onClose, apiEndpoint, columns, existingData }) {
|
|
1377
1400
|
const isMobile = useIsMobile();
|
|
1378
|
-
const [formData, setFormData] = (0,
|
|
1379
|
-
const [errors, setErrors] = (0,
|
|
1380
|
-
(0,
|
|
1401
|
+
const [formData, setFormData] = (0, import_react11.useState)({});
|
|
1402
|
+
const [errors, setErrors] = (0, import_react11.useState)({});
|
|
1403
|
+
(0, import_react11.useEffect)(() => {
|
|
1381
1404
|
if (existingData) {
|
|
1382
1405
|
setFormData(existingData);
|
|
1383
1406
|
} else {
|
|
@@ -1664,7 +1687,7 @@ var DialogContent = React14.forwardRef(({ className, children, ...props }, ref)
|
|
|
1664
1687
|
{
|
|
1665
1688
|
ref,
|
|
1666
1689
|
className: cn(
|
|
1667
|
-
"fixed left-[50%] top-[50%] z-50 grid w-
|
|
1690
|
+
"fixed left-[50%] top-[50%] z-50 grid w-[calc(100vw-1rem)] max-w-lg max-h-[calc(100vh-1rem)] translate-x-[-50%] translate-y-[-50%] gap-4 overflow-y-auto rounded-lg border bg-background p-4 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:max-h-[calc(100vh-2rem)] sm:p-6",
|
|
1668
1691
|
className
|
|
1669
1692
|
),
|
|
1670
1693
|
...props,
|
|
@@ -1700,7 +1723,7 @@ var DialogFooter = ({
|
|
|
1700
1723
|
"div",
|
|
1701
1724
|
{
|
|
1702
1725
|
className: cn(
|
|
1703
|
-
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
1726
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:space-x-2 sm:gap-0 [&>button]:w-full sm:[&>button]:w-auto",
|
|
1704
1727
|
className
|
|
1705
1728
|
),
|
|
1706
1729
|
...props
|
|
@@ -1732,9 +1755,10 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
|
1732
1755
|
// src/components/Admin/CRUD.tsx
|
|
1733
1756
|
var import_lucide_react12 = require("lucide-react");
|
|
1734
1757
|
var import_link4 = __toESM(require("next/link"), 1);
|
|
1758
|
+
var import_navigation4 = require("next/navigation");
|
|
1735
1759
|
|
|
1736
1760
|
// src/components/Admin/BulkUploadDialog.tsx
|
|
1737
|
-
var
|
|
1761
|
+
var import_react12 = require("react");
|
|
1738
1762
|
var import_lucide_react11 = require("lucide-react");
|
|
1739
1763
|
var import_papaparse = __toESM(require("papaparse"), 1);
|
|
1740
1764
|
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
@@ -1749,18 +1773,18 @@ var AUTO_GENERATED_COLS = /* @__PURE__ */ new Set([
|
|
|
1749
1773
|
"deleted"
|
|
1750
1774
|
]);
|
|
1751
1775
|
function BulkUploadDialog({ apiEndpoint, onClose }) {
|
|
1752
|
-
const [columns, setColumns] = (0,
|
|
1753
|
-
const [uniqueColumns, setUniqueColumns] = (0,
|
|
1754
|
-
const [loading, setLoading] = (0,
|
|
1755
|
-
const [importing, setImporting] = (0,
|
|
1756
|
-
const [error, setError] = (0,
|
|
1757
|
-
const [success, setSuccess] = (0,
|
|
1758
|
-
const [parsedData, setParsedData] = (0,
|
|
1759
|
-
const [parseErrors, setParseErrors] = (0,
|
|
1760
|
-
const [upsertKey, setUpsertKey] = (0,
|
|
1761
|
-
const [fileName, setFileName] = (0,
|
|
1762
|
-
const fileInputRef = (0,
|
|
1763
|
-
(0,
|
|
1776
|
+
const [columns, setColumns] = (0, import_react12.useState)([]);
|
|
1777
|
+
const [uniqueColumns, setUniqueColumns] = (0, import_react12.useState)([]);
|
|
1778
|
+
const [loading, setLoading] = (0, import_react12.useState)(true);
|
|
1779
|
+
const [importing, setImporting] = (0, import_react12.useState)(false);
|
|
1780
|
+
const [error, setError] = (0, import_react12.useState)(null);
|
|
1781
|
+
const [success, setSuccess] = (0, import_react12.useState)(null);
|
|
1782
|
+
const [parsedData, setParsedData] = (0, import_react12.useState)([]);
|
|
1783
|
+
const [parseErrors, setParseErrors] = (0, import_react12.useState)([]);
|
|
1784
|
+
const [upsertKey, setUpsertKey] = (0, import_react12.useState)("id");
|
|
1785
|
+
const [fileName, setFileName] = (0, import_react12.useState)("");
|
|
1786
|
+
const fileInputRef = (0, import_react12.useRef)(null);
|
|
1787
|
+
(0, import_react12.useEffect)(() => {
|
|
1764
1788
|
fetchMetadata();
|
|
1765
1789
|
}, [apiEndpoint]);
|
|
1766
1790
|
const fetchMetadata = async () => {
|
|
@@ -2002,6 +2026,7 @@ function BulkUploadDialog({ apiEndpoint, onClose }) {
|
|
|
2002
2026
|
}
|
|
2003
2027
|
|
|
2004
2028
|
// src/components/Admin/CRUD.tsx
|
|
2029
|
+
var import_sonner4 = require("sonner");
|
|
2005
2030
|
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
2006
2031
|
function getNestedValue(obj, path) {
|
|
2007
2032
|
return path.split(".").reduce((current, key) => {
|
|
@@ -2026,38 +2051,55 @@ function AdminCRUD({
|
|
|
2026
2051
|
extraListParams,
|
|
2027
2052
|
manageUserGroups
|
|
2028
2053
|
}) {
|
|
2029
|
-
const
|
|
2030
|
-
const [
|
|
2031
|
-
const [
|
|
2032
|
-
const [
|
|
2033
|
-
const [
|
|
2034
|
-
const [
|
|
2035
|
-
const [
|
|
2036
|
-
const [
|
|
2037
|
-
const [
|
|
2038
|
-
const [
|
|
2039
|
-
const [
|
|
2040
|
-
const [
|
|
2041
|
-
const [
|
|
2042
|
-
const [
|
|
2043
|
-
const [
|
|
2044
|
-
const [
|
|
2045
|
-
const [
|
|
2046
|
-
const [
|
|
2047
|
-
const [
|
|
2054
|
+
const router = (0, import_navigation4.useRouter)();
|
|
2055
|
+
const [data, setData] = (0, import_react13.useState)([]);
|
|
2056
|
+
const [loading, setLoading] = (0, import_react13.useState)(true);
|
|
2057
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
2058
|
+
const [page, setPage] = (0, import_react13.useState)(1);
|
|
2059
|
+
const [totalPages, setTotalPages] = (0, import_react13.useState)(1);
|
|
2060
|
+
const [sortField, setSortField] = (0, import_react13.useState)(defaultSortField);
|
|
2061
|
+
const [sortOrder, setSortOrder] = (0, import_react13.useState)(defaultSortOrder);
|
|
2062
|
+
const [searchQuery, setSearchQuery] = (0, import_react13.useState)("");
|
|
2063
|
+
const [searchInput, setSearchInput] = (0, import_react13.useState)("");
|
|
2064
|
+
const [filterValues, setFilterValues] = (0, import_react13.useState)({});
|
|
2065
|
+
const [filterApplyKey, setFilterApplyKey] = (0, import_react13.useState)(0);
|
|
2066
|
+
const [isFormOpen, setIsFormOpen] = (0, import_react13.useState)(false);
|
|
2067
|
+
const [editingItem, setEditingItem] = (0, import_react13.useState)(null);
|
|
2068
|
+
const [deleteDialogOpen, setDeleteDialogOpen] = (0, import_react13.useState)(false);
|
|
2069
|
+
const [itemToDelete, setItemToDelete] = (0, import_react13.useState)(null);
|
|
2070
|
+
const [deleteLoading, setDeleteLoading] = (0, import_react13.useState)(false);
|
|
2071
|
+
const [refreshKey, setRefreshKey] = (0, import_react13.useState)(0);
|
|
2072
|
+
const [bulkDialogOpen, setBulkDialogOpen] = (0, import_react13.useState)(false);
|
|
2073
|
+
const [showFilters, setShowFilters] = (0, import_react13.useState)(false);
|
|
2074
|
+
const [isRefetching, setIsRefetching] = (0, import_react13.useState)(false);
|
|
2075
|
+
const [roleOptions, setRoleOptions] = (0, import_react13.useState)([]);
|
|
2076
|
+
const hasLoadedRef = (0, import_react13.useRef)(false);
|
|
2048
2077
|
const isMobile = useIsMobile();
|
|
2049
2078
|
const showGroupColumn = !!manageUserGroups && roleOptions.length > 0;
|
|
2050
|
-
(0,
|
|
2079
|
+
(0, import_react13.useEffect)(() => {
|
|
2080
|
+
const timeoutId = setTimeout(() => {
|
|
2081
|
+
if (searchInput !== searchQuery) {
|
|
2082
|
+
setPage(1);
|
|
2083
|
+
setSearchQuery(searchInput);
|
|
2084
|
+
}
|
|
2085
|
+
}, 300);
|
|
2086
|
+
return () => clearTimeout(timeoutId);
|
|
2087
|
+
}, [searchInput, searchQuery]);
|
|
2088
|
+
(0, import_react13.useEffect)(() => {
|
|
2051
2089
|
if (!manageUserGroups) return;
|
|
2052
2090
|
fetch("/api/admin/roles").then((r) => r.ok ? r.json() : null).then((data2) => {
|
|
2053
2091
|
if (data2?.groups?.length) setRoleOptions(data2.groups.map((g) => ({ id: g.id, name: g.name })));
|
|
2054
2092
|
}).catch(() => {
|
|
2055
2093
|
});
|
|
2056
2094
|
}, [manageUserGroups]);
|
|
2057
|
-
(0,
|
|
2095
|
+
(0, import_react13.useEffect)(() => {
|
|
2058
2096
|
async function loadData() {
|
|
2059
2097
|
try {
|
|
2060
|
-
|
|
2098
|
+
if (!hasLoadedRef.current) {
|
|
2099
|
+
setLoading(true);
|
|
2100
|
+
} else {
|
|
2101
|
+
setIsRefetching(true);
|
|
2102
|
+
}
|
|
2061
2103
|
setError(null);
|
|
2062
2104
|
const params = new URLSearchParams({
|
|
2063
2105
|
page: page.toString(),
|
|
@@ -2097,6 +2139,8 @@ function AdminCRUD({
|
|
|
2097
2139
|
setTotalPages(1);
|
|
2098
2140
|
} finally {
|
|
2099
2141
|
setLoading(false);
|
|
2142
|
+
setIsRefetching(false);
|
|
2143
|
+
hasLoadedRef.current = true;
|
|
2100
2144
|
}
|
|
2101
2145
|
}
|
|
2102
2146
|
loadData();
|
|
@@ -2107,30 +2151,67 @@ function AdminCRUD({
|
|
|
2107
2151
|
};
|
|
2108
2152
|
const handleSearch = () => {
|
|
2109
2153
|
setPage(1);
|
|
2154
|
+
setSearchQuery(searchInput);
|
|
2110
2155
|
};
|
|
2111
2156
|
const handleApplyFilters = () => {
|
|
2112
2157
|
setPage(1);
|
|
2113
2158
|
setFilterApplyKey((k) => k + 1);
|
|
2114
2159
|
};
|
|
2160
|
+
const closeDeleteDialog = () => {
|
|
2161
|
+
console.debug("[CRUD][delete] closeDeleteDialog", {
|
|
2162
|
+
open: deleteDialogOpen,
|
|
2163
|
+
itemId: itemToDelete?.id,
|
|
2164
|
+
loading: deleteLoading
|
|
2165
|
+
});
|
|
2166
|
+
setDeleteDialogOpen(false);
|
|
2167
|
+
setItemToDelete(null);
|
|
2168
|
+
setDeleteLoading(false);
|
|
2169
|
+
};
|
|
2115
2170
|
const handleDeleteClick = (item) => {
|
|
2171
|
+
console.debug("[CRUD][delete] handleDeleteClick", {
|
|
2172
|
+
itemId: item?.id,
|
|
2173
|
+
resource: resourceName
|
|
2174
|
+
});
|
|
2175
|
+
const active = typeof document !== "undefined" ? document.activeElement : null;
|
|
2176
|
+
if (active instanceof HTMLElement) {
|
|
2177
|
+
active.blur();
|
|
2178
|
+
}
|
|
2179
|
+
setDeleteLoading(false);
|
|
2116
2180
|
setItemToDelete(item);
|
|
2117
|
-
|
|
2181
|
+
window.setTimeout(() => {
|
|
2182
|
+
setDeleteDialogOpen(true);
|
|
2183
|
+
}, 0);
|
|
2118
2184
|
};
|
|
2119
2185
|
const handleDelete = async () => {
|
|
2120
|
-
if (!itemToDelete)
|
|
2186
|
+
if (!itemToDelete) {
|
|
2187
|
+
console.debug("[CRUD][delete] handleDelete blocked: no itemToDelete");
|
|
2188
|
+
return;
|
|
2189
|
+
}
|
|
2190
|
+
console.debug("[CRUD][delete] handleDelete start", {
|
|
2191
|
+
itemId: itemToDelete.id,
|
|
2192
|
+
resource: resourceName
|
|
2193
|
+
});
|
|
2121
2194
|
setDeleteLoading(true);
|
|
2122
2195
|
try {
|
|
2123
2196
|
const res = await fetch(`${apiEndpoint}/${itemToDelete.id}`, { method: "DELETE" });
|
|
2197
|
+
console.debug("[CRUD][delete] handleDelete response", {
|
|
2198
|
+
itemId: itemToDelete.id,
|
|
2199
|
+
ok: res.ok,
|
|
2200
|
+
status: res.status
|
|
2201
|
+
});
|
|
2124
2202
|
if (res.ok) {
|
|
2203
|
+
setData((prev) => prev.filter((row) => String(row.id) !== String(itemToDelete.id)));
|
|
2125
2204
|
setRefreshKey((k) => k + 1);
|
|
2126
|
-
|
|
2127
|
-
setItemToDelete(null);
|
|
2205
|
+
closeDeleteDialog();
|
|
2128
2206
|
} else {
|
|
2129
2207
|
console.error("Failed to delete item");
|
|
2130
2208
|
}
|
|
2131
2209
|
} catch (err) {
|
|
2132
2210
|
console.error("Error deleting item:", err);
|
|
2133
2211
|
} finally {
|
|
2212
|
+
console.debug("[CRUD][delete] handleDelete finally", {
|
|
2213
|
+
itemId: itemToDelete?.id
|
|
2214
|
+
});
|
|
2134
2215
|
setDeleteLoading(false);
|
|
2135
2216
|
}
|
|
2136
2217
|
};
|
|
@@ -2165,18 +2246,33 @@ function AdminCRUD({
|
|
|
2165
2246
|
}
|
|
2166
2247
|
const result = await res.json();
|
|
2167
2248
|
if (result.inviteLink) {
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
Note: ${result.note}`);
|
|
2249
|
+
const note = result.note ? ` ${result.note}` : "";
|
|
2250
|
+
import_sonner4.toast.success(`New invite link generated.${note}`);
|
|
2171
2251
|
}
|
|
2172
2252
|
} catch (error2) {
|
|
2173
2253
|
console.error("Error regenerating invite:", error2);
|
|
2174
|
-
|
|
2254
|
+
import_sonner4.toast.error(error2 instanceof Error ? error2.message : "Failed to regenerate invite");
|
|
2175
2255
|
}
|
|
2176
2256
|
};
|
|
2177
2257
|
const addEditPage = addEditPageUrl && addEditPageUrl.length > 0;
|
|
2178
2258
|
const hasCustomView = customViewPageUrl && customViewPageUrl.length > 0;
|
|
2179
2259
|
const resourceName = apiEndpoint.replace("/api/", "");
|
|
2260
|
+
const hasDetailViewPage = (/* @__PURE__ */ new Set(["contacts", "form-submissions", "orders", "payments"])).has(resourceName);
|
|
2261
|
+
const getRowDestination = (item) => {
|
|
2262
|
+
if (item.id == null) return "modal";
|
|
2263
|
+
if (addEditPage) return `${addEditPageUrl}/${item.id}`;
|
|
2264
|
+
if (hasCustomView) return `${customViewPageUrl}/${item.id}`;
|
|
2265
|
+
if (hasDetailViewPage) return `/admin/${resourceName}/${item.id}/view`;
|
|
2266
|
+
return "modal";
|
|
2267
|
+
};
|
|
2268
|
+
const handleRowClick = (item) => {
|
|
2269
|
+
const destination = getRowDestination(item);
|
|
2270
|
+
if (destination === "modal") {
|
|
2271
|
+
handleEdit(item);
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
router.push(destination);
|
|
2275
|
+
};
|
|
2180
2276
|
const handleExport = async () => {
|
|
2181
2277
|
try {
|
|
2182
2278
|
const res = await fetch(`${apiEndpoint}/export?format=csv`);
|
|
@@ -2190,7 +2286,7 @@ Note: ${result.note}`);
|
|
|
2190
2286
|
URL.revokeObjectURL(url);
|
|
2191
2287
|
} catch (err) {
|
|
2192
2288
|
console.error("Export error:", err);
|
|
2193
|
-
|
|
2289
|
+
import_sonner4.toast.error("Failed to export data");
|
|
2194
2290
|
}
|
|
2195
2291
|
};
|
|
2196
2292
|
if (loading) {
|
|
@@ -2330,8 +2426,8 @@ Note: ${result.note}`);
|
|
|
2330
2426
|
{
|
|
2331
2427
|
type: "text",
|
|
2332
2428
|
placeholder: "Search...",
|
|
2333
|
-
value:
|
|
2334
|
-
onChange: (e) =>
|
|
2429
|
+
value: searchInput,
|
|
2430
|
+
onChange: (e) => setSearchInput(e.target.value),
|
|
2335
2431
|
onKeyDown: (e) => e.key === "Enter" && handleSearch(),
|
|
2336
2432
|
className: "border-0 focus-visible:ring-0 focus-visible:ring-offset-0 rounded-l-md rounded-r-none flex-1 min-w-0"
|
|
2337
2433
|
}
|
|
@@ -2425,6 +2521,10 @@ Note: ${result.note}`);
|
|
|
2425
2521
|
}),
|
|
2426
2522
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Button, { variant: "outline", size: "sm", className: "h-8", onClick: handleApplyFilters, children: "Apply filters" })
|
|
2427
2523
|
] }),
|
|
2524
|
+
isRefetching && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "mb-3 flex items-center text-xs text-gray-500", children: [
|
|
2525
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "animate-spin rounded-full h-3.5 w-3.5 border-2 border-gray-300 border-t-gray-600" }),
|
|
2526
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ml-2", children: "Refreshing list..." })
|
|
2527
|
+
] }),
|
|
2428
2528
|
isMobile ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex flex-col gap-2 min-w-0", children: data && data.length > 0 ? data.map((item, index) => {
|
|
2429
2529
|
const displayCols = columns?.slice(0, 4) ?? [];
|
|
2430
2530
|
const primary = displayCols[0];
|
|
@@ -2432,27 +2532,17 @@ Note: ${result.note}`);
|
|
|
2432
2532
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2433
2533
|
"div",
|
|
2434
2534
|
{
|
|
2435
|
-
className: "min-w-0 rounded-md border border-gray-200 bg-gray-50/50 p-2.5 flex flex-col gap-1",
|
|
2535
|
+
className: "min-w-0 rounded-md border border-gray-200 bg-gray-50/50 p-2.5 flex flex-col gap-1 cursor-pointer",
|
|
2536
|
+
onClick: () => handleRowClick(item),
|
|
2436
2537
|
children: [
|
|
2437
2538
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
|
|
2438
2539
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "min-w-0 break-words flex-1 leading-tight", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "font-medium", children: primaryVal != null ? String(primaryVal) : "\u2014" }) }),
|
|
2439
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenu, { children: [
|
|
2540
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenu, { children: [
|
|
2440
2541
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Button, { variant: "ghost", size: "icon", className: "h-8 w-8 shrink-0", children: [
|
|
2441
2542
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react12.MoreVertical, { className: "h-4 w-4" }),
|
|
2442
2543
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "sr-only", children: "Actions" })
|
|
2443
2544
|
] }) }),
|
|
2444
2545
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenuContent, { align: "end", children: [
|
|
2445
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2446
|
-
import_link4.default,
|
|
2447
|
-
{
|
|
2448
|
-
href: hasCustomView ? `${customViewPageUrl}/${item.id}` : `/admin/${resourceName}/${item.id}/view`,
|
|
2449
|
-
className: "flex items-center gap-2",
|
|
2450
|
-
children: [
|
|
2451
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react12.Eye, { className: "h-4 w-4" }),
|
|
2452
|
-
"View"
|
|
2453
|
-
]
|
|
2454
|
-
}
|
|
2455
|
-
) }),
|
|
2456
2546
|
!addEditPage && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenuItem, { onClick: () => handleEdit(item), children: [
|
|
2457
2547
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react12.Edit, { className: "h-4 w-4 mr-2" }),
|
|
2458
2548
|
"Edit"
|
|
@@ -2477,7 +2567,7 @@ Note: ${result.note}`);
|
|
|
2477
2567
|
}
|
|
2478
2568
|
)
|
|
2479
2569
|
] })
|
|
2480
|
-
] })
|
|
2570
|
+
] }, `mobile-actions-${String(item.id ?? index)}-${refreshKey}`) })
|
|
2481
2571
|
] }),
|
|
2482
2572
|
displayCols.slice(1).map((col) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "text-sm text-gray-600 flex gap-1 min-w-0 leading-tight", children: [
|
|
2483
2573
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "shrink-0", children: [
|
|
@@ -2486,7 +2576,7 @@ Note: ${result.note}`);
|
|
|
2486
2576
|
] }),
|
|
2487
2577
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "min-w-0 break-words", children: formatCellValue(getNestedValue(item, col.field || col.key), col) })
|
|
2488
2578
|
] }, col.field || col.key)),
|
|
2489
|
-
showGroupColumn && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "text-sm text-gray-600 flex gap-1 items-center min-w-0", children: [
|
|
2579
|
+
showGroupColumn && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "text-sm text-gray-600 flex gap-1 items-center min-w-0", onClick: (e) => e.stopPropagation(), children: [
|
|
2490
2580
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "shrink-0", children: "Group:" }),
|
|
2491
2581
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2492
2582
|
"select",
|
|
@@ -2526,70 +2616,94 @@ Note: ${result.note}`);
|
|
|
2526
2616
|
showGroupColumn && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableHead, { className: "min-w-[140px]", children: "Group" }),
|
|
2527
2617
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableHead, { className: "w-12 text-center", children: "Actions" })
|
|
2528
2618
|
] }) }),
|
|
2529
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableBody, { children: data && data.length > 0 ? data.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
if (v) handleUserGroupChange(Number(item.id), Number(v));
|
|
2543
|
-
},
|
|
2544
|
-
children: [
|
|
2545
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: "", children: "\u2014" }),
|
|
2546
|
-
roleOptions.map((g) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: g.id, children: g.name }, g.id))
|
|
2547
|
-
]
|
|
2548
|
-
}
|
|
2549
|
-
) }),
|
|
2550
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableCell, { className: "text-center", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenu, { children: [
|
|
2551
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", children: [
|
|
2552
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react12.MoreVertical, { className: "h-4 w-4" }),
|
|
2553
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "sr-only", children: "Actions" })
|
|
2554
|
-
] }) }),
|
|
2555
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenuContent, { align: "end", children: [
|
|
2556
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2557
|
-
import_link4.default,
|
|
2619
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableBody, { children: data && data.length > 0 ? data.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2620
|
+
TableRow,
|
|
2621
|
+
{
|
|
2622
|
+
className: "cursor-pointer",
|
|
2623
|
+
onClick: () => handleRowClick(item),
|
|
2624
|
+
children: [
|
|
2625
|
+
columns && columns.map((col, colIndex) => {
|
|
2626
|
+
const fieldKey = col.field || col.key;
|
|
2627
|
+
const value = getNestedValue(item, fieldKey);
|
|
2628
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableCell, { children: formatCellValue(value, col) }, `${item.id}-${colIndex}-${fieldKey}`);
|
|
2629
|
+
}),
|
|
2630
|
+
showGroupColumn && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableCell, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2631
|
+
"select",
|
|
2558
2632
|
{
|
|
2559
|
-
|
|
2560
|
-
|
|
2633
|
+
className: "max-w-[180px] rounded border border-gray-300 px-2 py-1 text-sm dark:border-gray-600 dark:bg-gray-800",
|
|
2634
|
+
value: item.groupId != null && roleOptions.some((g) => g.id === item.groupId) ? String(item.groupId) : "",
|
|
2635
|
+
onChange: (e) => {
|
|
2636
|
+
const v = e.target.value;
|
|
2637
|
+
if (v) handleUserGroupChange(Number(item.id), Number(v));
|
|
2638
|
+
},
|
|
2561
2639
|
children: [
|
|
2562
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2563
|
-
"
|
|
2640
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: "", children: "\u2014" }),
|
|
2641
|
+
roleOptions.map((g) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: g.id, children: g.name }, g.id))
|
|
2564
2642
|
]
|
|
2565
2643
|
}
|
|
2566
2644
|
) }),
|
|
2567
|
-
|
|
2568
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
"
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2645
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableCell, { className: "text-center", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
|
|
2646
|
+
!addEditPage && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2647
|
+
Button,
|
|
2648
|
+
{
|
|
2649
|
+
variant: "outline",
|
|
2650
|
+
size: "icon",
|
|
2651
|
+
className: "h-7 w-7",
|
|
2652
|
+
onClick: () => handleEdit(item),
|
|
2653
|
+
title: "Edit",
|
|
2654
|
+
children: [
|
|
2655
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react12.Edit, { className: "h-3.5 w-3.5" }),
|
|
2656
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "sr-only", children: "Edit" })
|
|
2657
|
+
]
|
|
2658
|
+
}
|
|
2659
|
+
),
|
|
2660
|
+
addEditPage && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2661
|
+
Button,
|
|
2662
|
+
{
|
|
2663
|
+
variant: "outline",
|
|
2664
|
+
size: "icon",
|
|
2665
|
+
className: "h-7 w-7",
|
|
2666
|
+
onClick: () => router.push(addEditPageUrl + "/" + item.id),
|
|
2667
|
+
title: "Edit",
|
|
2668
|
+
children: [
|
|
2669
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react12.Edit, { className: "h-3.5 w-3.5" }),
|
|
2670
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "sr-only", children: "Edit" })
|
|
2671
|
+
]
|
|
2672
|
+
}
|
|
2673
|
+
),
|
|
2674
|
+
resourceName === "users" && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2675
|
+
Button,
|
|
2676
|
+
{
|
|
2677
|
+
variant: "outline",
|
|
2678
|
+
size: "icon",
|
|
2679
|
+
className: "h-7 w-7",
|
|
2680
|
+
onClick: () => handleRegenerateInvite(Number(item.id)),
|
|
2681
|
+
title: "Regenerate Invite",
|
|
2682
|
+
children: [
|
|
2683
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react12.RefreshCw, { className: "h-3.5 w-3.5" }),
|
|
2684
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "sr-only", children: "Regenerate Invite" })
|
|
2685
|
+
]
|
|
2686
|
+
}
|
|
2687
|
+
),
|
|
2688
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2689
|
+
Button,
|
|
2690
|
+
{
|
|
2691
|
+
variant: "outline",
|
|
2692
|
+
size: "icon",
|
|
2693
|
+
className: "h-7 w-7 border-red-300 text-red-600 hover:text-red-700",
|
|
2694
|
+
onClick: () => handleDeleteClick(item),
|
|
2695
|
+
title: "Delete",
|
|
2696
|
+
children: [
|
|
2697
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react12.Trash2, { className: "h-3.5 w-3.5" }),
|
|
2698
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "sr-only", children: "Delete" })
|
|
2699
|
+
]
|
|
2700
|
+
}
|
|
2701
|
+
)
|
|
2702
|
+
] }) })
|
|
2703
|
+
]
|
|
2704
|
+
},
|
|
2705
|
+
String(item.id ?? index)
|
|
2706
|
+
)) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2593
2707
|
TableCell,
|
|
2594
2708
|
{
|
|
2595
2709
|
colSpan: columns ? columns.length + 1 + (showGroupColumn ? 1 : 0) : 1,
|
|
@@ -2635,21 +2749,35 @@ Note: ${result.note}`);
|
|
|
2635
2749
|
existingData: editingItem
|
|
2636
2750
|
}
|
|
2637
2751
|
),
|
|
2638
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2752
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2753
|
+
Dialog,
|
|
2754
|
+
{
|
|
2755
|
+
open: deleteDialogOpen,
|
|
2756
|
+
onOpenChange: (open) => {
|
|
2757
|
+
console.debug("[CRUD][delete] dialog onOpenChange", {
|
|
2758
|
+
nextOpen: open,
|
|
2759
|
+
itemId: itemToDelete?.id
|
|
2760
|
+
});
|
|
2761
|
+
if (!open) closeDeleteDialog();
|
|
2762
|
+
else setDeleteDialogOpen(true);
|
|
2763
|
+
},
|
|
2764
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DialogContent, { children: [
|
|
2765
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DialogHeader, { children: [
|
|
2766
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DialogTitle, { children: "Confirm Delete" }),
|
|
2767
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DialogDescription, { children: "Are you sure you want to delete this item? This action cannot be undone." }),
|
|
2768
|
+
itemToDelete && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "mt-2 p-2 bg-gray-50 rounded", children: [
|
|
2769
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("strong", { children: "Item:" }),
|
|
2770
|
+
" ",
|
|
2771
|
+
String(itemToDelete.name ?? itemToDelete.title ?? itemToDelete.id ?? "")
|
|
2772
|
+
] })
|
|
2773
|
+
] }),
|
|
2774
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DialogFooter, { children: [
|
|
2775
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Button, { variant: "outline", onClick: closeDeleteDialog, disabled: deleteLoading, children: "Cancel" }),
|
|
2776
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Button, { variant: "destructive", onClick: handleDelete, disabled: deleteLoading, children: deleteLoading ? "Deleting..." : "Delete" })
|
|
2777
|
+
] })
|
|
2646
2778
|
] })
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Button, { variant: "outline", onClick: () => setDeleteDialogOpen(false), disabled: deleteLoading, children: "Cancel" }),
|
|
2650
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Button, { variant: "destructive", onClick: handleDelete, disabled: deleteLoading, children: deleteLoading ? "Deleting..." : "Delete" })
|
|
2651
|
-
] })
|
|
2652
|
-
] }) }),
|
|
2779
|
+
}
|
|
2780
|
+
),
|
|
2653
2781
|
bulkDialogOpen && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
2654
2782
|
BulkUploadDialog,
|
|
2655
2783
|
{
|
|
@@ -2665,7 +2793,7 @@ Note: ${result.note}`);
|
|
|
2665
2793
|
}
|
|
2666
2794
|
|
|
2667
2795
|
// src/components/Admin/FormBuilder.tsx
|
|
2668
|
-
var
|
|
2796
|
+
var import_react15 = require("react");
|
|
2669
2797
|
|
|
2670
2798
|
// src/components/ui/badge.tsx
|
|
2671
2799
|
var import_class_variance_authority4 = require("class-variance-authority");
|
|
@@ -2692,10 +2820,10 @@ function Badge({ className, variant, ...props }) {
|
|
|
2692
2820
|
|
|
2693
2821
|
// src/components/Admin/FormBuilder.tsx
|
|
2694
2822
|
var import_lucide_react14 = require("lucide-react");
|
|
2695
|
-
var
|
|
2823
|
+
var import_navigation5 = require("next/navigation");
|
|
2696
2824
|
|
|
2697
2825
|
// src/components/Admin/FormBuilder/FieldConfiguration.tsx
|
|
2698
|
-
var
|
|
2826
|
+
var import_react14 = require("react");
|
|
2699
2827
|
var import_lucide_react13 = require("lucide-react");
|
|
2700
2828
|
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2701
2829
|
var FIELD_TYPES = [
|
|
@@ -2716,7 +2844,7 @@ var COLUMN_WIDTHS = [
|
|
|
2716
2844
|
{ value: 12, label: "Full Width (12 cols)" }
|
|
2717
2845
|
];
|
|
2718
2846
|
function FieldConfiguration({ field, onUpdate }) {
|
|
2719
|
-
const [newOption, setNewOption] = (0,
|
|
2847
|
+
const [newOption, setNewOption] = (0, import_react14.useState)("");
|
|
2720
2848
|
const addOption = () => {
|
|
2721
2849
|
if (newOption.trim()) {
|
|
2722
2850
|
const currentOptions = field.options || [];
|
|
@@ -2989,7 +3117,7 @@ function FieldConfiguration({ field, onUpdate }) {
|
|
|
2989
3117
|
// src/components/Admin/FormBuilder.tsx
|
|
2990
3118
|
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2991
3119
|
function FormBuilder({ formId }) {
|
|
2992
|
-
const [formData, setFormData] = (0,
|
|
3120
|
+
const [formData, setFormData] = (0, import_react15.useState)({
|
|
2993
3121
|
name: "",
|
|
2994
3122
|
description: "",
|
|
2995
3123
|
campaign: "",
|
|
@@ -2997,12 +3125,12 @@ function FormBuilder({ formId }) {
|
|
|
2997
3125
|
published: false,
|
|
2998
3126
|
fields: []
|
|
2999
3127
|
});
|
|
3000
|
-
const [errors, setErrors] = (0,
|
|
3001
|
-
const [isLoading, setIsLoading] = (0,
|
|
3002
|
-
const [isSaving, setIsSaving] = (0,
|
|
3003
|
-
const [selectedField, setSelectedField] = (0,
|
|
3004
|
-
const router = (0,
|
|
3005
|
-
const loadFormData = (0,
|
|
3128
|
+
const [errors, setErrors] = (0, import_react15.useState)([]);
|
|
3129
|
+
const [isLoading, setIsLoading] = (0, import_react15.useState)(false);
|
|
3130
|
+
const [isSaving, setIsSaving] = (0, import_react15.useState)(false);
|
|
3131
|
+
const [selectedField, setSelectedField] = (0, import_react15.useState)(null);
|
|
3132
|
+
const router = (0, import_navigation5.useRouter)();
|
|
3133
|
+
const loadFormData = (0, import_react15.useCallback)(async () => {
|
|
3006
3134
|
try {
|
|
3007
3135
|
setIsLoading(true);
|
|
3008
3136
|
const response = await fetch(`/api/forms/${formId}`);
|
|
@@ -3030,12 +3158,12 @@ function FormBuilder({ formId }) {
|
|
|
3030
3158
|
setIsLoading(false);
|
|
3031
3159
|
}
|
|
3032
3160
|
}, [formId]);
|
|
3033
|
-
(0,
|
|
3161
|
+
(0, import_react15.useEffect)(() => {
|
|
3034
3162
|
if (formId) {
|
|
3035
3163
|
loadFormData();
|
|
3036
3164
|
}
|
|
3037
3165
|
}, [formId, loadFormData]);
|
|
3038
|
-
(0,
|
|
3166
|
+
(0, import_react15.useEffect)(() => {
|
|
3039
3167
|
if (formData.name && !formData.slug) {
|
|
3040
3168
|
const generatedSlug = formData.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
3041
3169
|
setFormData((prev) => ({ ...prev, slug: generatedSlug }));
|
|
@@ -3166,19 +3294,34 @@ function FormBuilder({ formId }) {
|
|
|
3166
3294
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h1", { className: "text-xl font-bold text-white", children: formId ? "Edit Form" : "Create New Form" }),
|
|
3167
3295
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-xs text-gray-300 mt-0.5", children: "Design your form with drag-and-drop fields" })
|
|
3168
3296
|
] }),
|
|
3169
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3297
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center space-x-3", children: [
|
|
3298
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
3299
|
+
Button,
|
|
3300
|
+
{
|
|
3301
|
+
variant: "outline",
|
|
3302
|
+
size: "icon",
|
|
3303
|
+
className: "md:hidden h-8 w-8 bg-white text-gray-800 hover:bg-gray-100 border-0",
|
|
3304
|
+
onClick: () => router.push("/admin/forms"),
|
|
3305
|
+
children: [
|
|
3306
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react14.X, { className: "h-4 w-4" }),
|
|
3307
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: "Close" })
|
|
3308
|
+
]
|
|
3309
|
+
}
|
|
3310
|
+
),
|
|
3311
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
3312
|
+
Button,
|
|
3313
|
+
{
|
|
3314
|
+
variant: "outline",
|
|
3315
|
+
size: "sm",
|
|
3316
|
+
className: "hidden md:inline-flex bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
3317
|
+
onClick: () => router.push("/admin/forms"),
|
|
3318
|
+
children: [
|
|
3319
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react14.X, { className: "h-4 w-4 mr-2" }),
|
|
3320
|
+
"Close"
|
|
3321
|
+
]
|
|
3322
|
+
}
|
|
3323
|
+
)
|
|
3324
|
+
] })
|
|
3182
3325
|
] }) }),
|
|
3183
3326
|
errors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "bg-red-50 border-l-4 border-red-400 p-4 mx-6 mt-4", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "ml-3", children: [
|
|
3184
3327
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h3", { className: "text-sm font-medium text-red-800", children: "Please fix the following errors:" }),
|
|
@@ -3356,11 +3499,24 @@ function FormBuilder({ formId }) {
|
|
|
3356
3499
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "bg-gray-50 border-t border-gray-200 px-6 py-4", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
3357
3500
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "text-sm text-gray-600", children: formId ? "Editing existing form" : "Creating new form" }),
|
|
3358
3501
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-center space-x-3", children: [
|
|
3502
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
3503
|
+
Button,
|
|
3504
|
+
{
|
|
3505
|
+
variant: "outline",
|
|
3506
|
+
size: "icon",
|
|
3507
|
+
className: "md:hidden h-8 w-8 bg-white text-gray-800 hover:bg-gray-100 border-0",
|
|
3508
|
+
onClick: () => router.push("/admin/forms"),
|
|
3509
|
+
children: [
|
|
3510
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react14.X, { className: "h-4 w-4" }),
|
|
3511
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: "Close" })
|
|
3512
|
+
]
|
|
3513
|
+
}
|
|
3514
|
+
),
|
|
3359
3515
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
3360
3516
|
Button,
|
|
3361
3517
|
{
|
|
3362
3518
|
variant: "outline",
|
|
3363
|
-
className: "bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
3519
|
+
className: "hidden md:inline-flex bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
3364
3520
|
onClick: () => router.push("/admin/forms"),
|
|
3365
3521
|
children: "Close"
|
|
3366
3522
|
}
|
|
@@ -3370,19 +3526,45 @@ function FormBuilder({ formId }) {
|
|
|
3370
3526
|
{
|
|
3371
3527
|
onClick: () => handleSave(false),
|
|
3372
3528
|
disabled: isSaving,
|
|
3373
|
-
|
|
3529
|
+
size: "icon",
|
|
3530
|
+
className: "md:hidden h-8 w-8 bg-white text-gray-800 hover:bg-gray-100 border-0",
|
|
3531
|
+
children: [
|
|
3532
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react14.Save, { className: "h-4 w-4" }),
|
|
3533
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: isSaving ? "Saving..." : "Save" })
|
|
3534
|
+
]
|
|
3535
|
+
}
|
|
3536
|
+
),
|
|
3537
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
3538
|
+
Button,
|
|
3539
|
+
{
|
|
3540
|
+
onClick: () => handleSave(false),
|
|
3541
|
+
disabled: isSaving,
|
|
3542
|
+
className: "hidden md:inline-flex bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
3374
3543
|
children: [
|
|
3375
3544
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react14.Save, { className: "h-4 w-4 mr-2" }),
|
|
3376
3545
|
isSaving ? "Saving..." : "Save"
|
|
3377
3546
|
]
|
|
3378
3547
|
}
|
|
3379
3548
|
),
|
|
3549
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
3550
|
+
Button,
|
|
3551
|
+
{
|
|
3552
|
+
onClick: () => handleSave(true),
|
|
3553
|
+
disabled: isSaving,
|
|
3554
|
+
size: "icon",
|
|
3555
|
+
className: "md:hidden h-8 w-8 bg-gray-800 text-white hover:bg-gray-700",
|
|
3556
|
+
children: [
|
|
3557
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react14.Check, { className: "h-4 w-4" }),
|
|
3558
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: isSaving ? "Publishing..." : "Publish" })
|
|
3559
|
+
]
|
|
3560
|
+
}
|
|
3561
|
+
),
|
|
3380
3562
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
3381
3563
|
Button,
|
|
3382
3564
|
{
|
|
3383
3565
|
onClick: () => handleSave(true),
|
|
3384
3566
|
disabled: isSaving,
|
|
3385
|
-
className: "bg-gray-800 text-white hover:bg-gray-700 text-xs",
|
|
3567
|
+
className: "hidden md:inline-flex bg-gray-800 text-white hover:bg-gray-700 text-xs",
|
|
3386
3568
|
children: isSaving ? "Publishing..." : "Publish"
|
|
3387
3569
|
}
|
|
3388
3570
|
)
|
|
@@ -3392,10 +3574,10 @@ function FormBuilder({ formId }) {
|
|
|
3392
3574
|
}
|
|
3393
3575
|
|
|
3394
3576
|
// src/components/Admin/BlogEditorPage.tsx
|
|
3395
|
-
var
|
|
3577
|
+
var import_react21 = require("react");
|
|
3396
3578
|
|
|
3397
3579
|
// src/components/ui/image-upload.tsx
|
|
3398
|
-
var
|
|
3580
|
+
var import_react16 = require("react");
|
|
3399
3581
|
var import_lucide_react15 = require("lucide-react");
|
|
3400
3582
|
var import_image3 = __toESM(require("next/image"), 1);
|
|
3401
3583
|
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
@@ -3410,12 +3592,12 @@ function ImageUpload({
|
|
|
3410
3592
|
// 10MB default
|
|
3411
3593
|
acceptedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"]
|
|
3412
3594
|
}) {
|
|
3413
|
-
const [isDragOver, setIsDragOver] = (0,
|
|
3414
|
-
const [isUploading, setIsUploading] = (0,
|
|
3415
|
-
const [uploadProgress, setUploadProgress] = (0,
|
|
3416
|
-
const [error, setError] = (0,
|
|
3417
|
-
const fileInputRef = (0,
|
|
3418
|
-
const handleFileSelect = (0,
|
|
3595
|
+
const [isDragOver, setIsDragOver] = (0, import_react16.useState)(false);
|
|
3596
|
+
const [isUploading, setIsUploading] = (0, import_react16.useState)(false);
|
|
3597
|
+
const [uploadProgress, setUploadProgress] = (0, import_react16.useState)(0);
|
|
3598
|
+
const [error, setError] = (0, import_react16.useState)(null);
|
|
3599
|
+
const fileInputRef = (0, import_react16.useRef)(null);
|
|
3600
|
+
const handleFileSelect = (0, import_react16.useCallback)(async (file) => {
|
|
3419
3601
|
setError(null);
|
|
3420
3602
|
setUploadProgress(0);
|
|
3421
3603
|
if (!acceptedTypes.includes(file.type)) {
|
|
@@ -3447,7 +3629,7 @@ function ImageUpload({
|
|
|
3447
3629
|
setIsUploading(false);
|
|
3448
3630
|
}
|
|
3449
3631
|
}, [onChange, acceptedTypes, maxSize]);
|
|
3450
|
-
const handleDrop = (0,
|
|
3632
|
+
const handleDrop = (0, import_react16.useCallback)((e) => {
|
|
3451
3633
|
e.preventDefault();
|
|
3452
3634
|
setIsDragOver(false);
|
|
3453
3635
|
const files = Array.from(e.dataTransfer.files);
|
|
@@ -3455,21 +3637,21 @@ function ImageUpload({
|
|
|
3455
3637
|
handleFileSelect(files[0]);
|
|
3456
3638
|
}
|
|
3457
3639
|
}, [handleFileSelect]);
|
|
3458
|
-
const handleDragOver = (0,
|
|
3640
|
+
const handleDragOver = (0, import_react16.useCallback)((e) => {
|
|
3459
3641
|
e.preventDefault();
|
|
3460
3642
|
setIsDragOver(true);
|
|
3461
3643
|
}, []);
|
|
3462
|
-
const handleDragLeave = (0,
|
|
3644
|
+
const handleDragLeave = (0, import_react16.useCallback)((e) => {
|
|
3463
3645
|
e.preventDefault();
|
|
3464
3646
|
setIsDragOver(false);
|
|
3465
3647
|
}, []);
|
|
3466
|
-
const handleFileInputChange = (0,
|
|
3648
|
+
const handleFileInputChange = (0, import_react16.useCallback)((e) => {
|
|
3467
3649
|
const files = e.target.files;
|
|
3468
3650
|
if (files && files.length > 0) {
|
|
3469
3651
|
handleFileSelect(files[0]);
|
|
3470
3652
|
}
|
|
3471
3653
|
}, [handleFileSelect]);
|
|
3472
|
-
const handleRemove = (0,
|
|
3654
|
+
const handleRemove = (0, import_react16.useCallback)(() => {
|
|
3473
3655
|
if (onRemove) {
|
|
3474
3656
|
onRemove();
|
|
3475
3657
|
} else {
|
|
@@ -3478,7 +3660,7 @@ function ImageUpload({
|
|
|
3478
3660
|
setError(null);
|
|
3479
3661
|
setUploadProgress(0);
|
|
3480
3662
|
}, [onChange, onRemove]);
|
|
3481
|
-
const openFileDialog = (0,
|
|
3663
|
+
const openFileDialog = (0, import_react16.useCallback)(() => {
|
|
3482
3664
|
fileInputRef.current?.click();
|
|
3483
3665
|
}, []);
|
|
3484
3666
|
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: cn("space-y-3", className), children: [
|
|
@@ -3596,10 +3778,10 @@ function ImageUpload({
|
|
|
3596
3778
|
|
|
3597
3779
|
// src/components/Admin/BlogEditorPage.tsx
|
|
3598
3780
|
var import_lucide_react19 = require("lucide-react");
|
|
3599
|
-
var
|
|
3781
|
+
var import_navigation6 = require("next/navigation");
|
|
3600
3782
|
|
|
3601
3783
|
// src/components/Admin/TagAutocomplete.tsx
|
|
3602
|
-
var
|
|
3784
|
+
var import_react17 = require("react");
|
|
3603
3785
|
var import_lucide_react16 = require("lucide-react");
|
|
3604
3786
|
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
3605
3787
|
function TagAutocomplete({
|
|
@@ -3608,14 +3790,14 @@ function TagAutocomplete({
|
|
|
3608
3790
|
placeholder = "Search or create tags...",
|
|
3609
3791
|
className = ""
|
|
3610
3792
|
}) {
|
|
3611
|
-
const [inputValue, setInputValue] = (0,
|
|
3612
|
-
const [suggestions, setSuggestions] = (0,
|
|
3613
|
-
const [isLoading, setIsLoading] = (0,
|
|
3614
|
-
const [showSuggestions, setShowSuggestions] = (0,
|
|
3615
|
-
const [isCreating, setIsCreating] = (0,
|
|
3616
|
-
const inputRef = (0,
|
|
3617
|
-
const suggestionsRef = (0,
|
|
3618
|
-
const fetchSuggestions = (0,
|
|
3793
|
+
const [inputValue, setInputValue] = (0, import_react17.useState)("");
|
|
3794
|
+
const [suggestions, setSuggestions] = (0, import_react17.useState)([]);
|
|
3795
|
+
const [isLoading, setIsLoading] = (0, import_react17.useState)(false);
|
|
3796
|
+
const [showSuggestions, setShowSuggestions] = (0, import_react17.useState)(false);
|
|
3797
|
+
const [isCreating, setIsCreating] = (0, import_react17.useState)(false);
|
|
3798
|
+
const inputRef = (0, import_react17.useRef)(null);
|
|
3799
|
+
const suggestionsRef = (0, import_react17.useRef)(null);
|
|
3800
|
+
const fetchSuggestions = (0, import_react17.useCallback)(async (query) => {
|
|
3619
3801
|
if (!query.trim()) {
|
|
3620
3802
|
setSuggestions([]);
|
|
3621
3803
|
return;
|
|
@@ -3637,7 +3819,7 @@ function TagAutocomplete({
|
|
|
3637
3819
|
setIsLoading(false);
|
|
3638
3820
|
}
|
|
3639
3821
|
}, [selectedTags]);
|
|
3640
|
-
(0,
|
|
3822
|
+
(0, import_react17.useEffect)(() => {
|
|
3641
3823
|
const timeoutId = setTimeout(() => {
|
|
3642
3824
|
fetchSuggestions(inputValue);
|
|
3643
3825
|
}, 300);
|
|
@@ -3701,7 +3883,7 @@ function TagAutocomplete({
|
|
|
3701
3883
|
inputRef.current?.blur();
|
|
3702
3884
|
}
|
|
3703
3885
|
};
|
|
3704
|
-
(0,
|
|
3886
|
+
(0, import_react17.useEffect)(() => {
|
|
3705
3887
|
const handleClickOutside = (event) => {
|
|
3706
3888
|
if (suggestionsRef.current && !suggestionsRef.current.contains(event.target) && inputRef.current && !inputRef.current.contains(event.target)) {
|
|
3707
3889
|
setShowSuggestions(false);
|
|
@@ -3760,7 +3942,10 @@ function TagAutocomplete({
|
|
|
3760
3942
|
"div",
|
|
3761
3943
|
{
|
|
3762
3944
|
className: "px-4 py-2 hover:bg-gray-100 cursor-pointer text-sm",
|
|
3763
|
-
|
|
3945
|
+
onMouseDown: (e) => {
|
|
3946
|
+
e.preventDefault();
|
|
3947
|
+
handleSuggestionSelect(tag);
|
|
3948
|
+
},
|
|
3764
3949
|
children: tag.name
|
|
3765
3950
|
},
|
|
3766
3951
|
tag.id
|
|
@@ -3776,7 +3961,10 @@ function TagAutocomplete({
|
|
|
3776
3961
|
"div",
|
|
3777
3962
|
{
|
|
3778
3963
|
className: "px-4 py-2 hover:bg-gray-100 cursor-pointer text-sm text-gray-700 flex items-center",
|
|
3779
|
-
|
|
3964
|
+
onMouseDown: (e) => {
|
|
3965
|
+
e.preventDefault();
|
|
3966
|
+
handleCreateTag();
|
|
3967
|
+
},
|
|
3780
3968
|
children: [
|
|
3781
3969
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react16.Plus, { className: "h-3 w-3 mr-2" }),
|
|
3782
3970
|
'Create "',
|
|
@@ -3792,7 +3980,7 @@ function TagAutocomplete({
|
|
|
3792
3980
|
|
|
3793
3981
|
// src/components/Admin/JoditRichText.tsx
|
|
3794
3982
|
var import_dynamic = __toESM(require("next/dynamic"), 1);
|
|
3795
|
-
var
|
|
3983
|
+
var import_react18 = require("react");
|
|
3796
3984
|
var import_jodit_min = require("jodit/es2021/jodit.min.css");
|
|
3797
3985
|
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
3798
3986
|
var JoditEditor = (0, import_dynamic.default)(() => import("jodit-react").then((m) => m.default), {
|
|
@@ -3800,7 +3988,7 @@ var JoditEditor = (0, import_dynamic.default)(() => import("jodit-react").then((
|
|
|
3800
3988
|
loading: () => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "min-h-[300px] rounded-md border border-gray-200 bg-gray-50 animate-pulse" })
|
|
3801
3989
|
});
|
|
3802
3990
|
function JoditRichText({ value, onChange, placeholder, minHeight = 400 }) {
|
|
3803
|
-
const config = (0,
|
|
3991
|
+
const config = (0, import_react18.useMemo)(
|
|
3804
3992
|
() => ({
|
|
3805
3993
|
readonly: false,
|
|
3806
3994
|
placeholder: placeholder ?? "",
|
|
@@ -3864,7 +4052,7 @@ function JoditRichText({ value, onChange, placeholder, minHeight = 400 }) {
|
|
|
3864
4052
|
}
|
|
3865
4053
|
|
|
3866
4054
|
// src/components/Admin/CategoryAutocomplete.tsx
|
|
3867
|
-
var
|
|
4055
|
+
var import_react19 = require("react");
|
|
3868
4056
|
var import_lucide_react17 = require("lucide-react");
|
|
3869
4057
|
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
3870
4058
|
function CategoryAutocomplete({
|
|
@@ -3873,13 +4061,13 @@ function CategoryAutocomplete({
|
|
|
3873
4061
|
placeholder = "Search or create category...",
|
|
3874
4062
|
className = ""
|
|
3875
4063
|
}) {
|
|
3876
|
-
const [inputValue, setInputValue] = (0,
|
|
3877
|
-
const [suggestions, setSuggestions] = (0,
|
|
3878
|
-
const [isLoading, setIsLoading] = (0,
|
|
3879
|
-
const [showSuggestions, setShowSuggestions] = (0,
|
|
3880
|
-
const [isCreating, setIsCreating] = (0,
|
|
3881
|
-
const inputRef = (0,
|
|
3882
|
-
const suggestionsRef = (0,
|
|
4064
|
+
const [inputValue, setInputValue] = (0, import_react19.useState)("");
|
|
4065
|
+
const [suggestions, setSuggestions] = (0, import_react19.useState)([]);
|
|
4066
|
+
const [isLoading, setIsLoading] = (0, import_react19.useState)(false);
|
|
4067
|
+
const [showSuggestions, setShowSuggestions] = (0, import_react19.useState)(false);
|
|
4068
|
+
const [isCreating, setIsCreating] = (0, import_react19.useState)(false);
|
|
4069
|
+
const inputRef = (0, import_react19.useRef)(null);
|
|
4070
|
+
const suggestionsRef = (0, import_react19.useRef)(null);
|
|
3883
4071
|
const fetchSuggestions = async (query) => {
|
|
3884
4072
|
if (!query.trim()) {
|
|
3885
4073
|
setSuggestions([]);
|
|
@@ -3899,7 +4087,7 @@ function CategoryAutocomplete({
|
|
|
3899
4087
|
setIsLoading(false);
|
|
3900
4088
|
}
|
|
3901
4089
|
};
|
|
3902
|
-
(0,
|
|
4090
|
+
(0, import_react19.useEffect)(() => {
|
|
3903
4091
|
const timeoutId = setTimeout(() => {
|
|
3904
4092
|
fetchSuggestions(inputValue);
|
|
3905
4093
|
}, 300);
|
|
@@ -3961,7 +4149,7 @@ function CategoryAutocomplete({
|
|
|
3961
4149
|
inputRef.current?.blur();
|
|
3962
4150
|
}
|
|
3963
4151
|
};
|
|
3964
|
-
(0,
|
|
4152
|
+
(0, import_react19.useEffect)(() => {
|
|
3965
4153
|
const handleClickOutside = (event) => {
|
|
3966
4154
|
if (suggestionsRef.current && !suggestionsRef.current.contains(event.target) && inputRef.current && !inputRef.current.contains(event.target)) {
|
|
3967
4155
|
setShowSuggestions(false);
|
|
@@ -4020,7 +4208,10 @@ function CategoryAutocomplete({
|
|
|
4020
4208
|
"div",
|
|
4021
4209
|
{
|
|
4022
4210
|
className: "px-4 py-2 hover:bg-gray-100 cursor-pointer text-sm flex items-center justify-between",
|
|
4023
|
-
|
|
4211
|
+
onMouseDown: (e) => {
|
|
4212
|
+
e.preventDefault();
|
|
4213
|
+
handleSuggestionSelect(category);
|
|
4214
|
+
},
|
|
4024
4215
|
children: [
|
|
4025
4216
|
category.name,
|
|
4026
4217
|
selectedCategory === category.name && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react17.Check, { className: "h-4 w-4 text-green-500" })
|
|
@@ -4039,7 +4230,10 @@ function CategoryAutocomplete({
|
|
|
4039
4230
|
"div",
|
|
4040
4231
|
{
|
|
4041
4232
|
className: "px-4 py-2 hover:bg-gray-100 cursor-pointer text-sm text-gray-700 flex items-center",
|
|
4042
|
-
|
|
4233
|
+
onMouseDown: (e) => {
|
|
4234
|
+
e.preventDefault();
|
|
4235
|
+
handleCreateCategory();
|
|
4236
|
+
},
|
|
4043
4237
|
children: [
|
|
4044
4238
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react17.Plus, { className: "h-3 w-3 mr-2" }),
|
|
4045
4239
|
'Create "',
|
|
@@ -4054,7 +4248,7 @@ function CategoryAutocomplete({
|
|
|
4054
4248
|
}
|
|
4055
4249
|
|
|
4056
4250
|
// src/components/Admin/UserAutocomplete.tsx
|
|
4057
|
-
var
|
|
4251
|
+
var import_react20 = require("react");
|
|
4058
4252
|
var import_lucide_react18 = require("lucide-react");
|
|
4059
4253
|
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
4060
4254
|
function UserAutocomplete({
|
|
@@ -4063,12 +4257,12 @@ function UserAutocomplete({
|
|
|
4063
4257
|
placeholder = "Select author...",
|
|
4064
4258
|
className = ""
|
|
4065
4259
|
}) {
|
|
4066
|
-
const [inputValue, setInputValue] = (0,
|
|
4067
|
-
const [suggestions, setSuggestions] = (0,
|
|
4068
|
-
const [isLoading, setIsLoading] = (0,
|
|
4069
|
-
const [showSuggestions, setShowSuggestions] = (0,
|
|
4070
|
-
const inputRef = (0,
|
|
4071
|
-
const suggestionsRef = (0,
|
|
4260
|
+
const [inputValue, setInputValue] = (0, import_react20.useState)("");
|
|
4261
|
+
const [suggestions, setSuggestions] = (0, import_react20.useState)([]);
|
|
4262
|
+
const [isLoading, setIsLoading] = (0, import_react20.useState)(false);
|
|
4263
|
+
const [showSuggestions, setShowSuggestions] = (0, import_react20.useState)(false);
|
|
4264
|
+
const inputRef = (0, import_react20.useRef)(null);
|
|
4265
|
+
const suggestionsRef = (0, import_react20.useRef)(null);
|
|
4072
4266
|
const fetchSuggestions = async (query) => {
|
|
4073
4267
|
setIsLoading(true);
|
|
4074
4268
|
try {
|
|
@@ -4088,7 +4282,7 @@ function UserAutocomplete({
|
|
|
4088
4282
|
setIsLoading(false);
|
|
4089
4283
|
}
|
|
4090
4284
|
};
|
|
4091
|
-
(0,
|
|
4285
|
+
(0, import_react20.useEffect)(() => {
|
|
4092
4286
|
const timeoutId = setTimeout(() => {
|
|
4093
4287
|
fetchSuggestions(inputValue);
|
|
4094
4288
|
}, 300);
|
|
@@ -4119,7 +4313,7 @@ function UserAutocomplete({
|
|
|
4119
4313
|
inputRef.current?.blur();
|
|
4120
4314
|
}
|
|
4121
4315
|
};
|
|
4122
|
-
(0,
|
|
4316
|
+
(0, import_react20.useEffect)(() => {
|
|
4123
4317
|
const handleClickOutside = (event) => {
|
|
4124
4318
|
if (suggestionsRef.current && !suggestionsRef.current.contains(event.target) && inputRef.current && !inputRef.current.contains(event.target)) {
|
|
4125
4319
|
setShowSuggestions(false);
|
|
@@ -4166,7 +4360,10 @@ function UserAutocomplete({
|
|
|
4166
4360
|
"div",
|
|
4167
4361
|
{
|
|
4168
4362
|
className: "px-4 py-2 hover:bg-gray-100 cursor-pointer text-sm flex items-center justify-between",
|
|
4169
|
-
|
|
4363
|
+
onMouseDown: (e) => {
|
|
4364
|
+
e.preventDefault();
|
|
4365
|
+
handleSuggestionSelect(user);
|
|
4366
|
+
},
|
|
4170
4367
|
children: [
|
|
4171
4368
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col", children: [
|
|
4172
4369
|
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "font-medium", children: user.name }),
|
|
@@ -4185,40 +4382,40 @@ function UserAutocomplete({
|
|
|
4185
4382
|
// src/components/Admin/BlogEditorPage.tsx
|
|
4186
4383
|
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
4187
4384
|
function BlogEditor({ existingBlog }) {
|
|
4188
|
-
const [title, setTitle] = (0,
|
|
4189
|
-
const [slug, setSlug] = (0,
|
|
4190
|
-
const [coverImage, setCoverImage] = (0,
|
|
4385
|
+
const [title, setTitle] = (0, import_react21.useState)(existingBlog?.title || "");
|
|
4386
|
+
const [slug, setSlug] = (0, import_react21.useState)(existingBlog?.slug || "");
|
|
4387
|
+
const [coverImage, setCoverImage] = (0, import_react21.useState)(
|
|
4191
4388
|
existingBlog?.coverImage || null
|
|
4192
4389
|
);
|
|
4193
|
-
const [ogImage, setOgImage] = (0,
|
|
4390
|
+
const [ogImage, setOgImage] = (0, import_react21.useState)(
|
|
4194
4391
|
existingBlog?.seo?.ogImage || existingBlog?.ogImage || null
|
|
4195
4392
|
);
|
|
4196
|
-
const [metaTitle, setMetaTitle] = (0,
|
|
4197
|
-
const [metaDescription, setMetaDescription] = (0,
|
|
4393
|
+
const [metaTitle, setMetaTitle] = (0, import_react21.useState)(existingBlog?.seo?.title || "");
|
|
4394
|
+
const [metaDescription, setMetaDescription] = (0, import_react21.useState)(
|
|
4198
4395
|
existingBlog?.seo?.description || ""
|
|
4199
4396
|
);
|
|
4200
|
-
const [metaKeywords, setMetaKeywords] = (0,
|
|
4397
|
+
const [metaKeywords, setMetaKeywords] = (0, import_react21.useState)(
|
|
4201
4398
|
existingBlog?.seo?.keywords || ""
|
|
4202
4399
|
);
|
|
4203
|
-
const [published, setPublished] = (0,
|
|
4204
|
-
const [tags, setTags] = (0,
|
|
4400
|
+
const [published, setPublished] = (0, import_react21.useState)(existingBlog?.published || false);
|
|
4401
|
+
const [tags, setTags] = (0, import_react21.useState)(
|
|
4205
4402
|
existingBlog?.tags?.map((tag) => tag.name) || []
|
|
4206
4403
|
);
|
|
4207
|
-
const [category, setCategory] = (0,
|
|
4404
|
+
const [category, setCategory] = (0, import_react21.useState)(
|
|
4208
4405
|
existingBlog?.category?.name || null
|
|
4209
4406
|
);
|
|
4210
|
-
const [authorId, setAuthorId] = (0,
|
|
4407
|
+
const [authorId, setAuthorId] = (0, import_react21.useState)(
|
|
4211
4408
|
existingBlog?.authorId || null
|
|
4212
4409
|
);
|
|
4213
|
-
const [content, setContent] = (0,
|
|
4214
|
-
const [createdAt, setCreatedAt] = (0,
|
|
4410
|
+
const [content, setContent] = (0, import_react21.useState)(existingBlog?.content || "");
|
|
4411
|
+
const [createdAt, setCreatedAt] = (0, import_react21.useState)(
|
|
4215
4412
|
existingBlog?.createdAt ? new Date(existingBlog.createdAt).toISOString().slice(0, 16) : (/* @__PURE__ */ new Date()).toISOString().slice(0, 16)
|
|
4216
4413
|
);
|
|
4217
|
-
const [errors, setErrors] = (0,
|
|
4218
|
-
const [isLoading, setIsLoading] = (0,
|
|
4219
|
-
const [isSaving, setIsSaving] = (0,
|
|
4220
|
-
const router = (0,
|
|
4221
|
-
(0,
|
|
4414
|
+
const [errors, setErrors] = (0, import_react21.useState)([]);
|
|
4415
|
+
const [isLoading, setIsLoading] = (0, import_react21.useState)(false);
|
|
4416
|
+
const [isSaving, setIsSaving] = (0, import_react21.useState)(false);
|
|
4417
|
+
const router = (0, import_navigation6.useRouter)();
|
|
4418
|
+
(0, import_react21.useEffect)(() => {
|
|
4222
4419
|
if (title && !slug) {
|
|
4223
4420
|
const generatedSlug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
4224
4421
|
setSlug(generatedSlug);
|
|
@@ -4343,11 +4540,25 @@ function BlogEditor({ existingBlog }) {
|
|
|
4343
4540
|
),
|
|
4344
4541
|
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "text-xs text-gray-300", children: "Published" })
|
|
4345
4542
|
] }),
|
|
4543
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
4544
|
+
Button,
|
|
4545
|
+
{
|
|
4546
|
+
size: "icon",
|
|
4547
|
+
className: "md:hidden h-8 w-8 bg-white text-gray-800 hover:bg-gray-100 border-0",
|
|
4548
|
+
onClick: () => handleSave(published),
|
|
4549
|
+
disabled: isSaving,
|
|
4550
|
+
title: isSaving ? "Saving..." : "Save",
|
|
4551
|
+
children: [
|
|
4552
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react19.Save, { className: "h-4 w-4" }),
|
|
4553
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "sr-only", children: isSaving ? "Saving..." : "Save" })
|
|
4554
|
+
]
|
|
4555
|
+
}
|
|
4556
|
+
),
|
|
4346
4557
|
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
4347
4558
|
Button,
|
|
4348
4559
|
{
|
|
4349
4560
|
size: "sm",
|
|
4350
|
-
className: "flex items-center bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
4561
|
+
className: "hidden md:inline-flex items-center bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
4351
4562
|
onClick: () => handleSave(published),
|
|
4352
4563
|
disabled: isSaving,
|
|
4353
4564
|
children: [
|
|
@@ -4360,8 +4571,22 @@ function BlogEditor({ existingBlog }) {
|
|
|
4360
4571
|
Button,
|
|
4361
4572
|
{
|
|
4362
4573
|
variant: "outline",
|
|
4363
|
-
size: "
|
|
4364
|
-
className: "
|
|
4574
|
+
size: "icon",
|
|
4575
|
+
className: "md:hidden h-8 w-8 bg-white text-gray-800 hover:bg-gray-100 border-0",
|
|
4576
|
+
onClick: () => router.back(),
|
|
4577
|
+
title: "Close",
|
|
4578
|
+
children: [
|
|
4579
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react19.X, { className: "h-4 w-4" }),
|
|
4580
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "sr-only", children: "Close" })
|
|
4581
|
+
]
|
|
4582
|
+
}
|
|
4583
|
+
),
|
|
4584
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
4585
|
+
Button,
|
|
4586
|
+
{
|
|
4587
|
+
variant: "outline",
|
|
4588
|
+
size: "sm",
|
|
4589
|
+
className: "hidden md:inline-flex items-center bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
4365
4590
|
onClick: () => router.back(),
|
|
4366
4591
|
children: [
|
|
4367
4592
|
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react19.X, { className: "h-4 w-4 mr-1" }),
|
|
@@ -4543,11 +4768,11 @@ function BlogEditor({ existingBlog }) {
|
|
|
4543
4768
|
}
|
|
4544
4769
|
|
|
4545
4770
|
// src/components/Admin/AnalyticsChart.tsx
|
|
4546
|
-
var
|
|
4771
|
+
var import_react22 = require("react");
|
|
4547
4772
|
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
4548
4773
|
function AnalyticsChart({ data, title, type }) {
|
|
4549
|
-
const canvasRef = (0,
|
|
4550
|
-
(0,
|
|
4774
|
+
const canvasRef = (0, import_react22.useRef)(null);
|
|
4775
|
+
(0, import_react22.useEffect)(() => {
|
|
4551
4776
|
if (!canvasRef.current || !data.length) return;
|
|
4552
4777
|
const ctx = canvasRef.current.getContext("2d");
|
|
4553
4778
|
if (!ctx) return;
|
|
@@ -4739,13 +4964,13 @@ function AnalyticsCard({ title, value, subtitle, icon, trend }) {
|
|
|
4739
4964
|
}
|
|
4740
4965
|
|
|
4741
4966
|
// src/components/Admin/AnalyticsExclusion.tsx
|
|
4742
|
-
var
|
|
4967
|
+
var import_navigation7 = require("next/navigation");
|
|
4743
4968
|
|
|
4744
4969
|
// src/hooks/use-analytics.ts
|
|
4745
|
-
var
|
|
4970
|
+
var import_react23 = require("react");
|
|
4746
4971
|
function useAnalytics(pathname) {
|
|
4747
4972
|
const isAdminRoute = pathname != null && pathname.startsWith("/admin");
|
|
4748
|
-
(0,
|
|
4973
|
+
(0, import_react23.useEffect)(() => {
|
|
4749
4974
|
if (isAdminRoute && typeof window !== "undefined") {
|
|
4750
4975
|
if (typeof window.gtag === "function") {
|
|
4751
4976
|
window.gtag = () => {
|
|
@@ -4762,7 +4987,7 @@ function useAnalytics(pathname) {
|
|
|
4762
4987
|
// src/components/Admin/AnalyticsExclusion.tsx
|
|
4763
4988
|
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
4764
4989
|
function AnalyticsExclusion() {
|
|
4765
|
-
const pathname = (0,
|
|
4990
|
+
const pathname = (0, import_navigation7.usePathname)();
|
|
4766
4991
|
const { isAdminRoute, shouldTrack } = useAnalytics(pathname);
|
|
4767
4992
|
if (!isAdminRoute) return null;
|
|
4768
4993
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "fixed bottom-4 right-4 bg-green-100 border border-green-400 text-green-700 px-4 py-2 rounded text-sm z-50", children: [
|
|
@@ -4772,15 +4997,15 @@ function AnalyticsExclusion() {
|
|
|
4772
4997
|
}
|
|
4773
4998
|
|
|
4774
4999
|
// src/components/Admin/ComponentSettings.tsx
|
|
4775
|
-
var
|
|
5000
|
+
var import_react24 = require("react");
|
|
4776
5001
|
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
4777
5002
|
function EntitySelect({
|
|
4778
5003
|
prop,
|
|
4779
5004
|
value,
|
|
4780
5005
|
onChange
|
|
4781
5006
|
}) {
|
|
4782
|
-
const [options, setOptions] = (0,
|
|
4783
|
-
(0,
|
|
5007
|
+
const [options, setOptions] = (0, import_react24.useState)([]);
|
|
5008
|
+
(0, import_react24.useEffect)(() => {
|
|
4784
5009
|
if (!prop.entity) return;
|
|
4785
5010
|
fetch(`/api/${prop.entity}`).then((r) => r.ok ? r.json() : { data: [] }).then((res) => setOptions(res.data || res || [])).catch(() => {
|
|
4786
5011
|
});
|
|
@@ -4918,7 +5143,7 @@ function ComponentSettings({
|
|
|
4918
5143
|
}
|
|
4919
5144
|
|
|
4920
5145
|
// src/components/Admin/NavbarEditor.tsx
|
|
4921
|
-
var
|
|
5146
|
+
var import_react25 = require("react");
|
|
4922
5147
|
var import_lucide_react21 = require("lucide-react");
|
|
4923
5148
|
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
4924
5149
|
function generateId() {
|
|
@@ -4938,9 +5163,9 @@ function NavItemEditor({
|
|
|
4938
5163
|
canMoveUp,
|
|
4939
5164
|
canMoveDown
|
|
4940
5165
|
}) {
|
|
4941
|
-
const [expanded, setExpanded] = (0,
|
|
4942
|
-
const [pages, setPages] = (0,
|
|
4943
|
-
(0,
|
|
5166
|
+
const [expanded, setExpanded] = (0, import_react25.useState)(false);
|
|
5167
|
+
const [pages, setPages] = (0, import_react25.useState)([]);
|
|
5168
|
+
(0, import_react25.useEffect)(() => {
|
|
4944
5169
|
if (!expanded) return;
|
|
4945
5170
|
fetch("/api/pages").then((r) => r.ok ? r.json() : []).then((data) => {
|
|
4946
5171
|
const res = data;
|
|
@@ -5208,9 +5433,9 @@ function NavbarEditor({ config, onChange }) {
|
|
|
5208
5433
|
|
|
5209
5434
|
// src/admin/pages/SignInPage.tsx
|
|
5210
5435
|
var import_link5 = __toESM(require("next/link"), 1);
|
|
5211
|
-
var
|
|
5212
|
-
var
|
|
5213
|
-
var
|
|
5436
|
+
var import_react26 = require("react");
|
|
5437
|
+
var import_react27 = require("next-auth/react");
|
|
5438
|
+
var import_navigation8 = require("next/navigation");
|
|
5214
5439
|
|
|
5215
5440
|
// src/components/Admin/AuthPageLayout.tsx
|
|
5216
5441
|
var import_image5 = __toESM(require("next/image"), 1);
|
|
@@ -5268,13 +5493,13 @@ function AuthPageLayout({ children }) {
|
|
|
5268
5493
|
// src/admin/pages/SignInPage.tsx
|
|
5269
5494
|
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
5270
5495
|
var SigninPage = () => {
|
|
5271
|
-
const [email, setEmail] = (0,
|
|
5272
|
-
const [password, setPassword] = (0,
|
|
5273
|
-
const [error, setError] = (0,
|
|
5274
|
-
const [loading, setLoading] = (0,
|
|
5275
|
-
const router = (0,
|
|
5276
|
-
const { status } = (0,
|
|
5277
|
-
(0,
|
|
5496
|
+
const [email, setEmail] = (0, import_react26.useState)("");
|
|
5497
|
+
const [password, setPassword] = (0, import_react26.useState)("");
|
|
5498
|
+
const [error, setError] = (0, import_react26.useState)("");
|
|
5499
|
+
const [loading, setLoading] = (0, import_react26.useState)(false);
|
|
5500
|
+
const router = (0, import_navigation8.useRouter)();
|
|
5501
|
+
const { status } = (0, import_react27.useSession)();
|
|
5502
|
+
(0, import_react26.useEffect)(() => {
|
|
5278
5503
|
if (status === "authenticated") {
|
|
5279
5504
|
router.replace("/admin/dashboard");
|
|
5280
5505
|
}
|
|
@@ -5284,7 +5509,7 @@ var SigninPage = () => {
|
|
|
5284
5509
|
setLoading(true);
|
|
5285
5510
|
setError("");
|
|
5286
5511
|
try {
|
|
5287
|
-
const result = await (0,
|
|
5512
|
+
const result = await (0, import_react27.signIn)("credentials", {
|
|
5288
5513
|
email,
|
|
5289
5514
|
password,
|
|
5290
5515
|
redirect: false
|
|
@@ -5357,13 +5582,13 @@ var SigninPage = () => {
|
|
|
5357
5582
|
var SignInPage_default = SigninPage;
|
|
5358
5583
|
|
|
5359
5584
|
// src/admin/pages/ForgotPasswordPage.tsx
|
|
5360
|
-
var
|
|
5585
|
+
var import_react28 = require("react");
|
|
5361
5586
|
var import_link6 = __toESM(require("next/link"), 1);
|
|
5362
5587
|
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
5363
5588
|
function ForgotPasswordPage() {
|
|
5364
|
-
const [email, setEmail] = (0,
|
|
5365
|
-
const [loading, setLoading] = (0,
|
|
5366
|
-
const [message, setMessage] = (0,
|
|
5589
|
+
const [email, setEmail] = (0, import_react28.useState)("");
|
|
5590
|
+
const [loading, setLoading] = (0, import_react28.useState)(false);
|
|
5591
|
+
const [message, setMessage] = (0, import_react28.useState)("");
|
|
5367
5592
|
const handleSubmit = async (e) => {
|
|
5368
5593
|
e.preventDefault();
|
|
5369
5594
|
if (!email.trim()) return;
|
|
@@ -5405,17 +5630,17 @@ function ForgotPasswordPage() {
|
|
|
5405
5630
|
}
|
|
5406
5631
|
|
|
5407
5632
|
// src/admin/pages/ResetPasswordPage.tsx
|
|
5408
|
-
var
|
|
5409
|
-
var
|
|
5633
|
+
var import_react29 = require("react");
|
|
5634
|
+
var import_navigation9 = require("next/navigation");
|
|
5410
5635
|
var import_link7 = __toESM(require("next/link"), 1);
|
|
5411
5636
|
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
5412
5637
|
function ResetPasswordPageContent() {
|
|
5413
|
-
const searchParams = (0,
|
|
5638
|
+
const searchParams = (0, import_navigation9.useSearchParams)();
|
|
5414
5639
|
const token = searchParams.get("token");
|
|
5415
|
-
const [formData, setFormData] = (0,
|
|
5416
|
-
const [loading, setLoading] = (0,
|
|
5417
|
-
const [message, setMessage] = (0,
|
|
5418
|
-
(0,
|
|
5640
|
+
const [formData, setFormData] = (0, import_react29.useState)({ password: "", confirmPassword: "" });
|
|
5641
|
+
const [loading, setLoading] = (0, import_react29.useState)(false);
|
|
5642
|
+
const [message, setMessage] = (0, import_react29.useState)("");
|
|
5643
|
+
(0, import_react29.useEffect)(() => {
|
|
5419
5644
|
if (!token) setMessage("Invalid reset link. Token is missing.");
|
|
5420
5645
|
}, [token]);
|
|
5421
5646
|
const handleSubmit = async (e) => {
|
|
@@ -5477,26 +5702,26 @@ function ResetPasswordPageContent() {
|
|
|
5477
5702
|
] }) });
|
|
5478
5703
|
}
|
|
5479
5704
|
function ResetPasswordPage() {
|
|
5480
|
-
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
5705
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react29.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "min-h-screen flex items-center justify-center bg-gray-50 dark:bg-dark", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "max-w-[400px] w-full p-6 text-center", children: [
|
|
5481
5706
|
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "animate-spin rounded-full h-6 w-6 border-2 border-gray-300 border-t-gray-600 mx-auto" }),
|
|
5482
5707
|
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "mt-3 text-sm text-body-color", children: "Loading..." })
|
|
5483
5708
|
] }) }), children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ResetPasswordPageContent, {}) });
|
|
5484
5709
|
}
|
|
5485
5710
|
|
|
5486
5711
|
// src/admin/pages/InvitePage.tsx
|
|
5487
|
-
var
|
|
5488
|
-
var
|
|
5712
|
+
var import_react30 = require("react");
|
|
5713
|
+
var import_navigation10 = require("next/navigation");
|
|
5489
5714
|
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
5490
5715
|
function InvitePageContent() {
|
|
5491
|
-
const searchParams = (0,
|
|
5716
|
+
const searchParams = (0, import_navigation10.useSearchParams)();
|
|
5492
5717
|
const token = searchParams.get("token");
|
|
5493
|
-
const [formData, setFormData] = (0,
|
|
5718
|
+
const [formData, setFormData] = (0, import_react30.useState)({
|
|
5494
5719
|
password: "",
|
|
5495
5720
|
confirmPassword: ""
|
|
5496
5721
|
});
|
|
5497
|
-
const [loading, setLoading] = (0,
|
|
5498
|
-
const [message, setMessage] = (0,
|
|
5499
|
-
(0,
|
|
5722
|
+
const [loading, setLoading] = (0, import_react30.useState)(false);
|
|
5723
|
+
const [message, setMessage] = (0, import_react30.useState)("");
|
|
5724
|
+
(0, import_react30.useEffect)(() => {
|
|
5500
5725
|
if (!token) {
|
|
5501
5726
|
setMessage("Invalid invite link. Token is missing.");
|
|
5502
5727
|
}
|
|
@@ -5561,29 +5786,70 @@ function InvitePageContent() {
|
|
|
5561
5786
|
] }) });
|
|
5562
5787
|
}
|
|
5563
5788
|
function InvitePage() {
|
|
5564
|
-
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
5789
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react30.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "min-h-screen flex items-center justify-center bg-gray-50", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "max-w-[400px] w-full p-6 text-center", children: [
|
|
5565
5790
|
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "animate-spin rounded-full h-6 w-6 border-2 border-gray-300 border-t-gray-600 mx-auto" }),
|
|
5566
5791
|
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "mt-3 text-sm text-gray-600", children: "Loading..." })
|
|
5567
5792
|
] }) }), children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(InvitePageContent, {}) });
|
|
5568
5793
|
}
|
|
5569
5794
|
|
|
5570
5795
|
// src/admin/pages/DashboardPage.tsx
|
|
5571
|
-
var
|
|
5572
|
-
var
|
|
5796
|
+
var import_react31 = require("next-auth/react");
|
|
5797
|
+
var import_react32 = require("react");
|
|
5798
|
+
var import_navigation11 = require("next/navigation");
|
|
5799
|
+
var import_chart2 = require("chart.js");
|
|
5800
|
+
var import_react_chartjs_22 = require("react-chartjs-2");
|
|
5573
5801
|
var import_lucide_react22 = require("lucide-react");
|
|
5574
5802
|
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
5803
|
+
import_chart2.Chart.register(import_chart2.ArcElement, import_chart2.Tooltip, import_chart2.Legend);
|
|
5575
5804
|
function DashboardPage() {
|
|
5576
|
-
const { data: session } = (0,
|
|
5577
|
-
const
|
|
5578
|
-
const
|
|
5579
|
-
const [
|
|
5580
|
-
const [
|
|
5581
|
-
(0,
|
|
5805
|
+
const { data: session } = (0, import_react31.useSession)();
|
|
5806
|
+
const router = (0, import_navigation11.useRouter)();
|
|
5807
|
+
const { storeEnabled } = (0, import_react32.useContext)(AdminConfigContext);
|
|
5808
|
+
const [analyticsData, setAnalyticsData] = (0, import_react32.useState)(null);
|
|
5809
|
+
const [ecommerceData, setEcommerceData] = (0, import_react32.useState)(null);
|
|
5810
|
+
const [dashboardStats, setDashboardStats] = (0, import_react32.useState)(null);
|
|
5811
|
+
const [loading, setLoading] = (0, import_react32.useState)({ analytics: true, stats: true, ecommerce: true });
|
|
5812
|
+
const [analyticsEnabled, setAnalyticsEnabled] = (0, import_react32.useState)(false);
|
|
5813
|
+
const [days, setDays] = (0, import_react32.useState)(30);
|
|
5814
|
+
const [activeTab, setActiveTab] = (0, import_react32.useState)("overview");
|
|
5815
|
+
const formatMoney4 = (0, import_react32.useMemo)(
|
|
5816
|
+
() => (value) => new Intl.NumberFormat(void 0, {
|
|
5817
|
+
style: "currency",
|
|
5818
|
+
currency: "INR",
|
|
5819
|
+
maximumFractionDigits: 0
|
|
5820
|
+
}).format(value || 0),
|
|
5821
|
+
[]
|
|
5822
|
+
);
|
|
5823
|
+
const safePct = (v, max) => max > 0 ? Math.max(6, Math.round(v / max * 100)) : 0;
|
|
5824
|
+
const productPalette = ["#2563eb", "#0ea5e9", "#14b8a6", "#22c55e", "#eab308", "#f97316"];
|
|
5825
|
+
const salesBreakdownPalette = {
|
|
5826
|
+
sales: "#22c55e",
|
|
5827
|
+
returns: "#ef4444",
|
|
5828
|
+
replacements: "#f59e0b"
|
|
5829
|
+
};
|
|
5830
|
+
const contactTypeColor = (type) => {
|
|
5831
|
+
const t = type.trim().toLowerCase();
|
|
5832
|
+
if (t === "customer") return "#2563eb";
|
|
5833
|
+
if (t === "lead") return "#a855f7";
|
|
5834
|
+
if (t === "vendor") return "#14b8a6";
|
|
5835
|
+
return "#6b7280";
|
|
5836
|
+
};
|
|
5837
|
+
const metricBarColor = (label) => {
|
|
5838
|
+
const key = label.toLowerCase();
|
|
5839
|
+
if (key.includes("contact")) return "bg-blue-500";
|
|
5840
|
+
if (key.includes("submission")) return "bg-purple-500";
|
|
5841
|
+
if (key.includes("net sales")) return "bg-green-500";
|
|
5842
|
+
if (key.includes("gross sales")) return "bg-emerald-500";
|
|
5843
|
+
if (key.includes("return")) return "bg-red-500";
|
|
5844
|
+
return "bg-gray-600";
|
|
5845
|
+
};
|
|
5846
|
+
(0, import_react32.useEffect)(() => {
|
|
5582
5847
|
const fetchData = async () => {
|
|
5583
|
-
const analyticsPromise = fetch(
|
|
5848
|
+
const analyticsPromise = fetch(`/api/analytics?days=${days}`);
|
|
5584
5849
|
const statsPromise = fetch("/api/dashboard/stats");
|
|
5850
|
+
const ecommercePromise = storeEnabled ? fetch(`/api/dashboard/ecommerce?days=${days}`) : Promise.resolve(null);
|
|
5585
5851
|
try {
|
|
5586
|
-
const [analyticsResponse, statsResponse] = await Promise.all([analyticsPromise, statsPromise]);
|
|
5852
|
+
const [analyticsResponse, statsResponse, ecommerceResponse] = await Promise.all([analyticsPromise, statsPromise, ecommercePromise]);
|
|
5587
5853
|
if (analyticsResponse.ok) {
|
|
5588
5854
|
const data = await analyticsResponse.json();
|
|
5589
5855
|
setAnalyticsData(data);
|
|
@@ -5601,15 +5867,22 @@ function DashboardPage() {
|
|
|
5601
5867
|
const stats = await statsResponse.json();
|
|
5602
5868
|
setDashboardStats(stats);
|
|
5603
5869
|
}
|
|
5870
|
+
if (ecommerceResponse && ecommerceResponse.ok) {
|
|
5871
|
+
const ecommerce = await ecommerceResponse.json();
|
|
5872
|
+
setEcommerceData(ecommerce);
|
|
5873
|
+
} else {
|
|
5874
|
+
setEcommerceData(null);
|
|
5875
|
+
}
|
|
5604
5876
|
} catch (error) {
|
|
5605
5877
|
setAnalyticsEnabled(false);
|
|
5878
|
+
setEcommerceData(null);
|
|
5606
5879
|
} finally {
|
|
5607
|
-
setLoading({ analytics: false, stats: false });
|
|
5880
|
+
setLoading({ analytics: false, stats: false, ecommerce: false });
|
|
5608
5881
|
}
|
|
5609
5882
|
};
|
|
5610
5883
|
fetchData();
|
|
5611
|
-
}, []);
|
|
5612
|
-
const isLoading = loading.analytics || loading.stats;
|
|
5884
|
+
}, [days, storeEnabled]);
|
|
5885
|
+
const isLoading = loading.analytics || loading.stats || storeEnabled && loading.ecommerce;
|
|
5613
5886
|
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "min-w-0 rounded-lg bg-white shadow-md", children: [
|
|
5614
5887
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "bg-gray-800 border-b border-gray-700 px-4 py-2.5 rounded-t-lg", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
5615
5888
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
|
|
@@ -5625,13 +5898,54 @@ function DashboardPage() {
|
|
|
5625
5898
|
] }) })
|
|
5626
5899
|
] }) }),
|
|
5627
5900
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "min-w-0 p-4 bg-gray-50", children: [
|
|
5628
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "mb-
|
|
5629
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center
|
|
5901
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-3", children: [
|
|
5902
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
5903
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
5904
|
+
"button",
|
|
5905
|
+
{
|
|
5906
|
+
onClick: () => setActiveTab("overview"),
|
|
5907
|
+
className: `text-xs px-2 py-1 rounded border ${activeTab === "overview" ? "bg-gray-800 text-white border-gray-800" : "bg-white text-gray-700 border-gray-300"}`,
|
|
5908
|
+
children: "Overview"
|
|
5909
|
+
}
|
|
5910
|
+
),
|
|
5911
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
5912
|
+
"button",
|
|
5913
|
+
{
|
|
5914
|
+
onClick: () => setActiveTab("crm"),
|
|
5915
|
+
className: `text-xs px-2 py-1 rounded border ${activeTab === "crm" ? "bg-gray-800 text-white border-gray-800" : "bg-white text-gray-700 border-gray-300"}`,
|
|
5916
|
+
children: "CRM / Engagement"
|
|
5917
|
+
}
|
|
5918
|
+
),
|
|
5919
|
+
storeEnabled && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
5920
|
+
"button",
|
|
5921
|
+
{
|
|
5922
|
+
onClick: () => setActiveTab("sales"),
|
|
5923
|
+
className: `text-xs px-2 py-1 rounded border ${activeTab === "sales" ? "bg-gray-800 text-white border-gray-800" : "bg-white text-gray-700 border-gray-300"}`,
|
|
5924
|
+
children: "Sales"
|
|
5925
|
+
}
|
|
5926
|
+
)
|
|
5927
|
+
] }),
|
|
5928
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
5929
|
+
"select",
|
|
5930
|
+
{
|
|
5931
|
+
value: days,
|
|
5932
|
+
onChange: (e) => setDays(Number(e.target.value)),
|
|
5933
|
+
className: "border border-gray-300 text-xs rounded px-2 py-1 bg-white",
|
|
5934
|
+
children: [
|
|
5935
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("option", { value: 7, children: "Last 7 days" }),
|
|
5936
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("option", { value: 30, children: "Last 30 days" }),
|
|
5937
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("option", { value: 90, children: "Last 90 days" })
|
|
5938
|
+
]
|
|
5939
|
+
}
|
|
5940
|
+
)
|
|
5941
|
+
] }),
|
|
5942
|
+
activeTab === "overview" && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "mb-5", children: [
|
|
5943
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center mb-3", children: [
|
|
5630
5944
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react22.BarChart3, { className: "w-4 h-4 text-gray-600 mr-2" }),
|
|
5631
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h2", { className: "text-sm font-semibold text-gray-800", children: "
|
|
5945
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h2", { className: "text-sm font-semibold text-gray-800", children: "Contacts & Forms" })
|
|
5632
5946
|
] }),
|
|
5633
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-
|
|
5634
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded border border-gray-200 p-
|
|
5947
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4", children: [
|
|
5948
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded border border-gray-200 p-3 shadow-sm", children: [
|
|
5635
5949
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between mb-1.5", children: [
|
|
5636
5950
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs font-medium text-gray-600", children: "Total Contacts" }),
|
|
5637
5951
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "p-1 bg-gray-200 rounded", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react22.Users, { className: "w-3.5 h-3.5 text-gray-600" }) })
|
|
@@ -5647,7 +5961,7 @@ function DashboardPage() {
|
|
|
5647
5961
|
] })
|
|
5648
5962
|
] })
|
|
5649
5963
|
] }),
|
|
5650
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded border border-gray-200 p-
|
|
5964
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded border border-gray-200 p-3 shadow-sm", children: [
|
|
5651
5965
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between mb-1.5", children: [
|
|
5652
5966
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs font-medium text-gray-600", children: "Form Submissions" }),
|
|
5653
5967
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "p-1 bg-gray-200 rounded", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react22.FileText, { className: "w-3.5 h-3.5 text-gray-600" }) })
|
|
@@ -5663,7 +5977,7 @@ function DashboardPage() {
|
|
|
5663
5977
|
] })
|
|
5664
5978
|
] })
|
|
5665
5979
|
] }),
|
|
5666
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded border border-gray-200 p-
|
|
5980
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded border border-gray-200 p-3 shadow-sm", children: [
|
|
5667
5981
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between mb-1.5", children: [
|
|
5668
5982
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs font-medium text-gray-600", children: "Active Forms" }),
|
|
5669
5983
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "p-1 bg-gray-200 rounded", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react22.ClipboardList, { className: "w-3.5 h-3.5 text-gray-600" }) })
|
|
@@ -5673,7 +5987,7 @@ function DashboardPage() {
|
|
|
5673
5987
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-500", children: "Total" })
|
|
5674
5988
|
] })
|
|
5675
5989
|
] }),
|
|
5676
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded border border-gray-200 p-
|
|
5990
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded border border-gray-200 p-3 shadow-sm", children: [
|
|
5677
5991
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between mb-1.5", children: [
|
|
5678
5992
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs font-medium text-gray-600", children: "Registered Users" }),
|
|
5679
5993
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "p-1 bg-gray-200 rounded", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react22.UserCheck, { className: "w-3.5 h-3.5 text-gray-600" }) })
|
|
@@ -5685,8 +5999,151 @@ function DashboardPage() {
|
|
|
5685
5999
|
] })
|
|
5686
6000
|
] })
|
|
5687
6001
|
] }),
|
|
5688
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.
|
|
5689
|
-
|
|
6002
|
+
activeTab === "overview" && !isLoading && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "mb-6 bg-white rounded-lg border border-gray-200 p-4 shadow-sm", children: [
|
|
6003
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center mb-3", children: [
|
|
6004
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react22.Activity, { className: "w-4 h-4 text-gray-600 mr-2" }),
|
|
6005
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-sm font-semibold text-gray-800", children: "Overview Snapshot" })
|
|
6006
|
+
] }),
|
|
6007
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6", children: [
|
|
6008
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
|
|
6009
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h4", { className: "text-xs font-semibold text-gray-700 mb-2", children: "Contacts & Forms" }),
|
|
6010
|
+
(() => {
|
|
6011
|
+
const rows = [
|
|
6012
|
+
{ label: "Contacts (total)", value: dashboardStats?.contacts.total || 0 },
|
|
6013
|
+
{ label: "New contacts", value: dashboardStats?.contacts.recent || 0 },
|
|
6014
|
+
{ label: "Submissions", value: dashboardStats?.forms.submissions || 0 },
|
|
6015
|
+
{ label: "New submissions", value: dashboardStats?.forms.recentSubmissions || 0 }
|
|
6016
|
+
];
|
|
6017
|
+
const max = Math.max(...rows.map((r) => r.value), 0);
|
|
6018
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "space-y-2", children: rows.map((r) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
|
|
6019
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between text-xs mb-1", children: [
|
|
6020
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-gray-600", children: r.label }),
|
|
6021
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "font-semibold text-gray-900", children: r.value })
|
|
6022
|
+
] }),
|
|
6023
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "h-2 rounded bg-gray-100 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: `h-2 rounded ${metricBarColor(r.label)}`, style: { width: `${safePct(r.value, max)}%` } }) })
|
|
6024
|
+
] }, r.label)) });
|
|
6025
|
+
})()
|
|
6026
|
+
] }),
|
|
6027
|
+
storeEnabled && ecommerceData && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
|
|
6028
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h4", { className: "text-xs font-semibold text-gray-700 mb-2", children: "Sales, Returns & Products" }),
|
|
6029
|
+
(() => {
|
|
6030
|
+
const salesBase = Math.max(ecommerceData.kpis.grossSales, 1);
|
|
6031
|
+
const rows = [
|
|
6032
|
+
{ label: "Net sales", value: ecommerceData.kpis.netSales, display: formatMoney4(ecommerceData.kpis.netSales) },
|
|
6033
|
+
{ label: "Gross sales", value: ecommerceData.kpis.grossSales, display: formatMoney4(ecommerceData.kpis.grossSales) },
|
|
6034
|
+
{ label: "Return value", value: ecommerceData.kpis.returnValue, display: formatMoney4(ecommerceData.kpis.returnValue) },
|
|
6035
|
+
{ label: "Return rate", value: ecommerceData.kpis.returnRate, display: `${ecommerceData.kpis.returnRate.toFixed(1)}%` }
|
|
6036
|
+
];
|
|
6037
|
+
const topProduct = ecommerceData.topProducts[0];
|
|
6038
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "space-y-2", children: [
|
|
6039
|
+
rows.map((r) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
|
|
6040
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between text-xs mb-1", children: [
|
|
6041
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-gray-600", children: r.label }),
|
|
6042
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "font-semibold text-gray-900", children: r.display })
|
|
6043
|
+
] }),
|
|
6044
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "h-2 rounded bg-gray-100 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
6045
|
+
"div",
|
|
6046
|
+
{
|
|
6047
|
+
className: `h-2 rounded ${metricBarColor(r.label)}`,
|
|
6048
|
+
style: { width: `${r.label === "Return rate" ? Math.max(6, Math.round(r.value)) : safePct(r.value, salesBase)}%` }
|
|
6049
|
+
}
|
|
6050
|
+
) })
|
|
6051
|
+
] }, r.label)),
|
|
6052
|
+
topProduct && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "pt-1 text-xs text-gray-600", children: [
|
|
6053
|
+
"Top product: ",
|
|
6054
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "font-medium text-gray-900", children: topProduct.name }),
|
|
6055
|
+
" (",
|
|
6056
|
+
topProduct.units,
|
|
6057
|
+
" units)"
|
|
6058
|
+
] })
|
|
6059
|
+
] });
|
|
6060
|
+
})()
|
|
6061
|
+
] })
|
|
6062
|
+
] })
|
|
6063
|
+
] }),
|
|
6064
|
+
activeTab === "overview" && !isLoading && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-6 mb-7", children: [
|
|
6065
|
+
storeEnabled && ecommerceData && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-200 p-4 shadow-sm", children: [
|
|
6066
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-sm font-semibold text-gray-800 mb-3", children: "Sales vs Returns vs Replacements" }),
|
|
6067
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "h-56", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
6068
|
+
import_react_chartjs_22.Doughnut,
|
|
6069
|
+
{
|
|
6070
|
+
data: {
|
|
6071
|
+
labels: ["Sales", "Returns", "Replacements"],
|
|
6072
|
+
datasets: [
|
|
6073
|
+
{
|
|
6074
|
+
data: [
|
|
6075
|
+
ecommerceData.salesBreakdown?.sales.value ?? ecommerceData.kpis.grossSales,
|
|
6076
|
+
ecommerceData.salesBreakdown?.returns.value ?? ecommerceData.kpis.returnValue,
|
|
6077
|
+
ecommerceData.salesBreakdown?.replacements.value ?? 0
|
|
6078
|
+
],
|
|
6079
|
+
backgroundColor: [
|
|
6080
|
+
salesBreakdownPalette.sales,
|
|
6081
|
+
salesBreakdownPalette.returns,
|
|
6082
|
+
salesBreakdownPalette.replacements
|
|
6083
|
+
],
|
|
6084
|
+
borderWidth: 0
|
|
6085
|
+
}
|
|
6086
|
+
]
|
|
6087
|
+
},
|
|
6088
|
+
options: {
|
|
6089
|
+
maintainAspectRatio: false,
|
|
6090
|
+
plugins: {
|
|
6091
|
+
legend: { position: "bottom" }
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
6094
|
+
}
|
|
6095
|
+
) })
|
|
6096
|
+
] }),
|
|
6097
|
+
storeEnabled && ecommerceData && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-200 p-4 shadow-sm", children: [
|
|
6098
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-sm font-semibold text-gray-800 mb-3", children: "Product-wise Sales (Top)" }),
|
|
6099
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "h-56", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
6100
|
+
import_react_chartjs_22.Doughnut,
|
|
6101
|
+
{
|
|
6102
|
+
data: {
|
|
6103
|
+
labels: ecommerceData.topProducts.map((p) => p.name),
|
|
6104
|
+
datasets: [
|
|
6105
|
+
{
|
|
6106
|
+
data: ecommerceData.topProducts.map((p) => p.sales),
|
|
6107
|
+
backgroundColor: productPalette.slice(0, Math.max(3, ecommerceData.topProducts.length)),
|
|
6108
|
+
borderWidth: 0
|
|
6109
|
+
}
|
|
6110
|
+
]
|
|
6111
|
+
},
|
|
6112
|
+
options: {
|
|
6113
|
+
maintainAspectRatio: false,
|
|
6114
|
+
plugins: {
|
|
6115
|
+
legend: { position: "bottom" }
|
|
6116
|
+
}
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
6119
|
+
) })
|
|
6120
|
+
] }),
|
|
6121
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-200 p-4 shadow-sm", children: [
|
|
6122
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-sm font-semibold text-gray-800 mb-3", children: "Contact Distribution" }),
|
|
6123
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "h-56", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
6124
|
+
import_react_chartjs_22.Doughnut,
|
|
6125
|
+
{
|
|
6126
|
+
data: {
|
|
6127
|
+
labels: (dashboardStats?.contactTypes?.length ? dashboardStats.contactTypes : [{ type: "unknown", count: dashboardStats?.contacts.total || 0 }]).map((c) => c.type),
|
|
6128
|
+
datasets: [
|
|
6129
|
+
{
|
|
6130
|
+
data: (dashboardStats?.contactTypes?.length ? dashboardStats.contactTypes : [{ type: "unknown", count: dashboardStats?.contacts.total || 0 }]).map((c) => c.count),
|
|
6131
|
+
backgroundColor: (dashboardStats?.contactTypes?.length ? dashboardStats.contactTypes : [{ type: "unknown", count: dashboardStats?.contacts.total || 0 }]).map((c) => contactTypeColor(c.type)),
|
|
6132
|
+
borderWidth: 0
|
|
6133
|
+
}
|
|
6134
|
+
]
|
|
6135
|
+
},
|
|
6136
|
+
options: {
|
|
6137
|
+
maintainAspectRatio: false,
|
|
6138
|
+
plugins: {
|
|
6139
|
+
legend: { position: "bottom" }
|
|
6140
|
+
}
|
|
6141
|
+
}
|
|
6142
|
+
}
|
|
6143
|
+
) })
|
|
6144
|
+
] })
|
|
6145
|
+
] }),
|
|
6146
|
+
activeTab === "overview" && analyticsEnabled && !isLoading && analyticsData && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
5690
6147
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "mb-4", children: [
|
|
5691
6148
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center mb-2", children: [
|
|
5692
6149
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react22.Globe, { className: "w-4 h-4 text-gray-600 mr-2" }),
|
|
@@ -5772,31 +6229,279 @@ function DashboardPage() {
|
|
|
5772
6229
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "border-t-2 border-dotted border-gray-200 my-6" }),
|
|
5773
6230
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "mb-6", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(GeographicMap, { data: analyticsData.geographicData }) })
|
|
5774
6231
|
] }),
|
|
6232
|
+
activeTab === "crm" && !isLoading && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
6233
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "mb-4", children: [
|
|
6234
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center mb-2", children: [
|
|
6235
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react22.Users, { className: "w-4 h-4 text-gray-600 mr-2" }),
|
|
6236
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h2", { className: "text-sm font-semibold text-gray-800", children: "CRM Activity" })
|
|
6237
|
+
] }),
|
|
6238
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3", children: [
|
|
6239
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("button", { onClick: () => router.push("/admin/contacts"), className: "text-left bg-white rounded border border-gray-200 p-2.5 shadow-sm", children: [
|
|
6240
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-600", children: "Contacts" }),
|
|
6241
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-lg font-bold text-gray-900", children: dashboardStats?.contacts.total || 0 }),
|
|
6242
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-green-600", children: [
|
|
6243
|
+
"+",
|
|
6244
|
+
dashboardStats?.contacts.recent || 0,
|
|
6245
|
+
" recent"
|
|
6246
|
+
] })
|
|
6247
|
+
] }),
|
|
6248
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("button", { onClick: () => router.push("/admin/forms"), className: "text-left bg-white rounded border border-gray-200 p-2.5 shadow-sm", children: [
|
|
6249
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-600", children: "Active Forms" }),
|
|
6250
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-lg font-bold text-gray-900", children: dashboardStats?.forms.total || 0 })
|
|
6251
|
+
] }),
|
|
6252
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("button", { onClick: () => router.push("/admin/submissions"), className: "text-left bg-white rounded border border-gray-200 p-2.5 shadow-sm", children: [
|
|
6253
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-600", children: "Form Submissions" }),
|
|
6254
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-lg font-bold text-gray-900", children: dashboardStats?.forms.submissions || 0 }),
|
|
6255
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-green-600", children: [
|
|
6256
|
+
"+",
|
|
6257
|
+
dashboardStats?.forms.recentSubmissions || 0,
|
|
6258
|
+
" recent"
|
|
6259
|
+
] })
|
|
6260
|
+
] }),
|
|
6261
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("button", { onClick: () => router.push("/admin/blogs"), className: "text-left bg-white rounded border border-gray-200 p-2.5 shadow-sm", children: [
|
|
6262
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-600", children: "Blogs" }),
|
|
6263
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-lg font-bold text-gray-900", children: dashboardStats?.blogs || 0 })
|
|
6264
|
+
] })
|
|
6265
|
+
] })
|
|
6266
|
+
] }),
|
|
6267
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6", children: [
|
|
6268
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-100 p-4 shadow-sm", children: [
|
|
6269
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-base font-semibold text-gray-800 mb-3", children: "Engagement Funnel" }),
|
|
6270
|
+
(() => {
|
|
6271
|
+
const contacts = dashboardStats?.contacts.total || 0;
|
|
6272
|
+
const submissions = dashboardStats?.forms.submissions || 0;
|
|
6273
|
+
const visitors = analyticsData?.visitors || 0;
|
|
6274
|
+
const base = Math.max(visitors, contacts, submissions, 1);
|
|
6275
|
+
const rows = [
|
|
6276
|
+
{ label: "Visitors", value: visitors },
|
|
6277
|
+
{ label: "Contacts", value: contacts },
|
|
6278
|
+
{ label: "Submissions", value: submissions }
|
|
6279
|
+
];
|
|
6280
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "space-y-3", children: [
|
|
6281
|
+
rows.map((r) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
|
|
6282
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between text-xs mb-1", children: [
|
|
6283
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-gray-600", children: r.label }),
|
|
6284
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "font-semibold text-gray-900", children: r.value.toLocaleString() })
|
|
6285
|
+
] }),
|
|
6286
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "h-2 rounded bg-gray-100 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "h-2 bg-gray-700 rounded", style: { width: `${safePct(r.value, base)}%` } }) })
|
|
6287
|
+
] }, r.label)),
|
|
6288
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "pt-1 text-xs text-gray-600", children: [
|
|
6289
|
+
"Contact rate:",
|
|
6290
|
+
" ",
|
|
6291
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "font-semibold text-gray-900", children: [
|
|
6292
|
+
visitors > 0 ? (contacts / visitors * 100).toFixed(1) : "0.0",
|
|
6293
|
+
"%"
|
|
6294
|
+
] }),
|
|
6295
|
+
" ",
|
|
6296
|
+
" | Submission rate:",
|
|
6297
|
+
" ",
|
|
6298
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "font-semibold text-gray-900", children: [
|
|
6299
|
+
visitors > 0 ? (submissions / visitors * 100).toFixed(1) : "0.0",
|
|
6300
|
+
"%"
|
|
6301
|
+
] })
|
|
6302
|
+
] })
|
|
6303
|
+
] });
|
|
6304
|
+
})()
|
|
6305
|
+
] }),
|
|
6306
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-100 p-4 shadow-sm", children: [
|
|
6307
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-base font-semibold text-gray-800 mb-3", children: "CRM Distribution" }),
|
|
6308
|
+
(() => {
|
|
6309
|
+
const contacts = dashboardStats?.contacts.total || 0;
|
|
6310
|
+
const forms = dashboardStats?.forms.total || 0;
|
|
6311
|
+
const blogs = dashboardStats?.blogs || 0;
|
|
6312
|
+
const users = dashboardStats?.users || 0;
|
|
6313
|
+
const base = Math.max(contacts, forms, blogs, users, 1);
|
|
6314
|
+
const rows = [
|
|
6315
|
+
{ label: "Contacts", value: contacts },
|
|
6316
|
+
{ label: "Forms", value: forms },
|
|
6317
|
+
{ label: "Blogs", value: blogs },
|
|
6318
|
+
{ label: "Users", value: users }
|
|
6319
|
+
];
|
|
6320
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "space-y-2", children: rows.map((r) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
|
|
6321
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between text-xs mb-1", children: [
|
|
6322
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-gray-600", children: r.label }),
|
|
6323
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "font-semibold text-gray-900", children: r.value })
|
|
6324
|
+
] }),
|
|
6325
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "h-2 rounded bg-gray-100 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "h-2 bg-gray-700 rounded", style: { width: `${safePct(r.value, base)}%` } }) })
|
|
6326
|
+
] }, r.label)) });
|
|
6327
|
+
})()
|
|
6328
|
+
] })
|
|
6329
|
+
] }),
|
|
6330
|
+
analyticsEnabled && analyticsData && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-100 p-4 shadow-sm mb-2", children: [
|
|
6331
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-base font-semibold text-gray-800 mb-3", children: "Top Content & Sources" }),
|
|
6332
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6", children: [
|
|
6333
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
|
|
6334
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs font-semibold text-gray-700 mb-2", children: "Top Pages" }),
|
|
6335
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "space-y-1", children: analyticsData.topPages.slice(0, 5).map((page, index) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between text-xs", children: [
|
|
6336
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-gray-600 truncate max-w-[75%]", children: page.page }),
|
|
6337
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "font-semibold text-gray-900", children: page.views.toLocaleString() })
|
|
6338
|
+
] }, index)) })
|
|
6339
|
+
] }),
|
|
6340
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { children: [
|
|
6341
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs font-semibold text-gray-700 mb-2", children: "Traffic Sources" }),
|
|
6342
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "space-y-1", children: analyticsData.trafficSources.slice(0, 5).map((source, index) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between text-xs", children: [
|
|
6343
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-gray-600", children: source.source }),
|
|
6344
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "font-semibold text-gray-900", children: source.sessions.toLocaleString() })
|
|
6345
|
+
] }, index)) })
|
|
6346
|
+
] })
|
|
6347
|
+
] })
|
|
6348
|
+
] })
|
|
6349
|
+
] }),
|
|
6350
|
+
activeTab === "sales" && !isLoading && storeEnabled && ecommerceData && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
6351
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "border-t-2 border-dotted border-gray-200 my-6" }),
|
|
6352
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "mb-4", children: [
|
|
6353
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center mb-2", children: [
|
|
6354
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react22.BarChart3, { className: "w-4 h-4 text-gray-600 mr-2" }),
|
|
6355
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h2", { className: "text-sm font-semibold text-gray-800", children: "Store Analytics" })
|
|
6356
|
+
] }),
|
|
6357
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3", children: [
|
|
6358
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("button", { onClick: () => router.push("/admin/orders"), className: "text-left bg-white rounded border border-gray-200 p-2.5 shadow-sm", children: [
|
|
6359
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-600", children: "Net Sales" }),
|
|
6360
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-lg font-bold text-gray-900", children: formatMoney4(ecommerceData.kpis.netSales) })
|
|
6361
|
+
] }),
|
|
6362
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("button", { onClick: () => router.push("/admin/orders"), className: "text-left bg-white rounded border border-gray-200 p-2.5 shadow-sm", children: [
|
|
6363
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-600", children: "Orders" }),
|
|
6364
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-lg font-bold text-gray-900", children: ecommerceData.kpis.ordersPlaced })
|
|
6365
|
+
] }),
|
|
6366
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("button", { onClick: () => router.push("/admin/orders"), className: "text-left bg-white rounded border border-gray-200 p-2.5 shadow-sm", children: [
|
|
6367
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-600", children: "AOV" }),
|
|
6368
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-lg font-bold text-gray-900", children: formatMoney4(ecommerceData.kpis.averageOrderValue) })
|
|
6369
|
+
] }),
|
|
6370
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("button", { onClick: () => router.push("/admin/contacts"), className: "text-left bg-white rounded border border-gray-200 p-2.5 shadow-sm", children: [
|
|
6371
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-600", children: "Returning Customer Rate" }),
|
|
6372
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-lg font-bold text-gray-900", children: [
|
|
6373
|
+
ecommerceData.kpis.returningCustomerRate.toFixed(1),
|
|
6374
|
+
"%"
|
|
6375
|
+
] })
|
|
6376
|
+
] }),
|
|
6377
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("button", { onClick: () => router.push("/admin/orders"), className: "text-left bg-white rounded border border-gray-200 p-2.5 shadow-sm", children: [
|
|
6378
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-600", children: "Return Rate" }),
|
|
6379
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-lg font-bold text-gray-900", children: [
|
|
6380
|
+
ecommerceData.kpis.returnRate.toFixed(1),
|
|
6381
|
+
"%"
|
|
6382
|
+
] })
|
|
6383
|
+
] }),
|
|
6384
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("button", { onClick: () => router.push("/admin/payments"), className: "text-left bg-white rounded border border-gray-200 p-2.5 shadow-sm", children: [
|
|
6385
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-600", children: "Payment Success" }),
|
|
6386
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-lg font-bold text-gray-900", children: [
|
|
6387
|
+
ecommerceData.kpis.paymentSuccessRate.toFixed(1),
|
|
6388
|
+
"%"
|
|
6389
|
+
] })
|
|
6390
|
+
] })
|
|
6391
|
+
] })
|
|
6392
|
+
] }),
|
|
6393
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6", children: [
|
|
6394
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-100 p-4 shadow-sm", children: [
|
|
6395
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-base font-semibold text-gray-800 mb-3", children: "Sales Over Time" }),
|
|
6396
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "space-y-2 max-h-64 overflow-auto", children: ecommerceData.salesOverTime.slice(-10).map((point) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between text-xs", children: [
|
|
6397
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-gray-600", children: point.date }),
|
|
6398
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "font-semibold text-gray-900", children: [
|
|
6399
|
+
formatMoney4(point.value),
|
|
6400
|
+
" (",
|
|
6401
|
+
point.orders,
|
|
6402
|
+
")"
|
|
6403
|
+
] })
|
|
6404
|
+
] }, point.date)) })
|
|
6405
|
+
] }),
|
|
6406
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-100 p-4 shadow-sm", children: [
|
|
6407
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-base font-semibold text-gray-800 mb-3", children: "Top Products" }),
|
|
6408
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "space-y-2", children: ecommerceData.topProducts.map((p, idx) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
6409
|
+
"button",
|
|
6410
|
+
{
|
|
6411
|
+
onClick: () => router.push("/admin/products"),
|
|
6412
|
+
className: "w-full text-left flex items-center justify-between p-2 bg-gray-50 rounded-lg",
|
|
6413
|
+
children: [
|
|
6414
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-xs text-gray-700 truncate max-w-[70%]", children: p.name }),
|
|
6415
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "text-xs font-semibold text-gray-900", children: [
|
|
6416
|
+
p.units,
|
|
6417
|
+
" / ",
|
|
6418
|
+
formatMoney4(p.sales)
|
|
6419
|
+
] })
|
|
6420
|
+
]
|
|
6421
|
+
},
|
|
6422
|
+
`${p.name}-${idx}`
|
|
6423
|
+
)) })
|
|
6424
|
+
] })
|
|
6425
|
+
] }),
|
|
6426
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-6 mb-2", children: [
|
|
6427
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-100 p-4 shadow-sm", children: [
|
|
6428
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-base font-semibold text-gray-800 mb-2", children: "Customer Mix" }),
|
|
6429
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-gray-700", children: [
|
|
6430
|
+
"New: ",
|
|
6431
|
+
ecommerceData.customerMix.newCustomers
|
|
6432
|
+
] }),
|
|
6433
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-gray-700", children: [
|
|
6434
|
+
"Returning: ",
|
|
6435
|
+
ecommerceData.customerMix.returningCustomers
|
|
6436
|
+
] }),
|
|
6437
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-gray-700", children: [
|
|
6438
|
+
"Repeat rate: ",
|
|
6439
|
+
ecommerceData.customerMix.repeatPurchaseRate.toFixed(1),
|
|
6440
|
+
"%"
|
|
6441
|
+
] })
|
|
6442
|
+
] }),
|
|
6443
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-100 p-4 shadow-sm", children: [
|
|
6444
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-base font-semibold text-gray-800 mb-2", children: "Returns Trend" }),
|
|
6445
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-gray-700", children: [
|
|
6446
|
+
"Return value: ",
|
|
6447
|
+
formatMoney4(ecommerceData.kpis.returnValue)
|
|
6448
|
+
] }),
|
|
6449
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-gray-700", children: [
|
|
6450
|
+
"Return orders: ",
|
|
6451
|
+
ecommerceData.returnsTrend.reduce((s, r) => s + r.count, 0)
|
|
6452
|
+
] }),
|
|
6453
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-gray-700", children: [
|
|
6454
|
+
"Return rate: ",
|
|
6455
|
+
ecommerceData.kpis.returnRate.toFixed(1),
|
|
6456
|
+
"%"
|
|
6457
|
+
] })
|
|
6458
|
+
] }),
|
|
6459
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "bg-white rounded-lg border border-gray-100 p-4 shadow-sm", children: [
|
|
6460
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-base font-semibold text-gray-800 mb-2", children: "Inventory Risk" }),
|
|
6461
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-gray-700", children: [
|
|
6462
|
+
"Out of stock: ",
|
|
6463
|
+
ecommerceData.inventoryRisk.outOfStockCount
|
|
6464
|
+
] }),
|
|
6465
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-gray-700", children: [
|
|
6466
|
+
"Low stock: ",
|
|
6467
|
+
ecommerceData.inventoryRisk.lowStockCount
|
|
6468
|
+
] }),
|
|
6469
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-gray-700", children: [
|
|
6470
|
+
"Total units: ",
|
|
6471
|
+
ecommerceData.inventoryRisk.totalInventory
|
|
6472
|
+
] })
|
|
6473
|
+
] })
|
|
6474
|
+
] })
|
|
6475
|
+
] }),
|
|
5775
6476
|
isLoading && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-center py-8", children: [
|
|
5776
6477
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "animate-spin rounded-full h-5 w-5 border-2 border-gray-300 border-t-gray-600" }),
|
|
5777
6478
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "ml-2 text-sm text-gray-600", children: "Loading dashboard data..." })
|
|
5778
6479
|
] }),
|
|
5779
|
-
!isLoading && !analyticsEnabled && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "text-center py-8", children: [
|
|
6480
|
+
!isLoading && activeTab === "overview" && !analyticsEnabled && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "text-center py-8", children: [
|
|
5780
6481
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react22.Globe, { className: "w-10 h-10 text-gray-400 mx-auto mb-3" }),
|
|
5781
6482
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-base font-medium text-gray-600 mb-1", children: "Analytics Not Configured" }),
|
|
5782
6483
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-500", children: "Google Analytics integration is required to view website analytics." })
|
|
6484
|
+
] }),
|
|
6485
|
+
!isLoading && activeTab === "sales" && !storeEnabled && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "text-center py-8", children: [
|
|
6486
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h3", { className: "text-base font-medium text-gray-600 mb-1", children: "Store analytics unavailable" }),
|
|
6487
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-gray-500", children: "Enable store/ecomm to view sales reports." })
|
|
5783
6488
|
] })
|
|
5784
6489
|
] })
|
|
5785
6490
|
] });
|
|
5786
6491
|
}
|
|
5787
6492
|
|
|
5788
6493
|
// src/admin/pages/AdminPageResolver.tsx
|
|
5789
|
-
var
|
|
5790
|
-
var
|
|
6494
|
+
var import_react48 = require("react");
|
|
6495
|
+
var import_navigation21 = require("next/navigation");
|
|
5791
6496
|
|
|
5792
6497
|
// src/admin/pages/SubmissionDetailPage.tsx
|
|
5793
|
-
var
|
|
6498
|
+
var import_react33 = require("react");
|
|
5794
6499
|
var import_link9 = __toESM(require("next/link"), 1);
|
|
5795
6500
|
var import_lucide_react24 = require("lucide-react");
|
|
5796
6501
|
|
|
5797
6502
|
// src/components/Admin/DetailPageHeader.tsx
|
|
5798
6503
|
var import_link8 = __toESM(require("next/link"), 1);
|
|
5799
|
-
var
|
|
6504
|
+
var import_navigation12 = require("next/navigation");
|
|
5800
6505
|
var import_lucide_react23 = require("lucide-react");
|
|
5801
6506
|
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
5802
6507
|
function DetailPageHeader({
|
|
@@ -5808,7 +6513,7 @@ function DetailPageHeader({
|
|
|
5808
6513
|
onClose,
|
|
5809
6514
|
menuItems = []
|
|
5810
6515
|
}) {
|
|
5811
|
-
const router = (0,
|
|
6516
|
+
const router = (0, import_navigation12.useRouter)();
|
|
5812
6517
|
const handleClose = () => {
|
|
5813
6518
|
if (onClose) onClose();
|
|
5814
6519
|
else if (closeHref) router.push(closeHref);
|
|
@@ -5833,20 +6538,32 @@ function DetailPageHeader({
|
|
|
5833
6538
|
] })
|
|
5834
6539
|
] }),
|
|
5835
6540
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
5836
|
-
menuItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
5837
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
6541
|
+
menuItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
|
|
6542
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex items-center gap-2 md:hidden", children: menuItems.map((item, i) => {
|
|
6543
|
+
const Icon2 = item.icon;
|
|
6544
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
6545
|
+
Button,
|
|
6546
|
+
{
|
|
6547
|
+
variant: item.variant ?? "outline",
|
|
6548
|
+
size: "icon",
|
|
6549
|
+
className: "h-8 w-8 border border-gray-600 bg-transparent text-white hover:bg-gray-700",
|
|
6550
|
+
onClick: item.onClick,
|
|
6551
|
+
title: item.label,
|
|
6552
|
+
children: [
|
|
6553
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Icon2, { className: "h-4 w-4" }),
|
|
6554
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "sr-only", children: item.label })
|
|
6555
|
+
]
|
|
6556
|
+
},
|
|
6557
|
+
i
|
|
6558
|
+
);
|
|
6559
|
+
}) }),
|
|
6560
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "hidden md:flex items-center gap-2", children: menuItems.map((item, i) => {
|
|
6561
|
+
const Icon2 = item.icon;
|
|
6562
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(Button, { variant: item.variant ?? "outline", size: "sm", onClick: item.onClick, children: [
|
|
6563
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Icon2, { className: "h-4 w-4 mr-1" }),
|
|
6564
|
+
item.label
|
|
6565
|
+
] }, i);
|
|
6566
|
+
}) })
|
|
5850
6567
|
] }),
|
|
5851
6568
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
5852
6569
|
Button,
|
|
@@ -5868,10 +6585,10 @@ function DetailPageHeader({
|
|
|
5868
6585
|
// src/admin/pages/SubmissionDetailPage.tsx
|
|
5869
6586
|
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
5870
6587
|
function SubmissionDetailPage({ submissionId }) {
|
|
5871
|
-
const [submission, setSubmission] = (0,
|
|
5872
|
-
const [loading, setLoading] = (0,
|
|
5873
|
-
const [error, setError] = (0,
|
|
5874
|
-
(0,
|
|
6588
|
+
const [submission, setSubmission] = (0, import_react33.useState)(null);
|
|
6589
|
+
const [loading, setLoading] = (0, import_react33.useState)(true);
|
|
6590
|
+
const [error, setError] = (0, import_react33.useState)(null);
|
|
6591
|
+
(0, import_react33.useEffect)(() => {
|
|
5875
6592
|
async function load() {
|
|
5876
6593
|
try {
|
|
5877
6594
|
setLoading(true);
|
|
@@ -5979,9 +6696,9 @@ function SubmissionDetailPage({ submissionId }) {
|
|
|
5979
6696
|
}
|
|
5980
6697
|
|
|
5981
6698
|
// src/admin/pages/OrderDetailPage.tsx
|
|
5982
|
-
var
|
|
6699
|
+
var import_react34 = require("react");
|
|
5983
6700
|
var import_link10 = __toESM(require("next/link"), 1);
|
|
5984
|
-
var
|
|
6701
|
+
var import_navigation13 = require("next/navigation");
|
|
5985
6702
|
var import_lucide_react25 = require("lucide-react");
|
|
5986
6703
|
|
|
5987
6704
|
// src/components/Admin/DetailPageLayout.tsx
|
|
@@ -6023,11 +6740,13 @@ function formatAddress(a) {
|
|
|
6023
6740
|
return parts.join(", ") || "\u2014";
|
|
6024
6741
|
}
|
|
6025
6742
|
function OrderDetailPage({ orderId }) {
|
|
6026
|
-
const router = (0,
|
|
6027
|
-
const [order, setOrder] = (0,
|
|
6028
|
-
const [loading, setLoading] = (0,
|
|
6029
|
-
const [error, setError] = (0,
|
|
6030
|
-
(0,
|
|
6743
|
+
const router = (0, import_navigation13.useRouter)();
|
|
6744
|
+
const [order, setOrder] = (0, import_react34.useState)(null);
|
|
6745
|
+
const [loading, setLoading] = (0, import_react34.useState)(true);
|
|
6746
|
+
const [error, setError] = (0, import_react34.useState)(null);
|
|
6747
|
+
const [erpEnabled, setErpEnabled] = (0, import_react34.useState)(false);
|
|
6748
|
+
const [reposting, setReposting] = (0, import_react34.useState)(false);
|
|
6749
|
+
(0, import_react34.useEffect)(() => {
|
|
6031
6750
|
async function load() {
|
|
6032
6751
|
try {
|
|
6033
6752
|
setLoading(true);
|
|
@@ -6050,6 +6769,39 @@ function OrderDetailPage({ orderId }) {
|
|
|
6050
6769
|
}
|
|
6051
6770
|
load();
|
|
6052
6771
|
}, [orderId]);
|
|
6772
|
+
(0, import_react34.useEffect)(() => {
|
|
6773
|
+
let cancelled = false;
|
|
6774
|
+
(async () => {
|
|
6775
|
+
try {
|
|
6776
|
+
const res = await fetch(`/api/orders/${orderId}/repost-erp`);
|
|
6777
|
+
if (!res.ok) return;
|
|
6778
|
+
const data = await res.json();
|
|
6779
|
+
if (!cancelled) setErpEnabled(data.enabled === true);
|
|
6780
|
+
} catch {
|
|
6781
|
+
}
|
|
6782
|
+
})();
|
|
6783
|
+
return () => {
|
|
6784
|
+
cancelled = true;
|
|
6785
|
+
};
|
|
6786
|
+
}, [orderId]);
|
|
6787
|
+
async function handleRepostToErp() {
|
|
6788
|
+
if (reposting) return;
|
|
6789
|
+
setReposting(true);
|
|
6790
|
+
setError(null);
|
|
6791
|
+
try {
|
|
6792
|
+
const res = await fetch(`/api/orders/${orderId}/repost-erp`, { method: "POST" });
|
|
6793
|
+
if (!res.ok) {
|
|
6794
|
+
const body = await res.json().catch(() => ({}));
|
|
6795
|
+
setError(body.error ?? "Failed to repost to ERP");
|
|
6796
|
+
return;
|
|
6797
|
+
}
|
|
6798
|
+
router.refresh();
|
|
6799
|
+
} catch {
|
|
6800
|
+
setError("Failed to repost to ERP");
|
|
6801
|
+
} finally {
|
|
6802
|
+
setReposting(false);
|
|
6803
|
+
}
|
|
6804
|
+
}
|
|
6053
6805
|
if (loading) {
|
|
6054
6806
|
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "rounded-lg bg-white p-6 shadow-md", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex justify-center py-12", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-gray-500", children: "Loading..." }) }) });
|
|
6055
6807
|
}
|
|
@@ -6072,7 +6824,8 @@ function OrderDetailPage({ orderId }) {
|
|
|
6072
6824
|
{
|
|
6073
6825
|
title: `Order ${order.orderNumber}`,
|
|
6074
6826
|
subtitle: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-sm text-gray-400", children: formatDateTime(order.createdAt) }),
|
|
6075
|
-
closeHref: "/admin/orders"
|
|
6827
|
+
closeHref: "/admin/orders",
|
|
6828
|
+
menuItems: erpEnabled ? [{ label: reposting ? "Reposting..." : "Repost to ERP", icon: import_lucide_react25.RefreshCw, onClick: handleRepostToErp }] : void 0
|
|
6076
6829
|
}
|
|
6077
6830
|
),
|
|
6078
6831
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
@@ -6312,9 +7065,9 @@ function OrderDetailPage({ orderId }) {
|
|
|
6312
7065
|
}
|
|
6313
7066
|
|
|
6314
7067
|
// src/admin/pages/PaymentDetailPage.tsx
|
|
6315
|
-
var
|
|
7068
|
+
var import_react35 = require("react");
|
|
6316
7069
|
var import_link11 = __toESM(require("next/link"), 1);
|
|
6317
|
-
var
|
|
7070
|
+
var import_navigation14 = require("next/navigation");
|
|
6318
7071
|
var import_lucide_react26 = require("lucide-react");
|
|
6319
7072
|
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
6320
7073
|
function formatMoney2(amount, currency = "INR") {
|
|
@@ -6325,11 +7078,11 @@ function formatMoney2(amount, currency = "INR") {
|
|
|
6325
7078
|
}).format(Number(amount));
|
|
6326
7079
|
}
|
|
6327
7080
|
function PaymentDetailPage({ paymentId }) {
|
|
6328
|
-
const router = (0,
|
|
6329
|
-
const [payment, setPayment] = (0,
|
|
6330
|
-
const [loading, setLoading] = (0,
|
|
6331
|
-
const [error, setError] = (0,
|
|
6332
|
-
(0,
|
|
7081
|
+
const router = (0, import_navigation14.useRouter)();
|
|
7082
|
+
const [payment, setPayment] = (0, import_react35.useState)(null);
|
|
7083
|
+
const [loading, setLoading] = (0, import_react35.useState)(true);
|
|
7084
|
+
const [error, setError] = (0, import_react35.useState)(null);
|
|
7085
|
+
(0, import_react35.useEffect)(() => {
|
|
6333
7086
|
async function load() {
|
|
6334
7087
|
try {
|
|
6335
7088
|
setLoading(true);
|
|
@@ -6471,9 +7224,9 @@ function PaymentDetailPage({ paymentId }) {
|
|
|
6471
7224
|
}
|
|
6472
7225
|
|
|
6473
7226
|
// src/admin/pages/ContactDetailPage.tsx
|
|
6474
|
-
var
|
|
7227
|
+
var import_react36 = require("react");
|
|
6475
7228
|
var import_link12 = __toESM(require("next/link"), 1);
|
|
6476
|
-
var
|
|
7229
|
+
var import_navigation15 = require("next/navigation");
|
|
6477
7230
|
var import_lucide_react27 = require("lucide-react");
|
|
6478
7231
|
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
6479
7232
|
function formatMoney3(amount, currency = "INR") {
|
|
@@ -6489,13 +7242,13 @@ function formatAddress2(a) {
|
|
|
6489
7242
|
}
|
|
6490
7243
|
var ADDRESS_TAGS = [{ value: "", label: "\u2014" }, { value: "default", label: "Default" }, { value: "billing", label: "Billing" }, { value: "shipping", label: "Shipping" }];
|
|
6491
7244
|
function ContactDetailPage({ contactId }) {
|
|
6492
|
-
const router = (0,
|
|
6493
|
-
const [contact, setContact] = (0,
|
|
6494
|
-
const [loading, setLoading] = (0,
|
|
6495
|
-
const [error, setError] = (0,
|
|
6496
|
-
const [addAddressOpen, setAddAddressOpen] = (0,
|
|
6497
|
-
const [addAddressSaving, setAddAddressSaving] = (0,
|
|
6498
|
-
const [addAddressForm, setAddAddressForm] = (0,
|
|
7245
|
+
const router = (0, import_navigation15.useRouter)();
|
|
7246
|
+
const [contact, setContact] = (0, import_react36.useState)(null);
|
|
7247
|
+
const [loading, setLoading] = (0, import_react36.useState)(true);
|
|
7248
|
+
const [error, setError] = (0, import_react36.useState)(null);
|
|
7249
|
+
const [addAddressOpen, setAddAddressOpen] = (0, import_react36.useState)(false);
|
|
7250
|
+
const [addAddressSaving, setAddAddressSaving] = (0, import_react36.useState)(false);
|
|
7251
|
+
const [addAddressForm, setAddAddressForm] = (0, import_react36.useState)({
|
|
6499
7252
|
tag: "",
|
|
6500
7253
|
line1: "",
|
|
6501
7254
|
line2: "",
|
|
@@ -6507,7 +7260,7 @@ function ContactDetailPage({ contactId }) {
|
|
|
6507
7260
|
const loadContact = () => {
|
|
6508
7261
|
fetch(`/api/contacts/${contactId}`).then((res) => res.ok ? res.json() : null).then((data) => setContact(data)).catch(() => setContact(null));
|
|
6509
7262
|
};
|
|
6510
|
-
(0,
|
|
7263
|
+
(0, import_react36.useEffect)(() => {
|
|
6511
7264
|
async function load() {
|
|
6512
7265
|
try {
|
|
6513
7266
|
setLoading(true);
|
|
@@ -6843,9 +7596,9 @@ function ContactDetailPage({ contactId }) {
|
|
|
6843
7596
|
}
|
|
6844
7597
|
|
|
6845
7598
|
// src/admin/pages/SettingsPage.tsx
|
|
6846
|
-
var
|
|
6847
|
-
var
|
|
6848
|
-
var
|
|
7599
|
+
var import_react37 = require("react");
|
|
7600
|
+
var import_navigation16 = require("next/navigation");
|
|
7601
|
+
var import_sonner5 = require("sonner");
|
|
6849
7602
|
var import_lucide_react28 = require("lucide-react");
|
|
6850
7603
|
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
6851
7604
|
var DEFAULTS = {
|
|
@@ -6869,25 +7622,25 @@ function hexToHsl(hex) {
|
|
|
6869
7622
|
return `${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
|
|
6870
7623
|
}
|
|
6871
7624
|
function SettingsPage() {
|
|
6872
|
-
const searchParams = (0,
|
|
7625
|
+
const searchParams = (0, import_navigation16.useSearchParams)();
|
|
6873
7626
|
const tabParam = searchParams.get("tab");
|
|
6874
|
-
const { theme, themeRegistry } = (0,
|
|
6875
|
-
const [tab, setTab] = (0,
|
|
7627
|
+
const { theme, themeRegistry } = (0, import_react37.useContext)(AdminConfigContext);
|
|
7628
|
+
const [tab, setTab] = (0, import_react37.useState)(
|
|
6876
7629
|
tabParam === "navbar" ? "navbar" : tabParam === "store" ? "store" : "appearance"
|
|
6877
7630
|
);
|
|
6878
|
-
(0,
|
|
7631
|
+
(0, import_react37.useEffect)(() => {
|
|
6879
7632
|
if (tabParam === "navbar") setTab("navbar");
|
|
6880
7633
|
else if (tabParam === "store") setTab("store");
|
|
6881
7634
|
}, [tabParam]);
|
|
6882
|
-
const [settings, setSettings] = (0,
|
|
6883
|
-
const [loading, setLoading] = (0,
|
|
6884
|
-
const [saving, setSaving] = (0,
|
|
6885
|
-
const [activeThemeId, setActiveThemeId] = (0,
|
|
6886
|
-
const [navbarConfig, setNavbarConfig] = (0,
|
|
6887
|
-
const [themeSettingsLoading, setThemeSettingsLoading] = (0,
|
|
6888
|
-
const [storeEnabled, setStoreEnabled] = (0,
|
|
6889
|
-
const [storeSettingsLoading, setStoreSettingsLoading] = (0,
|
|
6890
|
-
(0,
|
|
7635
|
+
const [settings, setSettings] = (0, import_react37.useState)(DEFAULTS);
|
|
7636
|
+
const [loading, setLoading] = (0, import_react37.useState)(true);
|
|
7637
|
+
const [saving, setSaving] = (0, import_react37.useState)(false);
|
|
7638
|
+
const [activeThemeId, setActiveThemeId] = (0, import_react37.useState)("");
|
|
7639
|
+
const [navbarConfig, setNavbarConfig] = (0, import_react37.useState)({ logo: "", items: [], ctaLabel: "", ctaUrl: "" });
|
|
7640
|
+
const [themeSettingsLoading, setThemeSettingsLoading] = (0, import_react37.useState)(true);
|
|
7641
|
+
const [storeEnabled, setStoreEnabled] = (0, import_react37.useState)(false);
|
|
7642
|
+
const [storeSettingsLoading, setStoreSettingsLoading] = (0, import_react37.useState)(true);
|
|
7643
|
+
(0, import_react37.useEffect)(() => {
|
|
6891
7644
|
fetch("/api/settings/admin_view").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
6892
7645
|
setSettings({
|
|
6893
7646
|
primaryColor: data.primaryColor || DEFAULTS.primaryColor,
|
|
@@ -6896,7 +7649,7 @@ function SettingsPage() {
|
|
|
6896
7649
|
});
|
|
6897
7650
|
}).finally(() => setLoading(false));
|
|
6898
7651
|
}, []);
|
|
6899
|
-
(0,
|
|
7652
|
+
(0, import_react37.useEffect)(() => {
|
|
6900
7653
|
fetch("/api/settings/theme").then((r) => r.ok ? r.json() : null).then((data) => {
|
|
6901
7654
|
if (!data) return;
|
|
6902
7655
|
setActiveThemeId((data.activeThemeId || "").trim());
|
|
@@ -6908,7 +7661,7 @@ function SettingsPage() {
|
|
|
6908
7661
|
}
|
|
6909
7662
|
}).finally(() => setThemeSettingsLoading(false));
|
|
6910
7663
|
}, []);
|
|
6911
|
-
(0,
|
|
7664
|
+
(0, import_react37.useEffect)(() => {
|
|
6912
7665
|
fetch("/api/settings/store").then((r) => r.ok ? r.json() : {}).then((data) => {
|
|
6913
7666
|
setStoreEnabled(data.enabled === "true");
|
|
6914
7667
|
}).finally(() => setStoreSettingsLoading(false));
|
|
@@ -6932,9 +7685,9 @@ function SettingsPage() {
|
|
|
6932
7685
|
document.documentElement.style.setProperty("--secondary", hexToHsl(settings.secondaryColor));
|
|
6933
7686
|
if (settings.compactView === "true") document.documentElement.classList.add("compact");
|
|
6934
7687
|
else document.documentElement.classList.remove("compact");
|
|
6935
|
-
|
|
7688
|
+
import_sonner5.toast.success("Settings saved");
|
|
6936
7689
|
} catch {
|
|
6937
|
-
|
|
7690
|
+
import_sonner5.toast.error("Failed to save settings");
|
|
6938
7691
|
} finally {
|
|
6939
7692
|
setSaving(false);
|
|
6940
7693
|
}
|
|
@@ -6948,10 +7701,10 @@ function SettingsPage() {
|
|
|
6948
7701
|
headers: { "Content-Type": "application/json" },
|
|
6949
7702
|
body: JSON.stringify({ activeThemeId: { value: id, type: "public" } })
|
|
6950
7703
|
});
|
|
6951
|
-
|
|
7704
|
+
import_sonner5.toast.success("Theme updated");
|
|
6952
7705
|
window.location.reload();
|
|
6953
7706
|
} catch {
|
|
6954
|
-
|
|
7707
|
+
import_sonner5.toast.error("Failed to save theme");
|
|
6955
7708
|
} finally {
|
|
6956
7709
|
setSaving(false);
|
|
6957
7710
|
}
|
|
@@ -6964,9 +7717,9 @@ function SettingsPage() {
|
|
|
6964
7717
|
headers: { "Content-Type": "application/json" },
|
|
6965
7718
|
body: JSON.stringify({ navbar: { value: JSON.stringify(navbarConfig), type: "public" } })
|
|
6966
7719
|
});
|
|
6967
|
-
|
|
7720
|
+
import_sonner5.toast.success("Navbar saved");
|
|
6968
7721
|
} catch {
|
|
6969
|
-
|
|
7722
|
+
import_sonner5.toast.error("Failed to save");
|
|
6970
7723
|
} finally {
|
|
6971
7724
|
setSaving(false);
|
|
6972
7725
|
}
|
|
@@ -6979,10 +7732,10 @@ function SettingsPage() {
|
|
|
6979
7732
|
headers: { "Content-Type": "application/json" },
|
|
6980
7733
|
body: JSON.stringify({ enabled: { value: storeEnabled ? "true" : "false", type: "public" } })
|
|
6981
7734
|
});
|
|
6982
|
-
|
|
7735
|
+
import_sonner5.toast.success("Store settings saved");
|
|
6983
7736
|
window.location.reload();
|
|
6984
7737
|
} catch {
|
|
6985
|
-
|
|
7738
|
+
import_sonner5.toast.error("Failed to save store settings");
|
|
6986
7739
|
} finally {
|
|
6987
7740
|
setSaving(false);
|
|
6988
7741
|
}
|
|
@@ -7098,11 +7851,24 @@ function SettingsPage() {
|
|
|
7098
7851
|
}
|
|
7099
7852
|
|
|
7100
7853
|
// src/admin/pages/MediaLibraryPage.tsx
|
|
7101
|
-
var
|
|
7854
|
+
var import_react38 = __toESM(require("react"), 1);
|
|
7855
|
+
|
|
7856
|
+
// src/lib/media-zip-extract.ts
|
|
7857
|
+
var import_typeorm = require("typeorm");
|
|
7858
|
+
var ZIP_MIME_TYPES = /* @__PURE__ */ new Set(["application/zip", "application/x-zip-compressed"]);
|
|
7859
|
+
var MAX_TOTAL_UNCOMPRESSED = 80 * 1024 * 1024;
|
|
7860
|
+
function isZipMedia(mime, filename) {
|
|
7861
|
+
if (mime && ZIP_MIME_TYPES.has(mime)) return true;
|
|
7862
|
+
return filename.toLowerCase().endsWith(".zip");
|
|
7863
|
+
}
|
|
7864
|
+
|
|
7865
|
+
// src/admin/pages/MediaLibraryPage.tsx
|
|
7102
7866
|
var import_lucide_react29 = require("lucide-react");
|
|
7103
|
-
var
|
|
7867
|
+
var import_sonner6 = require("sonner");
|
|
7104
7868
|
var import_jsx_runtime53 = require("react/jsx-runtime");
|
|
7105
|
-
function getTypeCategory(mime) {
|
|
7869
|
+
function getTypeCategory(mime, kind, filename) {
|
|
7870
|
+
if (kind === "folder" || mime === "inode/directory") return "folder";
|
|
7871
|
+
if (isZipMedia(mime, filename ?? "")) return "zip";
|
|
7106
7872
|
if (!mime) return "other";
|
|
7107
7873
|
if (mime.startsWith("image/")) return "image";
|
|
7108
7874
|
if (mime.startsWith("video/")) return "video";
|
|
@@ -7110,56 +7876,66 @@ function getTypeCategory(mime) {
|
|
|
7110
7876
|
if (mime === "application/pdf" || mime.startsWith("application/")) return "document";
|
|
7111
7877
|
return "other";
|
|
7112
7878
|
}
|
|
7113
|
-
function getTypeLabel(mime) {
|
|
7114
|
-
|
|
7879
|
+
function getTypeLabel(mime, kind, filename) {
|
|
7880
|
+
if (kind === "folder" || mime === "inode/directory") return "Folder";
|
|
7881
|
+
if (isZipMedia(mime, filename ?? "")) return "Zip";
|
|
7882
|
+
const c = getTypeCategory(mime, kind, filename);
|
|
7115
7883
|
const labels = {
|
|
7116
7884
|
image: "Image",
|
|
7117
7885
|
video: "Video",
|
|
7118
7886
|
audio: "Audio",
|
|
7119
7887
|
document: "Document",
|
|
7888
|
+
folder: "Folder",
|
|
7889
|
+
zip: "Zip",
|
|
7120
7890
|
other: "Other"
|
|
7121
7891
|
};
|
|
7122
7892
|
return labels[c] || "Other";
|
|
7123
7893
|
}
|
|
7124
|
-
function TypeIcon({ mimeType }) {
|
|
7125
|
-
|
|
7894
|
+
function TypeIcon({ mimeType, kind, filename }) {
|
|
7895
|
+
if (kind === "folder" || mimeType === "inode/directory") {
|
|
7896
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Folder, { className: "h-10 w-10 text-amber-500" });
|
|
7897
|
+
}
|
|
7898
|
+
if (isZipMedia(mimeType, filename ?? "")) {
|
|
7899
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Archive, { className: "h-10 w-10 text-violet-600" });
|
|
7900
|
+
}
|
|
7901
|
+
const c = getTypeCategory(mimeType, kind, filename);
|
|
7126
7902
|
if (c === "image") return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Image, { className: "h-8 w-8 text-gray-400" });
|
|
7127
7903
|
if (c === "video") return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Film, { className: "h-8 w-8 text-gray-400" });
|
|
7128
7904
|
if (c === "document") return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.FileText, { className: "h-8 w-8 text-gray-400" });
|
|
7129
7905
|
return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.File, { className: "h-8 w-8 text-gray-400" });
|
|
7130
7906
|
}
|
|
7131
|
-
function groupByMonth(items) {
|
|
7132
|
-
const groups = {};
|
|
7133
|
-
items.forEach((item) => {
|
|
7134
|
-
const d = new Date(item.createdAt);
|
|
7135
|
-
const key = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;
|
|
7136
|
-
const label = d.toLocaleDateString("en-US", { year: "numeric", month: "long" });
|
|
7137
|
-
if (!groups[label]) groups[label] = [];
|
|
7138
|
-
groups[label].push(item);
|
|
7139
|
-
});
|
|
7140
|
-
return groups;
|
|
7141
|
-
}
|
|
7142
7907
|
function MediaLibraryPage() {
|
|
7143
|
-
const [data, setData] = (0,
|
|
7144
|
-
const [loading, setLoading] = (0,
|
|
7145
|
-
const [page, setPage] = (0,
|
|
7146
|
-
const [totalPages, setTotalPages] = (0,
|
|
7147
|
-
const [search, setSearch] = (0,
|
|
7148
|
-
const [searchInput, setSearchInput] = (0,
|
|
7149
|
-
const [typeFilter, setTypeFilter] = (0,
|
|
7150
|
-
const [
|
|
7151
|
-
const [
|
|
7152
|
-
const [
|
|
7153
|
-
const [
|
|
7154
|
-
const
|
|
7155
|
-
const
|
|
7908
|
+
const [data, setData] = (0, import_react38.useState)([]);
|
|
7909
|
+
const [loading, setLoading] = (0, import_react38.useState)(true);
|
|
7910
|
+
const [page, setPage] = (0, import_react38.useState)(1);
|
|
7911
|
+
const [totalPages, setTotalPages] = (0, import_react38.useState)(1);
|
|
7912
|
+
const [search, setSearch] = (0, import_react38.useState)("");
|
|
7913
|
+
const [searchInput, setSearchInput] = (0, import_react38.useState)("");
|
|
7914
|
+
const [typeFilter, setTypeFilter] = (0, import_react38.useState)("");
|
|
7915
|
+
const [sortField, setSortField] = (0, import_react38.useState)("filename");
|
|
7916
|
+
const [sortOrder, setSortOrder] = (0, import_react38.useState)("asc");
|
|
7917
|
+
const [viewMode, setViewMode] = (0, import_react38.useState)("grid");
|
|
7918
|
+
const [uploading, setUploading] = (0, import_react38.useState)(false);
|
|
7919
|
+
const [currentParentId, setCurrentParentId] = (0, import_react38.useState)(null);
|
|
7920
|
+
const [crumb, setCrumb] = (0, import_react38.useState)([]);
|
|
7921
|
+
const [newFolderOpen, setNewFolderOpen] = (0, import_react38.useState)(false);
|
|
7922
|
+
const [newFolderName, setNewFolderName] = (0, import_react38.useState)("");
|
|
7923
|
+
const [editModal, setEditModal] = (0, import_react38.useState)(null);
|
|
7924
|
+
const [deleteTarget, setDeleteTarget] = (0, import_react38.useState)(null);
|
|
7925
|
+
const [editAlt, setEditAlt] = (0, import_react38.useState)("");
|
|
7926
|
+
const [editPublic, setEditPublic] = (0, import_react38.useState)(false);
|
|
7927
|
+
const [editFilename, setEditFilename] = (0, import_react38.useState)("");
|
|
7928
|
+
const [extractingId, setExtractingId] = (0, import_react38.useState)(null);
|
|
7929
|
+
const fileInputRef = (0, import_react38.useRef)(null);
|
|
7930
|
+
const load = (0, import_react38.useCallback)(async () => {
|
|
7156
7931
|
setLoading(true);
|
|
7157
7932
|
const params = new URLSearchParams({
|
|
7158
7933
|
page: String(page),
|
|
7159
7934
|
limit: "24",
|
|
7160
|
-
sortField
|
|
7161
|
-
sortOrder
|
|
7935
|
+
sortField,
|
|
7936
|
+
sortOrder
|
|
7162
7937
|
});
|
|
7938
|
+
params.set("parentId", currentParentId === null ? "" : String(currentParentId));
|
|
7163
7939
|
if (search) params.set("search", search);
|
|
7164
7940
|
if (typeFilter) params.set("type", typeFilter);
|
|
7165
7941
|
try {
|
|
@@ -7174,11 +7950,33 @@ function MediaLibraryPage() {
|
|
|
7174
7950
|
} finally {
|
|
7175
7951
|
setLoading(false);
|
|
7176
7952
|
}
|
|
7177
|
-
}, [page, search, typeFilter]);
|
|
7178
|
-
(0,
|
|
7953
|
+
}, [page, search, typeFilter, currentParentId, sortField, sortOrder]);
|
|
7954
|
+
(0, import_react38.useEffect)(() => {
|
|
7179
7955
|
load();
|
|
7180
7956
|
}, [load]);
|
|
7181
7957
|
const handleSearch = () => setSearch(searchInput.trim());
|
|
7958
|
+
const goRoot = (0, import_react38.useCallback)(() => {
|
|
7959
|
+
setPage(1);
|
|
7960
|
+
setCurrentParentId(null);
|
|
7961
|
+
setCrumb([]);
|
|
7962
|
+
}, []);
|
|
7963
|
+
const goCrumb = (0, import_react38.useCallback)((index) => {
|
|
7964
|
+
setPage(1);
|
|
7965
|
+
if (index < 0) {
|
|
7966
|
+
goRoot();
|
|
7967
|
+
return;
|
|
7968
|
+
}
|
|
7969
|
+
const slice = crumb.slice(0, index + 1);
|
|
7970
|
+
setCrumb(slice);
|
|
7971
|
+
const last = slice[slice.length - 1];
|
|
7972
|
+
setCurrentParentId(last.id);
|
|
7973
|
+
}, [crumb, goRoot]);
|
|
7974
|
+
const enterFolder = (item) => {
|
|
7975
|
+
if (item.kind !== "folder") return;
|
|
7976
|
+
setPage(1);
|
|
7977
|
+
setCurrentParentId(item.id);
|
|
7978
|
+
setCrumb((c) => [...c, { id: item.id, name: item.filename }]);
|
|
7979
|
+
};
|
|
7182
7980
|
const handleUpload = async (e) => {
|
|
7183
7981
|
const file = e.target.files?.[0];
|
|
7184
7982
|
if (!file) return;
|
|
@@ -7186,6 +7984,7 @@ function MediaLibraryPage() {
|
|
|
7186
7984
|
try {
|
|
7187
7985
|
const formData = new FormData();
|
|
7188
7986
|
formData.append("file", file);
|
|
7987
|
+
if (currentParentId != null) formData.append("parentId", String(currentParentId));
|
|
7189
7988
|
const uploadRes = await fetch("/api/upload", { method: "POST", body: formData });
|
|
7190
7989
|
const uploadJson = await uploadRes.json();
|
|
7191
7990
|
if (!uploadRes.ok) throw new Error(uploadJson.error || "Upload failed");
|
|
@@ -7195,69 +7994,134 @@ function MediaLibraryPage() {
|
|
|
7195
7994
|
method: "POST",
|
|
7196
7995
|
headers: { "Content-Type": "application/json" },
|
|
7197
7996
|
body: JSON.stringify({
|
|
7997
|
+
kind: "file",
|
|
7198
7998
|
filename: file.name,
|
|
7199
7999
|
url: filePath.startsWith("http") ? filePath : filePath,
|
|
7200
8000
|
mimeType: file.type || "application/octet-stream",
|
|
7201
8001
|
size: file.size,
|
|
7202
8002
|
alt: null,
|
|
7203
|
-
isPublic: false
|
|
8003
|
+
isPublic: false,
|
|
8004
|
+
parentId: currentParentId
|
|
7204
8005
|
})
|
|
7205
8006
|
});
|
|
7206
8007
|
if (!createRes.ok) throw new Error("Failed to create media record");
|
|
7207
|
-
|
|
8008
|
+
import_sonner6.toast.success("File uploaded");
|
|
7208
8009
|
load();
|
|
7209
8010
|
} catch (err) {
|
|
7210
|
-
|
|
8011
|
+
import_sonner6.toast.error(err instanceof Error ? err.message : "Upload failed");
|
|
7211
8012
|
} finally {
|
|
7212
8013
|
setUploading(false);
|
|
7213
8014
|
e.target.value = "";
|
|
7214
8015
|
}
|
|
7215
8016
|
};
|
|
7216
|
-
const
|
|
7217
|
-
|
|
8017
|
+
const createFolder = async () => {
|
|
8018
|
+
const name = newFolderName.trim();
|
|
8019
|
+
if (!name) {
|
|
8020
|
+
import_sonner6.toast.error("Enter a folder name");
|
|
8021
|
+
return;
|
|
8022
|
+
}
|
|
7218
8023
|
try {
|
|
7219
|
-
const res = await fetch(
|
|
8024
|
+
const res = await fetch("/api/media", {
|
|
8025
|
+
method: "POST",
|
|
8026
|
+
headers: { "Content-Type": "application/json" },
|
|
8027
|
+
body: JSON.stringify({
|
|
8028
|
+
kind: "folder",
|
|
8029
|
+
filename: name,
|
|
8030
|
+
parentId: currentParentId
|
|
8031
|
+
})
|
|
8032
|
+
});
|
|
8033
|
+
const j = await res.json().catch(() => ({}));
|
|
8034
|
+
if (!res.ok) throw new Error(j.error || "Failed to create folder");
|
|
8035
|
+
import_sonner6.toast.success("Folder created");
|
|
8036
|
+
setNewFolderOpen(false);
|
|
8037
|
+
setNewFolderName("");
|
|
8038
|
+
load();
|
|
8039
|
+
} catch (err) {
|
|
8040
|
+
import_sonner6.toast.error(err instanceof Error ? err.message : "Failed");
|
|
8041
|
+
}
|
|
8042
|
+
};
|
|
8043
|
+
const handleExtractZip = async (id) => {
|
|
8044
|
+
setExtractingId(id);
|
|
8045
|
+
try {
|
|
8046
|
+
const res = await fetch(`/api/media/extract/${id}`, { method: "POST" });
|
|
8047
|
+
const j = await res.json().catch(() => ({}));
|
|
8048
|
+
if (!res.ok) throw new Error(j.error || "Extract failed");
|
|
8049
|
+
import_sonner6.toast.success(
|
|
8050
|
+
`Extracted ${j.files ?? 0} file(s)` + (j.folderEntries ? `, ${j.folderEntries} folder path(s)` : "")
|
|
8051
|
+
);
|
|
8052
|
+
load();
|
|
8053
|
+
} catch (e) {
|
|
8054
|
+
import_sonner6.toast.error(e instanceof Error ? e.message : "Extract failed");
|
|
8055
|
+
} finally {
|
|
8056
|
+
setExtractingId(null);
|
|
8057
|
+
}
|
|
8058
|
+
};
|
|
8059
|
+
const handleDelete = async (item) => {
|
|
8060
|
+
try {
|
|
8061
|
+
const res = await fetch(`/api/media/${item.id}`, { method: "DELETE" });
|
|
7220
8062
|
if (!res.ok) throw new Error("Delete failed");
|
|
7221
|
-
|
|
8063
|
+
import_sonner6.toast.success("Deleted");
|
|
8064
|
+
setDeleteTarget(null);
|
|
7222
8065
|
load();
|
|
7223
8066
|
} catch {
|
|
7224
|
-
|
|
8067
|
+
import_sonner6.toast.error("Delete failed");
|
|
7225
8068
|
}
|
|
7226
8069
|
};
|
|
7227
8070
|
const handleSaveEdit = async () => {
|
|
7228
8071
|
if (!editModal) return;
|
|
7229
8072
|
try {
|
|
8073
|
+
const isFolder = editModal.kind === "folder";
|
|
8074
|
+
const body = isFolder ? { filename: editFilename.trim() || editModal.filename } : { alt: editAlt || null, isPublic: editPublic };
|
|
8075
|
+
if (isFolder && !body.filename) {
|
|
8076
|
+
import_sonner6.toast.error("Name required");
|
|
8077
|
+
return;
|
|
8078
|
+
}
|
|
7230
8079
|
const res = await fetch(`/api/media/${editModal.id}`, {
|
|
7231
8080
|
method: "PUT",
|
|
7232
8081
|
headers: { "Content-Type": "application/json" },
|
|
7233
|
-
body: JSON.stringify(
|
|
8082
|
+
body: JSON.stringify(body)
|
|
7234
8083
|
});
|
|
7235
8084
|
if (!res.ok) throw new Error("Update failed");
|
|
7236
|
-
|
|
8085
|
+
import_sonner6.toast.success("Updated");
|
|
7237
8086
|
setEditModal(null);
|
|
7238
8087
|
load();
|
|
7239
8088
|
} catch {
|
|
7240
|
-
|
|
8089
|
+
import_sonner6.toast.error("Update failed");
|
|
7241
8090
|
}
|
|
7242
8091
|
};
|
|
7243
8092
|
const copyUrl = (url) => {
|
|
7244
8093
|
const full = url.startsWith("http") ? url : `${typeof window !== "undefined" ? window.location.origin : ""}${url}`;
|
|
7245
8094
|
navigator.clipboard.writeText(full);
|
|
7246
|
-
|
|
8095
|
+
import_sonner6.toast.success("URL copied");
|
|
7247
8096
|
};
|
|
7248
8097
|
const openEdit = (item) => {
|
|
7249
8098
|
setEditModal(item);
|
|
7250
8099
|
setEditAlt(item.alt || "");
|
|
7251
8100
|
setEditPublic(item.isPublic);
|
|
8101
|
+
setEditFilename(item.filename);
|
|
7252
8102
|
};
|
|
7253
|
-
const
|
|
7254
|
-
const
|
|
8103
|
+
const isImage = (mime, kind, filename) => kind !== "folder" && getTypeCategory(mime, kind, filename) === "image";
|
|
8104
|
+
const listViewData = import_react38.default.useMemo(() => {
|
|
8105
|
+
const sorted = [...data];
|
|
8106
|
+
const byDateDesc = (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
8107
|
+
const folders = sorted.filter((x) => x.kind === "folder").sort(byDateDesc);
|
|
8108
|
+
const files = sorted.filter((x) => x.kind !== "folder").sort(byDateDesc);
|
|
8109
|
+
return [...folders, ...files];
|
|
8110
|
+
}, [data]);
|
|
7255
8111
|
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "min-w-0 rounded-lg bg-white shadow-md", children: [
|
|
7256
8112
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "bg-gray-800 border-b border-gray-700 px-4 py-2.5 rounded-t-lg", children: [
|
|
7257
8113
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("h1", { className: "text-base font-semibold text-white", children: "Media Library" }),
|
|
7258
|
-
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("p", { className: "text-xs text-gray-300 mt-0.5", children: "
|
|
8114
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("p", { className: "text-xs text-gray-300 mt-0.5", children: "Folders and files (Google Drive\u2013style). Storage mirrors the folder tree under uploads/." })
|
|
7259
8115
|
] }),
|
|
7260
8116
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "min-w-0 p-4 space-y-3", children: [
|
|
8117
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex flex-wrap items-center gap-2 text-sm text-gray-700", children: [
|
|
8118
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "font-medium text-gray-600", children: "Location:" }),
|
|
8119
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("button", { type: "button", className: "text-blue-600 hover:underline", onClick: () => goCrumb(-1), children: "Media" }),
|
|
8120
|
+
crumb.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { className: "flex items-center gap-1", children: [
|
|
8121
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "text-gray-400", children: "/" }),
|
|
8122
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("button", { type: "button", className: "text-blue-600 hover:underline", onClick: () => goCrumb(i), children: c.name })
|
|
8123
|
+
] }, `${c.id}-${i}`))
|
|
8124
|
+
] }),
|
|
7261
8125
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
7262
8126
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
7263
8127
|
"input",
|
|
@@ -7267,7 +8131,7 @@ function MediaLibraryPage() {
|
|
|
7267
8131
|
className: "hidden",
|
|
7268
8132
|
onChange: handleUpload,
|
|
7269
8133
|
disabled: uploading,
|
|
7270
|
-
accept: "image/*,video/*,audio/*,.pdf,.doc,.docx"
|
|
8134
|
+
accept: "image/*,video/*,audio/*,.pdf,.doc,.docx,.zip"
|
|
7271
8135
|
}
|
|
7272
8136
|
),
|
|
7273
8137
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
@@ -7283,11 +8147,15 @@ function MediaLibraryPage() {
|
|
|
7283
8147
|
]
|
|
7284
8148
|
}
|
|
7285
8149
|
),
|
|
8150
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(Button, { type: "button", variant: "secondary", className: "flex items-center gap-2", onClick: () => setNewFolderOpen(true), children: [
|
|
8151
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.FolderPlus, { className: "h-4 w-4" }),
|
|
8152
|
+
"New folder"
|
|
8153
|
+
] }),
|
|
7286
8154
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex items-center gap-2 flex-1 min-w-[200px]", children: [
|
|
7287
8155
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
7288
8156
|
Input,
|
|
7289
8157
|
{
|
|
7290
|
-
placeholder: "Search by
|
|
8158
|
+
placeholder: "Search by name...",
|
|
7291
8159
|
value: searchInput,
|
|
7292
8160
|
onChange: (e) => setSearchInput(e.target.value),
|
|
7293
8161
|
onKeyDown: (e) => e.key === "Enter" && handleSearch()
|
|
@@ -7309,61 +8177,192 @@ function MediaLibraryPage() {
|
|
|
7309
8177
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("option", { value: "application", children: "Documents" })
|
|
7310
8178
|
]
|
|
7311
8179
|
}
|
|
7312
|
-
)
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("h2", { className: "text-sm font-semibold text-gray-500 uppercase tracking-wider mb-3", children: monthLabel }),
|
|
7316
|
-
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
7317
|
-
"div",
|
|
8180
|
+
),
|
|
8181
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
8182
|
+
"select",
|
|
7318
8183
|
{
|
|
7319
|
-
className: "border rounded-
|
|
8184
|
+
className: "border rounded-md px-3 py-2 text-sm bg-white max-w-[200px]",
|
|
8185
|
+
value: `${sortField}:${sortOrder}`,
|
|
8186
|
+
onChange: (e) => {
|
|
8187
|
+
const [f, o] = e.target.value.split(":");
|
|
8188
|
+
setSortField(f);
|
|
8189
|
+
setSortOrder(o);
|
|
8190
|
+
setPage(1);
|
|
8191
|
+
},
|
|
7320
8192
|
children: [
|
|
7321
|
-
/* @__PURE__ */ (0, import_jsx_runtime53.
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
8193
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("option", { value: "filename:asc", children: "Name A\u2013Z" }),
|
|
8194
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("option", { value: "filename:desc", children: "Name Z\u2013A" }),
|
|
8195
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("option", { value: "createdAt:desc", children: "Newest first" }),
|
|
8196
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("option", { value: "createdAt:asc", children: "Oldest first" })
|
|
8197
|
+
]
|
|
8198
|
+
}
|
|
8199
|
+
),
|
|
8200
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "inline-flex rounded-md border border-gray-300 overflow-hidden bg-white", children: [
|
|
8201
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
8202
|
+
"button",
|
|
8203
|
+
{
|
|
8204
|
+
type: "button",
|
|
8205
|
+
className: `px-2.5 py-2 text-xs inline-flex items-center gap-1 ${viewMode === "grid" ? "bg-gray-100 text-gray-900" : "text-gray-600"}`,
|
|
8206
|
+
onClick: () => setViewMode("grid"),
|
|
8207
|
+
title: "Grid view",
|
|
8208
|
+
children: [
|
|
8209
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.LayoutGrid, { className: "h-4 w-4" }),
|
|
8210
|
+
"Grid"
|
|
8211
|
+
]
|
|
8212
|
+
}
|
|
8213
|
+
),
|
|
8214
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
8215
|
+
"button",
|
|
8216
|
+
{
|
|
8217
|
+
type: "button",
|
|
8218
|
+
className: `px-2.5 py-2 text-xs inline-flex items-center gap-1 border-l border-gray-300 ${viewMode === "list" ? "bg-gray-100 text-gray-900" : "text-gray-600"}`,
|
|
8219
|
+
onClick: () => setViewMode("list"),
|
|
8220
|
+
title: "List view",
|
|
8221
|
+
children: [
|
|
8222
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.List, { className: "h-4 w-4" }),
|
|
8223
|
+
"List"
|
|
8224
|
+
]
|
|
8225
|
+
}
|
|
8226
|
+
)
|
|
8227
|
+
] })
|
|
8228
|
+
] }),
|
|
8229
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "flex justify-center py-12", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "animate-spin rounded-full h-8 w-8 border-2 border-gray-300 border-t-gray-600" }) }) : data.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "text-center py-12 text-gray-500", children: "This folder is empty. Create a folder or upload a file." }) : viewMode === "grid" ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4", children: data.map((item) => {
|
|
8230
|
+
const isF = item.kind === "folder";
|
|
8231
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
8232
|
+
"div",
|
|
8233
|
+
{
|
|
8234
|
+
className: "border rounded-lg overflow-hidden bg-gray-50 hover:bg-gray-100 transition-colors group",
|
|
8235
|
+
children: [
|
|
8236
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
8237
|
+
"button",
|
|
8238
|
+
{
|
|
8239
|
+
type: "button",
|
|
8240
|
+
className: "w-full aspect-square flex items-center justify-center bg-gray-200 relative cursor-pointer",
|
|
8241
|
+
onClick: () => isF && enterFolder(item),
|
|
8242
|
+
children: [
|
|
8243
|
+
isImage(item.mimeType, item.kind, item.filename) ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8244
|
+
"img",
|
|
8245
|
+
{
|
|
8246
|
+
src: item.url,
|
|
8247
|
+
alt: item.alt || item.filename,
|
|
8248
|
+
className: "w-full h-full object-cover"
|
|
8249
|
+
}
|
|
8250
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(TypeIcon, { mimeType: item.mimeType, kind: item.kind, filename: item.filename }),
|
|
8251
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-colors flex items-center justify-center gap-1 opacity-0 group-hover:opacity-100 flex-wrap", children: [
|
|
8252
|
+
!isF && isZipMedia(item.mimeType, item.filename) ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8253
|
+
Button,
|
|
8254
|
+
{
|
|
8255
|
+
size: "icon",
|
|
8256
|
+
variant: "outline",
|
|
8257
|
+
className: "h-8 w-8",
|
|
8258
|
+
title: "Extract zip into this folder",
|
|
8259
|
+
disabled: extractingId === item.id,
|
|
8260
|
+
onClick: (e) => {
|
|
8261
|
+
e.stopPropagation();
|
|
8262
|
+
handleExtractZip(item.id);
|
|
8263
|
+
},
|
|
8264
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Archive, { className: "h-3 w-3" })
|
|
8265
|
+
}
|
|
8266
|
+
) : null,
|
|
8267
|
+
!isF && item.url ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8268
|
+
Button,
|
|
8269
|
+
{
|
|
8270
|
+
size: "icon",
|
|
8271
|
+
variant: "outline",
|
|
8272
|
+
className: "h-8 w-8",
|
|
8273
|
+
onClick: (e) => {
|
|
8274
|
+
e.stopPropagation();
|
|
8275
|
+
copyUrl(item.url);
|
|
8276
|
+
},
|
|
8277
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Copy, { className: "h-3 w-3" })
|
|
8278
|
+
}
|
|
8279
|
+
) : null,
|
|
8280
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8281
|
+
Button,
|
|
8282
|
+
{
|
|
8283
|
+
size: "icon",
|
|
8284
|
+
variant: "outline",
|
|
8285
|
+
className: "h-8 w-8",
|
|
8286
|
+
onClick: (e) => {
|
|
8287
|
+
e.stopPropagation();
|
|
8288
|
+
openEdit(item);
|
|
8289
|
+
},
|
|
8290
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Edit2, { className: "h-3 w-3" })
|
|
8291
|
+
}
|
|
8292
|
+
),
|
|
8293
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8294
|
+
Button,
|
|
8295
|
+
{
|
|
8296
|
+
size: "icon",
|
|
8297
|
+
variant: "outline",
|
|
8298
|
+
className: "h-8 w-8 border-red-300 text-red-600 hover:text-red-700",
|
|
8299
|
+
onClick: (e) => {
|
|
8300
|
+
e.stopPropagation();
|
|
8301
|
+
setDeleteTarget(item);
|
|
8302
|
+
},
|
|
8303
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Trash2, { className: "h-3 w-3" })
|
|
8304
|
+
}
|
|
8305
|
+
)
|
|
8306
|
+
] })
|
|
8307
|
+
]
|
|
8308
|
+
}
|
|
8309
|
+
),
|
|
7354
8310
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "p-2", children: [
|
|
7355
|
-
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("p", { className: "text-xs font-medium text-gray-900 truncate", title: item.filename, children: item.filename }),
|
|
8311
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("p", { className: "text-xs font-medium text-gray-900 truncate text-left", title: item.filename, children: item.filename }),
|
|
7356
8312
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex items-center justify-between mt-1", children: [
|
|
7357
8313
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "text-xs text-gray-500", children: formatDate(item.createdAt) }),
|
|
7358
|
-
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "text-xs px-1.5 py-0.5 rounded bg-gray-200 text-gray-600", children: getTypeLabel(item.mimeType) })
|
|
8314
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "text-xs px-1.5 py-0.5 rounded bg-gray-200 text-gray-600", children: getTypeLabel(item.mimeType, item.kind, item.filename) })
|
|
7359
8315
|
] }),
|
|
7360
|
-
item.isPublic && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "text-xs text-green-600 mt-0.5 block", children: "Public" })
|
|
8316
|
+
item.isPublic && !isF && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "text-xs text-green-600 mt-0.5 block", children: "Public" }),
|
|
8317
|
+
isF && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8318
|
+
"button",
|
|
8319
|
+
{
|
|
8320
|
+
type: "button",
|
|
8321
|
+
className: "text-xs text-blue-600 mt-1 hover:underline",
|
|
8322
|
+
onClick: () => enterFolder(item),
|
|
8323
|
+
children: "Open"
|
|
8324
|
+
}
|
|
8325
|
+
)
|
|
7361
8326
|
] })
|
|
7362
8327
|
]
|
|
7363
8328
|
},
|
|
7364
8329
|
item.id
|
|
7365
|
-
)
|
|
7366
|
-
|
|
8330
|
+
);
|
|
8331
|
+
}) }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "overflow-x-auto rounded-md border border-gray-200 bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("table", { className: "w-full text-sm", children: [
|
|
8332
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("tr", { className: "border-b bg-gray-50", children: [
|
|
8333
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("th", { className: "p-3 text-left font-medium", children: "Name" }),
|
|
8334
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("th", { className: "p-3 text-left font-medium", children: "Type" }),
|
|
8335
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("th", { className: "p-3 text-left font-medium", children: "Created" }),
|
|
8336
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("th", { className: "p-3 text-left font-medium", children: "Size" }),
|
|
8337
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("th", { className: "p-3 text-right font-medium", children: "Actions" })
|
|
8338
|
+
] }) }),
|
|
8339
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("tbody", { children: listViewData.map((item) => {
|
|
8340
|
+
const isF = item.kind === "folder";
|
|
8341
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("tr", { className: "border-b border-gray-100 hover:bg-gray-50", children: [
|
|
8342
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("td", { className: "p-3", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
8343
|
+
"button",
|
|
8344
|
+
{
|
|
8345
|
+
type: "button",
|
|
8346
|
+
className: `inline-flex items-center gap-2 ${isF ? "text-blue-600 hover:underline" : "text-gray-900"}`,
|
|
8347
|
+
onClick: () => isF && enterFolder(item),
|
|
8348
|
+
children: [
|
|
8349
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(TypeIcon, { mimeType: item.mimeType, kind: item.kind, filename: item.filename }),
|
|
8350
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "truncate max-w-[260px]", title: item.filename, children: item.filename })
|
|
8351
|
+
]
|
|
8352
|
+
}
|
|
8353
|
+
) }),
|
|
8354
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("td", { className: "p-3 text-gray-600", children: getTypeLabel(item.mimeType, item.kind, item.filename) }),
|
|
8355
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("td", { className: "p-3 text-gray-600", children: formatDate(item.createdAt) }),
|
|
8356
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("td", { className: "p-3 text-gray-600", children: isF ? "\u2014" : `${Math.round((item.size || 0) / 1024)} KB` }),
|
|
8357
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("td", { className: "p-3", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex items-center justify-end gap-1", children: [
|
|
8358
|
+
!isF && isZipMedia(item.mimeType, item.filename) && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Button, { size: "icon", variant: "outline", className: "h-8 w-8", onClick: () => handleExtractZip(item.id), disabled: extractingId === item.id, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Archive, { className: "h-3 w-3" }) }),
|
|
8359
|
+
!isF && item.url && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Button, { size: "icon", variant: "outline", className: "h-8 w-8", onClick: () => copyUrl(item.url), children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Copy, { className: "h-3 w-3" }) }),
|
|
8360
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Button, { size: "icon", variant: "outline", className: "h-8 w-8", onClick: () => openEdit(item), children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Edit2, { className: "h-3 w-3" }) }),
|
|
8361
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Button, { size: "icon", variant: "outline", className: "h-8 w-8 border-red-300 text-red-600 hover:text-red-700", onClick: () => setDeleteTarget(item), children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react29.Trash2, { className: "h-3 w-3" }) })
|
|
8362
|
+
] }) })
|
|
8363
|
+
] }, item.id);
|
|
8364
|
+
}) })
|
|
8365
|
+
] }) }),
|
|
7367
8366
|
totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Pagination, { className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(PaginationContent, { children: [
|
|
7368
8367
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(PaginationItem, { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
7369
8368
|
PaginationPrevious,
|
|
@@ -7385,9 +8384,28 @@ function MediaLibraryPage() {
|
|
|
7385
8384
|
) })
|
|
7386
8385
|
] }) })
|
|
7387
8386
|
] }),
|
|
8387
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Dialog, { open: newFolderOpen, onOpenChange: setNewFolderOpen, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(DialogContent, { children: [
|
|
8388
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(DialogTitle, { children: "New folder" }) }),
|
|
8389
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8390
|
+
Input,
|
|
8391
|
+
{
|
|
8392
|
+
placeholder: "Folder name",
|
|
8393
|
+
value: newFolderName,
|
|
8394
|
+
onChange: (e) => setNewFolderName(e.target.value),
|
|
8395
|
+
onKeyDown: (e) => e.key === "Enter" && createFolder()
|
|
8396
|
+
}
|
|
8397
|
+
),
|
|
8398
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(DialogFooter, { children: [
|
|
8399
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Button, { variant: "outline", onClick: () => setNewFolderOpen(false), children: "Cancel" }),
|
|
8400
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Button, { onClick: createFolder, children: "Create" })
|
|
8401
|
+
] })
|
|
8402
|
+
] }) }),
|
|
7388
8403
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Dialog, { open: !!editModal, onOpenChange: (open) => !open && setEditModal(null), children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(DialogContent, { children: [
|
|
7389
|
-
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(DialogTitle, { children: "Edit media" }) }),
|
|
7390
|
-
editModal && /* @__PURE__ */ (0, import_jsx_runtime53.
|
|
8404
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(DialogTitle, { children: editModal?.kind === "folder" ? "Rename folder" : "Edit media" }) }),
|
|
8405
|
+
editModal && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "space-y-4 py-2", children: editModal.kind === "folder" ? /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { children: [
|
|
8406
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("label", { className: "text-sm font-medium", children: "Name" }),
|
|
8407
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Input, { value: editFilename, onChange: (e) => setEditFilename(e.target.value), className: "mt-1" })
|
|
8408
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
|
|
7391
8409
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { children: [
|
|
7392
8410
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("label", { className: "text-sm font-medium", children: "Alt text" }),
|
|
7393
8411
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
@@ -7412,20 +8430,37 @@ function MediaLibraryPage() {
|
|
|
7412
8430
|
),
|
|
7413
8431
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("label", { htmlFor: "edit-public", className: "text-sm", children: "Public (visible on site)" })
|
|
7414
8432
|
] })
|
|
7415
|
-
] }),
|
|
8433
|
+
] }) }),
|
|
7416
8434
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(DialogFooter, { children: [
|
|
7417
8435
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Button, { variant: "outline", onClick: () => setEditModal(null), children: "Cancel" }),
|
|
7418
8436
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Button, { onClick: handleSaveEdit, children: "Save" })
|
|
7419
8437
|
] })
|
|
8438
|
+
] }) }),
|
|
8439
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Dialog, { open: !!deleteTarget, onOpenChange: (open) => !open && setDeleteTarget(null), children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(DialogContent, { children: [
|
|
8440
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(DialogTitle, { children: "Confirm Delete" }) }),
|
|
8441
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("p", { className: "text-sm text-gray-600", children: deleteTarget?.kind === "folder" ? "Delete this folder and everything inside it?" : "Delete this file?" }),
|
|
8442
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(DialogFooter, { children: [
|
|
8443
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Button, { variant: "outline", onClick: () => setDeleteTarget(null), children: "Cancel" }),
|
|
8444
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8445
|
+
Button,
|
|
8446
|
+
{
|
|
8447
|
+
variant: "destructive",
|
|
8448
|
+
onClick: () => {
|
|
8449
|
+
if (deleteTarget) handleDelete(deleteTarget);
|
|
8450
|
+
},
|
|
8451
|
+
children: "Delete"
|
|
8452
|
+
}
|
|
8453
|
+
)
|
|
8454
|
+
] })
|
|
7420
8455
|
] }) })
|
|
7421
8456
|
] });
|
|
7422
8457
|
}
|
|
7423
8458
|
|
|
7424
8459
|
// src/admin/pages/PageBuilderPage.tsx
|
|
7425
|
-
var
|
|
8460
|
+
var import_react39 = require("react");
|
|
7426
8461
|
var import_core = require("@craftjs/core");
|
|
7427
8462
|
var import_lucide_react30 = require("lucide-react");
|
|
7428
|
-
var
|
|
8463
|
+
var import_navigation17 = require("next/navigation");
|
|
7429
8464
|
|
|
7430
8465
|
// src/theme/registry.ts
|
|
7431
8466
|
function buildResolver(theme) {
|
|
@@ -7450,7 +8485,7 @@ function getCatalog(theme) {
|
|
|
7450
8485
|
|
|
7451
8486
|
// src/admin/pages/PageBuilderPage.tsx
|
|
7452
8487
|
var LucideIcons = __toESM(require("lucide-react"), 1);
|
|
7453
|
-
var
|
|
8488
|
+
var import_react40 = __toESM(require("react"), 1);
|
|
7454
8489
|
var import_jsx_runtime54 = require("react/jsx-runtime");
|
|
7455
8490
|
function createSelectable(Comp) {
|
|
7456
8491
|
function SelectableWrapper(props) {
|
|
@@ -7495,16 +8530,16 @@ function DraggableCatalogItem({
|
|
|
7495
8530
|
Icon: Icon2,
|
|
7496
8531
|
description
|
|
7497
8532
|
}) {
|
|
7498
|
-
const elRef = (0,
|
|
7499
|
-
(0,
|
|
8533
|
+
const elRef = (0, import_react39.useRef)(null);
|
|
8534
|
+
(0, import_react39.useEffect)(() => {
|
|
7500
8535
|
if (!elRef.current) return;
|
|
7501
8536
|
if (meta.canContainChildren) {
|
|
7502
8537
|
connectors.create(
|
|
7503
8538
|
elRef.current,
|
|
7504
|
-
|
|
8539
|
+
import_react40.default.createElement(import_core.Element, { is: Comp, canvas: true, ...meta.defaultProps })
|
|
7505
8540
|
);
|
|
7506
8541
|
} else {
|
|
7507
|
-
connectors.create(elRef.current,
|
|
8542
|
+
connectors.create(elRef.current, import_react40.default.createElement(Comp, meta.defaultProps));
|
|
7508
8543
|
}
|
|
7509
8544
|
}, [connectors, Comp, meta.defaultProps, meta.name, meta.canContainChildren]);
|
|
7510
8545
|
return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
|
|
@@ -7523,7 +8558,7 @@ function DraggableCatalogItem({
|
|
|
7523
8558
|
function ComponentPickerContent({ theme, resolver }) {
|
|
7524
8559
|
const { connectors } = (0, import_core.useEditor)();
|
|
7525
8560
|
const catalog = getCatalog(theme);
|
|
7526
|
-
const [collapsed, setCollapsed] = (0,
|
|
8561
|
+
const [collapsed, setCollapsed] = (0, import_react39.useState)({});
|
|
7527
8562
|
const toggleCategory = (cat) => {
|
|
7528
8563
|
setCollapsed((prev) => ({ ...prev, [cat]: !prev[cat] }));
|
|
7529
8564
|
};
|
|
@@ -7728,7 +8763,7 @@ function LayersPanelContent() {
|
|
|
7728
8763
|
nodes: state.nodes,
|
|
7729
8764
|
selectedSet: state.events.selected
|
|
7730
8765
|
}));
|
|
7731
|
-
const [expanded, setExpanded] = (0,
|
|
8766
|
+
const [expanded, setExpanded] = (0, import_react39.useState)({});
|
|
7732
8767
|
const nodeMap = nodes || {};
|
|
7733
8768
|
const rootId = (() => {
|
|
7734
8769
|
const ids = Object.keys(nodeMap);
|
|
@@ -7850,10 +8885,10 @@ function CollapsibleSection({
|
|
|
7850
8885
|
] });
|
|
7851
8886
|
}
|
|
7852
8887
|
function RightSidebar({ theme, resolver, activeTab, setActiveTab, seoProps }) {
|
|
7853
|
-
const [componentsOpen, setComponentsOpen] = (0,
|
|
7854
|
-
const [propertiesOpen, setPropertiesOpen] = (0,
|
|
7855
|
-
const [layoutOpen, setLayoutOpen] = (0,
|
|
7856
|
-
const [layersOpen, setLayersOpen] = (0,
|
|
8888
|
+
const [componentsOpen, setComponentsOpen] = (0, import_react39.useState)(true);
|
|
8889
|
+
const [propertiesOpen, setPropertiesOpen] = (0, import_react39.useState)(true);
|
|
8890
|
+
const [layoutOpen, setLayoutOpen] = (0, import_react39.useState)(true);
|
|
8891
|
+
const [layersOpen, setLayersOpen] = (0, import_react39.useState)(true);
|
|
7857
8892
|
return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex flex-col flex-1 overflow-hidden", children: [
|
|
7858
8893
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { className: "flex border-b flex-shrink-0", children: ["designer", "seo"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
7859
8894
|
"button",
|
|
@@ -7921,7 +8956,7 @@ function SaveButton({
|
|
|
7921
8956
|
className
|
|
7922
8957
|
}) {
|
|
7923
8958
|
const { query } = (0, import_core.useEditor)();
|
|
7924
|
-
const [saving, setSaving] = (0,
|
|
8959
|
+
const [saving, setSaving] = (0, import_react39.useState)(false);
|
|
7925
8960
|
const handleSave = async () => {
|
|
7926
8961
|
setSaving(true);
|
|
7927
8962
|
try {
|
|
@@ -7993,22 +9028,22 @@ function SaveButton({
|
|
|
7993
9028
|
);
|
|
7994
9029
|
}
|
|
7995
9030
|
function PageBuilderPage({ pageId }) {
|
|
7996
|
-
const { theme } = (0,
|
|
7997
|
-
const router = (0,
|
|
7998
|
-
const [title, setTitle] = (0,
|
|
7999
|
-
const [slug, setSlug] = (0,
|
|
8000
|
-
const [published, setPublished] = (0,
|
|
8001
|
-
const [initialContent, setInitialContent] = (0,
|
|
8002
|
-
const [loading, setLoading] = (0,
|
|
8003
|
-
const [rightTab, setRightTab] = (0,
|
|
8004
|
-
const [seoTitle, setSeoTitle] = (0,
|
|
8005
|
-
const [seoDescription, setSeoDescription] = (0,
|
|
8006
|
-
const [seoKeywords, setSeoKeywords] = (0,
|
|
8007
|
-
const [seoOgTitle, setSeoOgTitle] = (0,
|
|
8008
|
-
const [seoOgDescription, setSeoOgDescription] = (0,
|
|
8009
|
-
const [seoOgImage, setSeoOgImage] = (0,
|
|
8010
|
-
const [pageSeoId, setPageSeoId] = (0,
|
|
8011
|
-
(0,
|
|
9031
|
+
const { theme } = (0, import_react39.useContext)(AdminConfigContext);
|
|
9032
|
+
const router = (0, import_navigation17.useRouter)();
|
|
9033
|
+
const [title, setTitle] = (0, import_react39.useState)("");
|
|
9034
|
+
const [slug, setSlug] = (0, import_react39.useState)("");
|
|
9035
|
+
const [published, setPublished] = (0, import_react39.useState)(false);
|
|
9036
|
+
const [initialContent, setInitialContent] = (0, import_react39.useState)(null);
|
|
9037
|
+
const [loading, setLoading] = (0, import_react39.useState)(!!pageId);
|
|
9038
|
+
const [rightTab, setRightTab] = (0, import_react39.useState)("designer");
|
|
9039
|
+
const [seoTitle, setSeoTitle] = (0, import_react39.useState)("");
|
|
9040
|
+
const [seoDescription, setSeoDescription] = (0, import_react39.useState)("");
|
|
9041
|
+
const [seoKeywords, setSeoKeywords] = (0, import_react39.useState)("");
|
|
9042
|
+
const [seoOgTitle, setSeoOgTitle] = (0, import_react39.useState)("");
|
|
9043
|
+
const [seoOgDescription, setSeoOgDescription] = (0, import_react39.useState)("");
|
|
9044
|
+
const [seoOgImage, setSeoOgImage] = (0, import_react39.useState)("");
|
|
9045
|
+
const [pageSeoId, setPageSeoId] = (0, import_react39.useState)(null);
|
|
9046
|
+
(0, import_react39.useEffect)(() => {
|
|
8012
9047
|
if (!pageId) {
|
|
8013
9048
|
setInitialContent(null);
|
|
8014
9049
|
setPageSeoId(null);
|
|
@@ -8037,7 +9072,7 @@ function PageBuilderPage({ pageId }) {
|
|
|
8037
9072
|
}
|
|
8038
9073
|
}).finally(() => setLoading(false));
|
|
8039
9074
|
}, [pageId]);
|
|
8040
|
-
(0,
|
|
9075
|
+
(0, import_react39.useEffect)(() => {
|
|
8041
9076
|
if (title && !slug) {
|
|
8042
9077
|
setSlug(
|
|
8043
9078
|
title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "")
|
|
@@ -8082,19 +9117,49 @@ function PageBuilderPage({ pageId }) {
|
|
|
8082
9117
|
onSaved: (id) => {
|
|
8083
9118
|
if (!pageId) router.replace(`/admin/pages/${id}`);
|
|
8084
9119
|
},
|
|
8085
|
-
className: "
|
|
9120
|
+
className: "md:hidden h-8 w-8 p-0 bg-white text-gray-800 hover:bg-gray-100 border-0",
|
|
9121
|
+
children: [
|
|
9122
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_lucide_react30.Save, { className: "h-4 w-4" }),
|
|
9123
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "sr-only", children: "Save" })
|
|
9124
|
+
]
|
|
9125
|
+
}
|
|
9126
|
+
),
|
|
9127
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
|
|
9128
|
+
SaveButton,
|
|
9129
|
+
{
|
|
9130
|
+
pageId,
|
|
9131
|
+
existingSeoId: pageSeoId,
|
|
9132
|
+
onSeoIdChange: setPageSeoId,
|
|
9133
|
+
pageData: { title, slug, published, seoTitle, seoDescription, seoKeywords, seoOgTitle, seoOgDescription, seoOgImage },
|
|
9134
|
+
onSaved: (id) => {
|
|
9135
|
+
if (!pageId) router.replace(`/admin/pages/${id}`);
|
|
9136
|
+
},
|
|
9137
|
+
className: "hidden md:inline-flex items-center bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
8086
9138
|
children: [
|
|
8087
9139
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_lucide_react30.Save, { className: "h-4 w-4 mr-1" }),
|
|
8088
9140
|
" Save"
|
|
8089
9141
|
]
|
|
8090
9142
|
}
|
|
8091
9143
|
),
|
|
9144
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
|
|
9145
|
+
Button,
|
|
9146
|
+
{
|
|
9147
|
+
variant: "outline",
|
|
9148
|
+
size: "icon",
|
|
9149
|
+
className: "md:hidden h-8 w-8 bg-white text-gray-800 hover:bg-gray-100 border-0",
|
|
9150
|
+
onClick: () => router.push("/admin/pages"),
|
|
9151
|
+
children: [
|
|
9152
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_lucide_react30.X, { className: "h-4 w-4" }),
|
|
9153
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "sr-only", children: "Close" })
|
|
9154
|
+
]
|
|
9155
|
+
}
|
|
9156
|
+
),
|
|
8092
9157
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
|
|
8093
9158
|
Button,
|
|
8094
9159
|
{
|
|
8095
9160
|
variant: "outline",
|
|
8096
9161
|
size: "sm",
|
|
8097
|
-
className: "flex items-center bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
9162
|
+
className: "hidden md:inline-flex items-center bg-white text-gray-800 hover:bg-gray-100 border-0 text-xs",
|
|
8098
9163
|
onClick: () => router.push("/admin/pages"),
|
|
8099
9164
|
children: [
|
|
8100
9165
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_lucide_react30.X, { className: "h-4 w-4 mr-1" }),
|
|
@@ -8155,7 +9220,7 @@ function PageBuilderPage({ pageId }) {
|
|
|
8155
9220
|
}
|
|
8156
9221
|
|
|
8157
9222
|
// src/admin/pages/PluginsPage.tsx
|
|
8158
|
-
var
|
|
9223
|
+
var import_react41 = require("react");
|
|
8159
9224
|
var import_lucide_react32 = require("lucide-react");
|
|
8160
9225
|
|
|
8161
9226
|
// src/lib/email-recipients.ts
|
|
@@ -8203,7 +9268,7 @@ var Checkbox = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__
|
|
|
8203
9268
|
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
8204
9269
|
|
|
8205
9270
|
// src/admin/pages/PluginsPage.tsx
|
|
8206
|
-
var
|
|
9271
|
+
var import_sonner7 = require("sonner");
|
|
8207
9272
|
var import_jsx_runtime56 = require("react/jsx-runtime");
|
|
8208
9273
|
function normalizeChatMode(raw) {
|
|
8209
9274
|
if (raw === "external" || raw === "llm") return raw;
|
|
@@ -8269,7 +9334,7 @@ function EmailRecipientTags({
|
|
|
8269
9334
|
onChange,
|
|
8270
9335
|
placeholder
|
|
8271
9336
|
}) {
|
|
8272
|
-
const [draft, setDraft] = (0,
|
|
9337
|
+
const [draft, setDraft] = (0, import_react41.useState)("");
|
|
8273
9338
|
const commitDraft = () => {
|
|
8274
9339
|
const parts = splitInputToEmails(draft);
|
|
8275
9340
|
if (!parts.length) return;
|
|
@@ -8345,36 +9410,36 @@ function PluginSettingsPanel({
|
|
|
8345
9410
|
const isEmail = settingsGroup === "email";
|
|
8346
9411
|
const isErp = settingsGroup === "erp";
|
|
8347
9412
|
const isSms = settingsGroup === "sms";
|
|
8348
|
-
const [enabled, setEnabled] = (0,
|
|
8349
|
-
const [botName, setBotName] = (0,
|
|
8350
|
-
const [icon, setIcon] = (0,
|
|
8351
|
-
const [iconImageUrl, setIconImageUrl] = (0,
|
|
8352
|
-
const [iconBackgroundColor, setIconBackgroundColor] = (0,
|
|
8353
|
-
const [headerColor, setHeaderColor] = (0,
|
|
8354
|
-
const [salesTeamEmails, setSalesTeamEmails] = (0,
|
|
8355
|
-
const [fulfilmentTeamEmails, setFulfilmentTeamEmails] = (0,
|
|
8356
|
-
const [crmEmails, setCrmEmails] = (0,
|
|
8357
|
-
const [logoUrl, setLogoUrl] = (0,
|
|
8358
|
-
const [companyName, setCompanyName] = (0,
|
|
8359
|
-
const [supportEmail, setSupportEmail] = (0,
|
|
8360
|
-
const [supportPhone, setSupportPhone] = (0,
|
|
8361
|
-
const [followUsTitle, setFollowUsTitle] = (0,
|
|
8362
|
-
const [socialLinkRows, setSocialLinkRows] = (0,
|
|
8363
|
-
const [footerDisclaimer, setFooterDisclaimer] = (0,
|
|
8364
|
-
const [chatMode, setChatMode] = (0,
|
|
8365
|
-
const [whatsappPhone, setWhatsappPhone] = (0,
|
|
8366
|
-
const [externalChatSnippet, setExternalChatSnippet] = (0,
|
|
8367
|
-
const [erpPipelineName, setErpPipelineName] = (0,
|
|
8368
|
-
const [erpPipelineStageName, setErpPipelineStageName] = (0,
|
|
8369
|
-
const [erpFormsCatalog, setErpFormsCatalog] = (0,
|
|
8370
|
-
const [erpOpportunityFormIds, setErpOpportunityFormIds] = (0,
|
|
8371
|
-
const [erpOpportunityIdsKeyPresent, setErpOpportunityIdsKeyPresent] = (0,
|
|
8372
|
-
const [smsProviderChoice, setSmsProviderChoice] = (0,
|
|
8373
|
-
const [msg91ApiMode, setMsg91ApiMode] = (0,
|
|
8374
|
-
const [smsTplItems, setSmsTplItems] = (0,
|
|
8375
|
-
const [loading, setLoading] = (0,
|
|
8376
|
-
const [saving, setSaving] = (0,
|
|
8377
|
-
(0,
|
|
9413
|
+
const [enabled, setEnabled] = (0, import_react41.useState)(true);
|
|
9414
|
+
const [botName, setBotName] = (0, import_react41.useState)("");
|
|
9415
|
+
const [icon, setIcon] = (0, import_react41.useState)("");
|
|
9416
|
+
const [iconImageUrl, setIconImageUrl] = (0, import_react41.useState)("");
|
|
9417
|
+
const [iconBackgroundColor, setIconBackgroundColor] = (0, import_react41.useState)("#6366f1");
|
|
9418
|
+
const [headerColor, setHeaderColor] = (0, import_react41.useState)("#6366f1");
|
|
9419
|
+
const [salesTeamEmails, setSalesTeamEmails] = (0, import_react41.useState)([]);
|
|
9420
|
+
const [fulfilmentTeamEmails, setFulfilmentTeamEmails] = (0, import_react41.useState)([]);
|
|
9421
|
+
const [crmEmails, setCrmEmails] = (0, import_react41.useState)([]);
|
|
9422
|
+
const [logoUrl, setLogoUrl] = (0, import_react41.useState)("");
|
|
9423
|
+
const [companyName, setCompanyName] = (0, import_react41.useState)("");
|
|
9424
|
+
const [supportEmail, setSupportEmail] = (0, import_react41.useState)("");
|
|
9425
|
+
const [supportPhone, setSupportPhone] = (0, import_react41.useState)("");
|
|
9426
|
+
const [followUsTitle, setFollowUsTitle] = (0, import_react41.useState)("Follow Us");
|
|
9427
|
+
const [socialLinkRows, setSocialLinkRows] = (0, import_react41.useState)([{ ...EMPTY_SOCIAL_ROW }]);
|
|
9428
|
+
const [footerDisclaimer, setFooterDisclaimer] = (0, import_react41.useState)("");
|
|
9429
|
+
const [chatMode, setChatMode] = (0, import_react41.useState)("whatsapp");
|
|
9430
|
+
const [whatsappPhone, setWhatsappPhone] = (0, import_react41.useState)("");
|
|
9431
|
+
const [externalChatSnippet, setExternalChatSnippet] = (0, import_react41.useState)("");
|
|
9432
|
+
const [erpPipelineName, setErpPipelineName] = (0, import_react41.useState)("");
|
|
9433
|
+
const [erpPipelineStageName, setErpPipelineStageName] = (0, import_react41.useState)("");
|
|
9434
|
+
const [erpFormsCatalog, setErpFormsCatalog] = (0, import_react41.useState)([]);
|
|
9435
|
+
const [erpOpportunityFormIds, setErpOpportunityFormIds] = (0, import_react41.useState)([]);
|
|
9436
|
+
const [erpOpportunityIdsKeyPresent, setErpOpportunityIdsKeyPresent] = (0, import_react41.useState)(false);
|
|
9437
|
+
const [smsProviderChoice, setSmsProviderChoice] = (0, import_react41.useState)("auto");
|
|
9438
|
+
const [msg91ApiMode, setMsg91ApiMode] = (0, import_react41.useState)("auto");
|
|
9439
|
+
const [smsTplItems, setSmsTplItems] = (0, import_react41.useState)([]);
|
|
9440
|
+
const [loading, setLoading] = (0, import_react41.useState)(true);
|
|
9441
|
+
const [saving, setSaving] = (0, import_react41.useState)(false);
|
|
9442
|
+
(0, import_react41.useEffect)(() => {
|
|
8378
9443
|
setLoading(true);
|
|
8379
9444
|
fetch(`/api/settings/${settingsGroup}`).then((r) => r.ok ? r.json() : {}).then(async (data) => {
|
|
8380
9445
|
setEnabled(data.enabled !== "false");
|
|
@@ -8431,7 +9496,7 @@ function PluginSettingsPanel({
|
|
|
8431
9496
|
}
|
|
8432
9497
|
}).finally(() => setLoading(false));
|
|
8433
9498
|
}, [settingsGroup, isLlm, isEmail, isErp, isSms]);
|
|
8434
|
-
(0,
|
|
9499
|
+
(0, import_react41.useEffect)(() => {
|
|
8435
9500
|
if (!isErp || loading) return;
|
|
8436
9501
|
fetch("/api/forms?limit=500&sortField=name&sortOrder=asc").then((r) => r.ok ? r.json() : { data: [] }).then((res) => {
|
|
8437
9502
|
const rows = (res.data ?? []).map((f) => ({
|
|
@@ -8515,10 +9580,10 @@ function PluginSettingsPanel({
|
|
|
8515
9580
|
});
|
|
8516
9581
|
if (!res2.ok) throw new Error();
|
|
8517
9582
|
}
|
|
8518
|
-
|
|
9583
|
+
import_sonner7.toast.success("Settings saved");
|
|
8519
9584
|
onSaved?.();
|
|
8520
9585
|
} catch {
|
|
8521
|
-
|
|
9586
|
+
import_sonner7.toast.error("Failed to save");
|
|
8522
9587
|
} finally {
|
|
8523
9588
|
setSaving(false);
|
|
8524
9589
|
}
|
|
@@ -9134,10 +10199,10 @@ function PluginListItem({
|
|
|
9134
10199
|
);
|
|
9135
10200
|
}
|
|
9136
10201
|
function PluginsPage() {
|
|
9137
|
-
const { pluginDescriptors = [] } = (0,
|
|
9138
|
-
const [selectedName, setSelectedName] = (0,
|
|
9139
|
-
const [enabledMap, setEnabledMap] = (0,
|
|
9140
|
-
(0,
|
|
10202
|
+
const { pluginDescriptors = [] } = (0, import_react41.useContext)(AdminConfigContext);
|
|
10203
|
+
const [selectedName, setSelectedName] = (0, import_react41.useState)(null);
|
|
10204
|
+
const [enabledMap, setEnabledMap] = (0, import_react41.useState)({});
|
|
10205
|
+
(0, import_react41.useEffect)(() => {
|
|
9141
10206
|
pluginDescriptors.forEach((p) => {
|
|
9142
10207
|
if (!p.settingsGroup) return;
|
|
9143
10208
|
fetch(`/api/settings/${p.settingsGroup}`).then((r) => r.ok ? r.json() : {}).then(
|
|
@@ -9198,8 +10263,8 @@ function PluginsPage() {
|
|
|
9198
10263
|
}
|
|
9199
10264
|
|
|
9200
10265
|
// src/admin/pages/BrandEditPage.tsx
|
|
9201
|
-
var
|
|
9202
|
-
var
|
|
10266
|
+
var import_react42 = require("react");
|
|
10267
|
+
var import_navigation18 = require("next/navigation");
|
|
9203
10268
|
var import_lucide_react33 = require("lucide-react");
|
|
9204
10269
|
|
|
9205
10270
|
// src/components/Admin/SeoSection.tsx
|
|
@@ -9294,20 +10359,20 @@ async function fetchSeo(seoId) {
|
|
|
9294
10359
|
var import_jsx_runtime58 = require("react/jsx-runtime");
|
|
9295
10360
|
var isCreate = (id) => id === "create";
|
|
9296
10361
|
function BrandEditPage({ brandId }) {
|
|
9297
|
-
const router = (0,
|
|
10362
|
+
const router = (0, import_navigation18.useRouter)();
|
|
9298
10363
|
const create = isCreate(brandId);
|
|
9299
|
-
const [loading, setLoading] = (0,
|
|
9300
|
-
const [saving, setSaving] = (0,
|
|
9301
|
-
const [errors, setErrors] = (0,
|
|
9302
|
-
const [name, setName] = (0,
|
|
9303
|
-
const [slug, setSlug] = (0,
|
|
9304
|
-
const [description, setDescription] = (0,
|
|
9305
|
-
const [logo, setLogo] = (0,
|
|
9306
|
-
const [active, setActive] = (0,
|
|
9307
|
-
const [sortOrder, setSortOrder] = (0,
|
|
9308
|
-
const [seoId, setSeoId] = (0,
|
|
9309
|
-
const [seo, setSeo] = (0,
|
|
9310
|
-
(0,
|
|
10364
|
+
const [loading, setLoading] = (0, import_react42.useState)(!create);
|
|
10365
|
+
const [saving, setSaving] = (0, import_react42.useState)(false);
|
|
10366
|
+
const [errors, setErrors] = (0, import_react42.useState)([]);
|
|
10367
|
+
const [name, setName] = (0, import_react42.useState)("");
|
|
10368
|
+
const [slug, setSlug] = (0, import_react42.useState)("");
|
|
10369
|
+
const [description, setDescription] = (0, import_react42.useState)("");
|
|
10370
|
+
const [logo, setLogo] = (0, import_react42.useState)("");
|
|
10371
|
+
const [active, setActive] = (0, import_react42.useState)(true);
|
|
10372
|
+
const [sortOrder, setSortOrder] = (0, import_react42.useState)(0);
|
|
10373
|
+
const [seoId, setSeoId] = (0, import_react42.useState)(null);
|
|
10374
|
+
const [seo, setSeo] = (0, import_react42.useState)({ seoTitle: "", seoDescription: "", seoKeywords: "", seoOgTitle: "", seoOgDescription: "", seoOgImage: "" });
|
|
10375
|
+
(0, import_react42.useEffect)(() => {
|
|
9311
10376
|
if (create) return;
|
|
9312
10377
|
let cancelled = false;
|
|
9313
10378
|
(async () => {
|
|
@@ -9387,8 +10452,8 @@ function BrandEditPage({ brandId }) {
|
|
|
9387
10452
|
subtitle: create ? "Create a new brand" : "Update brand details",
|
|
9388
10453
|
closeHref: "/admin/brands",
|
|
9389
10454
|
menuItems: [
|
|
9390
|
-
{ label: saving ? "Saving..." : "Save", onClick: handleSave },
|
|
9391
|
-
{ label: active ? "Deactivate" : "Activate", onClick: () => setActive(!active) }
|
|
10455
|
+
{ label: saving ? "Saving..." : "Save", icon: import_lucide_react33.Save, onClick: handleSave },
|
|
10456
|
+
{ label: active ? "Deactivate" : "Activate", icon: import_lucide_react33.Power, onClick: () => setActive(!active) }
|
|
9392
10457
|
]
|
|
9393
10458
|
}
|
|
9394
10459
|
),
|
|
@@ -9496,12 +10561,12 @@ function BrandEditPage({ brandId }) {
|
|
|
9496
10561
|
}
|
|
9497
10562
|
|
|
9498
10563
|
// src/admin/pages/ProductEditPage.tsx
|
|
9499
|
-
var
|
|
9500
|
-
var
|
|
10564
|
+
var import_react44 = require("react");
|
|
10565
|
+
var import_navigation19 = require("next/navigation");
|
|
9501
10566
|
var import_lucide_react34 = require("lucide-react");
|
|
9502
10567
|
|
|
9503
10568
|
// src/components/Admin/AttributeFacetNameInput.tsx
|
|
9504
|
-
var
|
|
10569
|
+
var import_react43 = require("react");
|
|
9505
10570
|
var import_jsx_runtime59 = require("react/jsx-runtime");
|
|
9506
10571
|
function slugFromName(name) {
|
|
9507
10572
|
const s = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -9512,13 +10577,16 @@ function AttributeFacetNameInput({
|
|
|
9512
10577
|
onChange,
|
|
9513
10578
|
inputClassName
|
|
9514
10579
|
}) {
|
|
9515
|
-
const [draft, setDraft] = (0,
|
|
9516
|
-
const [open, setOpen] = (0,
|
|
9517
|
-
const [list, setList] = (0,
|
|
9518
|
-
(0,
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
10580
|
+
const [draft, setDraft] = (0, import_react43.useState)(value);
|
|
10581
|
+
const [open, setOpen] = (0, import_react43.useState)(false);
|
|
10582
|
+
const [list, setList] = (0, import_react43.useState)([]);
|
|
10583
|
+
const isFocusedRef = (0, import_react43.useRef)(false);
|
|
10584
|
+
(0, import_react43.useEffect)(() => {
|
|
10585
|
+
if (!isFocusedRef.current && value !== draft) {
|
|
10586
|
+
setDraft(value);
|
|
10587
|
+
}
|
|
10588
|
+
}, [value, draft]);
|
|
10589
|
+
(0, import_react43.useEffect)(() => {
|
|
9522
10590
|
const q = draft.trim();
|
|
9523
10591
|
if (!q) {
|
|
9524
10592
|
setList([]);
|
|
@@ -9580,8 +10648,12 @@ function AttributeFacetNameInput({
|
|
|
9580
10648
|
setDraft(e.target.value);
|
|
9581
10649
|
setOpen(true);
|
|
9582
10650
|
},
|
|
9583
|
-
onFocus: () =>
|
|
10651
|
+
onFocus: () => {
|
|
10652
|
+
setOpen(true);
|
|
10653
|
+
isFocusedRef.current = true;
|
|
10654
|
+
},
|
|
9584
10655
|
onBlur: () => setTimeout(() => {
|
|
10656
|
+
isFocusedRef.current = false;
|
|
9585
10657
|
setOpen(false);
|
|
9586
10658
|
onChange(draft.trim());
|
|
9587
10659
|
}, 200),
|
|
@@ -9669,33 +10741,37 @@ function pickOtherMetadata(m) {
|
|
|
9669
10741
|
return rest;
|
|
9670
10742
|
}
|
|
9671
10743
|
function ProductEditPage({ productId }) {
|
|
9672
|
-
const router = (0,
|
|
10744
|
+
const router = (0, import_navigation19.useRouter)();
|
|
9673
10745
|
const create = isCreate2(productId);
|
|
9674
|
-
const [loading, setLoading] = (0,
|
|
9675
|
-
const [saving, setSaving] = (0,
|
|
9676
|
-
const [errors, setErrors] = (0,
|
|
9677
|
-
const [collections, setCollections] = (0,
|
|
9678
|
-
const [brands, setBrands] = (0,
|
|
9679
|
-
const [categories, setCategories] = (0,
|
|
9680
|
-
const [name, setName] = (0,
|
|
9681
|
-
const [productSlug, setProductSlug] = (0,
|
|
9682
|
-
const [sku, setSku] = (0,
|
|
9683
|
-
const [hsn, setHsn] = (0,
|
|
9684
|
-
const [
|
|
9685
|
-
const [
|
|
9686
|
-
const [
|
|
9687
|
-
const [
|
|
9688
|
-
const [
|
|
9689
|
-
const [
|
|
9690
|
-
const [
|
|
9691
|
-
const [
|
|
9692
|
-
const [
|
|
9693
|
-
const [
|
|
9694
|
-
const [
|
|
9695
|
-
const [
|
|
9696
|
-
const [
|
|
9697
|
-
const [
|
|
9698
|
-
const [
|
|
10746
|
+
const [loading, setLoading] = (0, import_react44.useState)(!create);
|
|
10747
|
+
const [saving, setSaving] = (0, import_react44.useState)(false);
|
|
10748
|
+
const [errors, setErrors] = (0, import_react44.useState)([]);
|
|
10749
|
+
const [collections, setCollections] = (0, import_react44.useState)([]);
|
|
10750
|
+
const [brands, setBrands] = (0, import_react44.useState)([]);
|
|
10751
|
+
const [categories, setCategories] = (0, import_react44.useState)([]);
|
|
10752
|
+
const [name, setName] = (0, import_react44.useState)("");
|
|
10753
|
+
const [productSlug, setProductSlug] = (0, import_react44.useState)("");
|
|
10754
|
+
const [sku, setSku] = (0, import_react44.useState)("");
|
|
10755
|
+
const [hsn, setHsn] = (0, import_react44.useState)("");
|
|
10756
|
+
const [uom, setUom] = (0, import_react44.useState)("");
|
|
10757
|
+
const [productType, setProductType] = (0, import_react44.useState)("product");
|
|
10758
|
+
const [collectionId, setCollectionId] = (0, import_react44.useState)(null);
|
|
10759
|
+
const [brandId, setBrandId] = (0, import_react44.useState)(null);
|
|
10760
|
+
const [categoryId, setCategoryId] = (0, import_react44.useState)(null);
|
|
10761
|
+
const [price, setPrice] = (0, import_react44.useState)("");
|
|
10762
|
+
const [compareAtPrice, setCompareAtPrice] = (0, import_react44.useState)("");
|
|
10763
|
+
const [quantity, setQuantity] = (0, import_react44.useState)(1);
|
|
10764
|
+
const [status, setStatus] = (0, import_react44.useState)("draft");
|
|
10765
|
+
const [featured, setFeatured] = (0, import_react44.useState)(false);
|
|
10766
|
+
const [description, setDescription] = (0, import_react44.useState)("");
|
|
10767
|
+
const [images, setImages] = (0, import_react44.useState)([{ url: "", alt: "", isDefault: true }]);
|
|
10768
|
+
const [specifications, setSpecifications] = (0, import_react44.useState)([{ key: "", value: "" }]);
|
|
10769
|
+
const [otherMetadata, setOtherMetadata] = (0, import_react44.useState)({});
|
|
10770
|
+
const [facetRows, setFacetRows] = (0, import_react44.useState)([{ name: "", value: "" }]);
|
|
10771
|
+
const [taxMasterList, setTaxMasterList] = (0, import_react44.useState)([]);
|
|
10772
|
+
const [taxRows, setTaxRows] = (0, import_react44.useState)([{ taxId: "", rate: "" }]);
|
|
10773
|
+
const [seoId, setSeoId] = (0, import_react44.useState)(null);
|
|
10774
|
+
const [seo, setSeo] = (0, import_react44.useState)({
|
|
9699
10775
|
seoTitle: "",
|
|
9700
10776
|
seoDescription: "",
|
|
9701
10777
|
seoKeywords: "",
|
|
@@ -9703,15 +10779,16 @@ function ProductEditPage({ productId }) {
|
|
|
9703
10779
|
seoOgDescription: "",
|
|
9704
10780
|
seoOgImage: ""
|
|
9705
10781
|
});
|
|
9706
|
-
(0,
|
|
10782
|
+
(0, import_react44.useEffect)(() => {
|
|
9707
10783
|
let cancelled = false;
|
|
9708
10784
|
(async () => {
|
|
9709
10785
|
try {
|
|
9710
|
-
const [colRes, brandRes, catRes, attrRes] = await Promise.all([
|
|
10786
|
+
const [colRes, brandRes, catRes, attrRes, taxesRes] = await Promise.all([
|
|
9711
10787
|
fetch("/api/collections?limit=500"),
|
|
9712
10788
|
fetch("/api/brands?limit=500"),
|
|
9713
10789
|
fetch("/api/product_categories?limit=500"),
|
|
9714
|
-
fetch("/api/attributes?limit=500")
|
|
10790
|
+
fetch("/api/attributes?limit=500"),
|
|
10791
|
+
fetch("/api/taxes?limit=200&sortField=name&sortOrder=asc")
|
|
9715
10792
|
]);
|
|
9716
10793
|
let attrList = [];
|
|
9717
10794
|
if (!cancelled && colRes.ok) {
|
|
@@ -9730,6 +10807,13 @@ function ProductEditPage({ productId }) {
|
|
|
9730
10807
|
const d = await attrRes.json();
|
|
9731
10808
|
if (Array.isArray(d.data)) attrList = d.data;
|
|
9732
10809
|
}
|
|
10810
|
+
if (!cancelled && taxesRes.ok) {
|
|
10811
|
+
const d = await taxesRes.json();
|
|
10812
|
+
const raw = Array.isArray(d.data) ? d.data : [];
|
|
10813
|
+
setTaxMasterList(
|
|
10814
|
+
raw.filter((t) => t.active !== false && t.deleted !== true)
|
|
10815
|
+
);
|
|
10816
|
+
}
|
|
9733
10817
|
if (create) {
|
|
9734
10818
|
if (!cancelled) {
|
|
9735
10819
|
setProductSlug("");
|
|
@@ -9745,6 +10829,8 @@ function ProductEditPage({ productId }) {
|
|
|
9745
10829
|
setProductSlug(typeof product.slug === "string" ? product.slug : "");
|
|
9746
10830
|
setSku(product.sku ?? "");
|
|
9747
10831
|
setHsn(product.hsn ?? "");
|
|
10832
|
+
setUom(product.uom != null ? String(product.uom) : "");
|
|
10833
|
+
setProductType(product.type === "service" ? "service" : "product");
|
|
9748
10834
|
setCollectionId(product.collectionId ?? null);
|
|
9749
10835
|
setBrandId(product.brandId ?? null);
|
|
9750
10836
|
setCategoryId(product.categoryId ?? null);
|
|
@@ -9775,6 +10861,19 @@ function ProductEditPage({ productId }) {
|
|
|
9775
10861
|
const seoData = await fetchSeo(product.seoId);
|
|
9776
10862
|
if (!cancelled) setSeo(seoData);
|
|
9777
10863
|
}
|
|
10864
|
+
const ptRes = await fetch(`/api/product_taxes?productId=${productId}&limit=100`);
|
|
10865
|
+
if (ptRes.ok && !cancelled) {
|
|
10866
|
+
const ptData = await ptRes.json();
|
|
10867
|
+
const pts = Array.isArray(ptData.data) ? ptData.data : [];
|
|
10868
|
+
if (pts.length > 0) {
|
|
10869
|
+
setTaxRows(
|
|
10870
|
+
pts.map((p) => ({
|
|
10871
|
+
taxId: p.taxId,
|
|
10872
|
+
rate: p.rate != null && p.rate !== "" ? String(p.rate) : ""
|
|
10873
|
+
}))
|
|
10874
|
+
);
|
|
10875
|
+
} else if (!cancelled) setTaxRows([{ taxId: "", rate: "" }]);
|
|
10876
|
+
}
|
|
9778
10877
|
const paRes = await fetch(`/api/product_attributes?productId=${productId}&limit=100`);
|
|
9779
10878
|
if (paRes.ok && !cancelled) {
|
|
9780
10879
|
const paData = await paRes.json();
|
|
@@ -9798,7 +10897,7 @@ function ProductEditPage({ productId }) {
|
|
|
9798
10897
|
cancelled = true;
|
|
9799
10898
|
};
|
|
9800
10899
|
}, [productId, create]);
|
|
9801
|
-
(0,
|
|
10900
|
+
(0, import_react44.useEffect)(() => {
|
|
9802
10901
|
if (!create || !name.trim() || productSlug.trim()) return;
|
|
9803
10902
|
setProductSlug(
|
|
9804
10903
|
name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "")
|
|
@@ -9823,6 +10922,8 @@ function ProductEditPage({ productId }) {
|
|
|
9823
10922
|
name: name.trim(),
|
|
9824
10923
|
sku: sku.trim() || null,
|
|
9825
10924
|
hsn: hsn.trim() || null,
|
|
10925
|
+
uom: uom.trim() || null,
|
|
10926
|
+
type: productType,
|
|
9826
10927
|
collectionId: collectionId || null,
|
|
9827
10928
|
brandId: brandId || null,
|
|
9828
10929
|
categoryId: categoryId || null,
|
|
@@ -9919,6 +11020,55 @@ function ProductEditPage({ productId }) {
|
|
|
9919
11020
|
});
|
|
9920
11021
|
}
|
|
9921
11022
|
}
|
|
11023
|
+
const parseTaxRate = (s) => {
|
|
11024
|
+
const t = s.trim();
|
|
11025
|
+
if (!t) return null;
|
|
11026
|
+
const n = Number(t);
|
|
11027
|
+
return Number.isFinite(n) ? n : null;
|
|
11028
|
+
};
|
|
11029
|
+
const ratesDiffer = (a, b) => {
|
|
11030
|
+
if (a == null && b == null) return false;
|
|
11031
|
+
if (a == null || b == null) return true;
|
|
11032
|
+
return Math.abs(a - b) > 1e-6;
|
|
11033
|
+
};
|
|
11034
|
+
const wantedTax = /* @__PURE__ */ new Map();
|
|
11035
|
+
for (const row of taxRows) {
|
|
11036
|
+
if (row.taxId === "") continue;
|
|
11037
|
+
wantedTax.set(row.taxId, parseTaxRate(row.rate));
|
|
11038
|
+
}
|
|
11039
|
+
const ptListRes = await fetch(`/api/product_taxes?productId=${savedId}&limit=200`);
|
|
11040
|
+
const ptListData = ptListRes.ok ? await ptListRes.json() : { data: [] };
|
|
11041
|
+
const existingPt = Array.isArray(ptListData.data) ? ptListData.data : [];
|
|
11042
|
+
for (const ep of existingPt) {
|
|
11043
|
+
if (!wantedTax.has(ep.taxId)) {
|
|
11044
|
+
await fetch(`/api/product_taxes/${ep.id}`, { method: "DELETE" });
|
|
11045
|
+
}
|
|
11046
|
+
}
|
|
11047
|
+
const survivors = existingPt.filter((ep) => wantedTax.has(ep.taxId));
|
|
11048
|
+
for (const [taxId, rate] of wantedTax) {
|
|
11049
|
+
const ep = survivors.find((e) => e.taxId === taxId);
|
|
11050
|
+
if (!ep) {
|
|
11051
|
+
await fetch("/api/product_taxes", {
|
|
11052
|
+
method: "POST",
|
|
11053
|
+
headers: { "Content-Type": "application/json" },
|
|
11054
|
+
body: JSON.stringify({
|
|
11055
|
+
productId: Number(savedId),
|
|
11056
|
+
taxId,
|
|
11057
|
+
rate
|
|
11058
|
+
})
|
|
11059
|
+
});
|
|
11060
|
+
} else {
|
|
11061
|
+
const existingRate = ep.rate == null || String(ep.rate).trim() === "" ? null : Number(ep.rate);
|
|
11062
|
+
const er = Number.isFinite(existingRate) ? existingRate : null;
|
|
11063
|
+
if (ratesDiffer(er, rate)) {
|
|
11064
|
+
await fetch(`/api/product_taxes/${ep.id}`, {
|
|
11065
|
+
method: "PUT",
|
|
11066
|
+
headers: { "Content-Type": "application/json" },
|
|
11067
|
+
body: JSON.stringify({ rate })
|
|
11068
|
+
});
|
|
11069
|
+
}
|
|
11070
|
+
}
|
|
11071
|
+
}
|
|
9922
11072
|
router.push("/admin/products");
|
|
9923
11073
|
} catch {
|
|
9924
11074
|
setErrors(["Failed to save"]);
|
|
@@ -9936,6 +11086,18 @@ function ProductEditPage({ productId }) {
|
|
|
9936
11086
|
const addFacet = () => setFacetRows((prev) => [...prev, { name: "", value: "" }]);
|
|
9937
11087
|
const removeFacet = (i) => setFacetRows((prev) => prev.length <= 1 ? prev : prev.filter((_, j) => j !== i));
|
|
9938
11088
|
const setFacet = (i, field, value) => setFacetRows((prev) => prev.map((row, j) => j === i ? { ...row, [field]: value } : row));
|
|
11089
|
+
const addTaxRow = () => setTaxRows((prev) => [...prev, { taxId: "", rate: "" }]);
|
|
11090
|
+
const removeTaxRow = (i) => setTaxRows((prev) => prev.length <= 1 ? prev : prev.filter((_, j) => j !== i));
|
|
11091
|
+
const setTaxRow = (i, patch) => setTaxRows((prev) => prev.map((row, j) => j === i ? { ...row, ...patch } : row));
|
|
11092
|
+
const taxesForSelect = (rowIndex) => {
|
|
11093
|
+
const row = taxRows[rowIndex];
|
|
11094
|
+
const selectedElsewhere = new Set(
|
|
11095
|
+
taxRows.map((r, j) => j !== rowIndex && r.taxId !== "" ? r.taxId : null).filter((x) => x != null)
|
|
11096
|
+
);
|
|
11097
|
+
return taxMasterList.filter(
|
|
11098
|
+
(t) => !selectedElsewhere.has(t.id) || row && row.taxId !== "" && t.id === row.taxId
|
|
11099
|
+
);
|
|
11100
|
+
};
|
|
9939
11101
|
if (loading) {
|
|
9940
11102
|
return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "text-gray-500", children: "Loading..." }) });
|
|
9941
11103
|
}
|
|
@@ -9947,8 +11109,8 @@ function ProductEditPage({ productId }) {
|
|
|
9947
11109
|
subtitle: create ? "Create a new product" : "Update product details",
|
|
9948
11110
|
closeHref: "/admin/products",
|
|
9949
11111
|
menuItems: [
|
|
9950
|
-
{ label: saving ? "Saving..." : "Save", onClick: handleSave },
|
|
9951
|
-
{ label: featured ? "Unfeature" : "Feature", onClick: () => setFeatured(!featured) }
|
|
11112
|
+
{ label: saving ? "Saving..." : "Save", icon: import_lucide_react34.Save, onClick: handleSave },
|
|
11113
|
+
{ label: featured ? "Unfeature" : "Feature", icon: import_lucide_react34.Star, onClick: () => setFeatured(!featured) }
|
|
9952
11114
|
]
|
|
9953
11115
|
}
|
|
9954
11116
|
),
|
|
@@ -9980,6 +11142,36 @@ function ProductEditPage({ productId }) {
|
|
|
9980
11142
|
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("input", { type: "text", value: hsn, onChange: (e) => setHsn(e.target.value), className: inputCls })
|
|
9981
11143
|
] })
|
|
9982
11144
|
] }),
|
|
11145
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
11146
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { children: [
|
|
11147
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("label", { className: labelCls, children: "UOM" }),
|
|
11148
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
11149
|
+
"input",
|
|
11150
|
+
{
|
|
11151
|
+
type: "text",
|
|
11152
|
+
value: uom,
|
|
11153
|
+
onChange: (e) => setUom(e.target.value),
|
|
11154
|
+
className: inputCls,
|
|
11155
|
+
placeholder: "e.g. pcs, kg, hrs"
|
|
11156
|
+
}
|
|
11157
|
+
)
|
|
11158
|
+
] }),
|
|
11159
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { children: [
|
|
11160
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("label", { className: labelCls, children: "Product type" }),
|
|
11161
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
|
|
11162
|
+
"select",
|
|
11163
|
+
{
|
|
11164
|
+
value: productType,
|
|
11165
|
+
onChange: (e) => setProductType(e.target.value === "service" ? "service" : "product"),
|
|
11166
|
+
className: inputCls,
|
|
11167
|
+
children: [
|
|
11168
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("option", { value: "product", children: "Product" }),
|
|
11169
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("option", { value: "service", children: "Service" })
|
|
11170
|
+
]
|
|
11171
|
+
}
|
|
11172
|
+
)
|
|
11173
|
+
] })
|
|
11174
|
+
] }),
|
|
9983
11175
|
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
9984
11176
|
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { children: [
|
|
9985
11177
|
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("label", { className: labelCls, children: "Brand" }),
|
|
@@ -10030,6 +11222,78 @@ function ProductEditPage({ productId }) {
|
|
|
10030
11222
|
] })
|
|
10031
11223
|
] })
|
|
10032
11224
|
] }),
|
|
11225
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("section", { children: [
|
|
11226
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("h2", { className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2", children: "Taxes" }),
|
|
11227
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("p", { className: "text-xs text-gray-500 mb-2", children: "Link taxes for this product. Leave rate empty to use the tax's default rate; set a value to override." }),
|
|
11228
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: `${sectionCls} space-y-2`, children: [
|
|
11229
|
+
taxRows.map((row, i) => {
|
|
11230
|
+
const options = taxesForSelect(i);
|
|
11231
|
+
const master = row.taxId !== "" ? taxMasterList.find((t) => t.id === row.taxId) : void 0;
|
|
11232
|
+
const defaultRateHint = master != null ? `Default: ${String(master.rate)}%` : void 0;
|
|
11233
|
+
return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "flex flex-wrap gap-2 items-end", children: [
|
|
11234
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "min-w-[160px] flex-1", children: [
|
|
11235
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("label", { className: labelCls, children: "Tax" }),
|
|
11236
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
|
|
11237
|
+
"select",
|
|
11238
|
+
{
|
|
11239
|
+
value: row.taxId === "" ? "" : String(row.taxId),
|
|
11240
|
+
onChange: (e) => {
|
|
11241
|
+
const v = e.target.value;
|
|
11242
|
+
setTaxRow(i, { taxId: v === "" ? "" : Number(v) });
|
|
11243
|
+
},
|
|
11244
|
+
className: inputCls,
|
|
11245
|
+
children: [
|
|
11246
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("option", { value: "", children: "\u2014" }),
|
|
11247
|
+
options.map((t) => /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("option", { value: t.id, children: [
|
|
11248
|
+
t.name,
|
|
11249
|
+
" (",
|
|
11250
|
+
String(t.rate),
|
|
11251
|
+
"%)"
|
|
11252
|
+
] }, t.id))
|
|
11253
|
+
]
|
|
11254
|
+
}
|
|
11255
|
+
)
|
|
11256
|
+
] }),
|
|
11257
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { className: "w-28", children: [
|
|
11258
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("label", { className: labelCls, children: "Rate %" }),
|
|
11259
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
11260
|
+
"input",
|
|
11261
|
+
{
|
|
11262
|
+
type: "text",
|
|
11263
|
+
inputMode: "decimal",
|
|
11264
|
+
value: row.rate,
|
|
11265
|
+
onChange: (e) => setTaxRow(i, { rate: e.target.value }),
|
|
11266
|
+
className: inputCls,
|
|
11267
|
+
placeholder: defaultRateHint ?? "optional",
|
|
11268
|
+
title: defaultRateHint
|
|
11269
|
+
}
|
|
11270
|
+
)
|
|
11271
|
+
] }),
|
|
11272
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
11273
|
+
"button",
|
|
11274
|
+
{
|
|
11275
|
+
type: "button",
|
|
11276
|
+
onClick: () => removeTaxRow(i),
|
|
11277
|
+
className: "p-2 text-gray-400 hover:text-red-600 rounded shrink-0 mb-0.5",
|
|
11278
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_lucide_react34.Trash2, { className: "h-4 w-4" })
|
|
11279
|
+
}
|
|
11280
|
+
)
|
|
11281
|
+
] }, i);
|
|
11282
|
+
}),
|
|
11283
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
|
|
11284
|
+
"button",
|
|
11285
|
+
{
|
|
11286
|
+
type: "button",
|
|
11287
|
+
onClick: addTaxRow,
|
|
11288
|
+
className: "inline-flex items-center gap-1 rounded border border-gray-300 bg-white px-2 py-1.5 text-xs font-medium text-gray-700 hover:bg-gray-50",
|
|
11289
|
+
children: [
|
|
11290
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_lucide_react34.Plus, { className: "h-3.5 w-3.5" }),
|
|
11291
|
+
" Add tax"
|
|
11292
|
+
]
|
|
11293
|
+
}
|
|
11294
|
+
)
|
|
11295
|
+
] })
|
|
11296
|
+
] }),
|
|
10033
11297
|
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("section", { children: [
|
|
10034
11298
|
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("h2", { className: "text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2", children: "Attributes (facets)" }),
|
|
10035
11299
|
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)("p", { className: "text-xs text-gray-500 mb-2", children: "Search existing attributes or create new ones (like blog categories). Value on the right." }),
|
|
@@ -10164,8 +11428,8 @@ function ProductEditPage({ productId }) {
|
|
|
10164
11428
|
}
|
|
10165
11429
|
|
|
10166
11430
|
// src/admin/pages/CollectionEditPage.tsx
|
|
10167
|
-
var
|
|
10168
|
-
var
|
|
11431
|
+
var import_react45 = require("react");
|
|
11432
|
+
var import_navigation20 = require("next/navigation");
|
|
10169
11433
|
var import_lucide_react35 = require("lucide-react");
|
|
10170
11434
|
var import_jsx_runtime61 = require("react/jsx-runtime");
|
|
10171
11435
|
var isCreate3 = (id) => id === "create";
|
|
@@ -10175,29 +11439,29 @@ var sectionCls2 = "min-w-0 overflow-hidden border border-gray-200 rounded-lg p-4
|
|
|
10175
11439
|
var labelCls2 = "block text-xs font-medium text-gray-600 mb-1";
|
|
10176
11440
|
var inputCls2 = "w-full rounded-md border border-gray-300 px-2 py-1.5 text-sm";
|
|
10177
11441
|
function CollectionEditPage({ collectionId }) {
|
|
10178
|
-
const router = (0,
|
|
11442
|
+
const router = (0, import_navigation20.useRouter)();
|
|
10179
11443
|
const create = isCreate3(collectionId);
|
|
10180
|
-
const [loading, setLoading] = (0,
|
|
10181
|
-
const [saving, setSaving] = (0,
|
|
10182
|
-
const [errors, setErrors] = (0,
|
|
10183
|
-
const [categories, setCategories] = (0,
|
|
10184
|
-
const [brands, setBrands] = (0,
|
|
10185
|
-
const [name, setName] = (0,
|
|
10186
|
-
const [slug, setSlug] = (0,
|
|
10187
|
-
const [hsn, setHsn] = (0,
|
|
10188
|
-
const [categoryId, setCategoryId] = (0,
|
|
10189
|
-
const [brandId, setBrandId] = (0,
|
|
10190
|
-
const [description, setDescription] = (0,
|
|
10191
|
-
const [image, setImage] = (0,
|
|
10192
|
-
const [active, setActive] = (0,
|
|
10193
|
-
const [sortOrder, setSortOrder] = (0,
|
|
10194
|
-
const [advancedOpen, setAdvancedOpen] = (0,
|
|
10195
|
-
const [heroSlides, setHeroSlides] = (0,
|
|
10196
|
-
const [variants, setVariants] = (0,
|
|
10197
|
-
const [experienceDescription, setExperienceDescription] = (0,
|
|
10198
|
-
const [brochureUrl, setBrochureUrl] = (0,
|
|
10199
|
-
const [seoId, setSeoId] = (0,
|
|
10200
|
-
const [seo, setSeo] = (0,
|
|
11444
|
+
const [loading, setLoading] = (0, import_react45.useState)(!create);
|
|
11445
|
+
const [saving, setSaving] = (0, import_react45.useState)(false);
|
|
11446
|
+
const [errors, setErrors] = (0, import_react45.useState)([]);
|
|
11447
|
+
const [categories, setCategories] = (0, import_react45.useState)([]);
|
|
11448
|
+
const [brands, setBrands] = (0, import_react45.useState)([]);
|
|
11449
|
+
const [name, setName] = (0, import_react45.useState)("");
|
|
11450
|
+
const [slug, setSlug] = (0, import_react45.useState)("");
|
|
11451
|
+
const [hsn, setHsn] = (0, import_react45.useState)("");
|
|
11452
|
+
const [categoryId, setCategoryId] = (0, import_react45.useState)(null);
|
|
11453
|
+
const [brandId, setBrandId] = (0, import_react45.useState)(null);
|
|
11454
|
+
const [description, setDescription] = (0, import_react45.useState)("");
|
|
11455
|
+
const [image, setImage] = (0, import_react45.useState)("");
|
|
11456
|
+
const [active, setActive] = (0, import_react45.useState)(true);
|
|
11457
|
+
const [sortOrder, setSortOrder] = (0, import_react45.useState)(0);
|
|
11458
|
+
const [advancedOpen, setAdvancedOpen] = (0, import_react45.useState)(false);
|
|
11459
|
+
const [heroSlides, setHeroSlides] = (0, import_react45.useState)([emptySlide()]);
|
|
11460
|
+
const [variants, setVariants] = (0, import_react45.useState)([emptyVariant()]);
|
|
11461
|
+
const [experienceDescription, setExperienceDescription] = (0, import_react45.useState)("");
|
|
11462
|
+
const [brochureUrl, setBrochureUrl] = (0, import_react45.useState)("");
|
|
11463
|
+
const [seoId, setSeoId] = (0, import_react45.useState)(null);
|
|
11464
|
+
const [seo, setSeo] = (0, import_react45.useState)({
|
|
10201
11465
|
seoTitle: "",
|
|
10202
11466
|
seoDescription: "",
|
|
10203
11467
|
seoKeywords: "",
|
|
@@ -10205,7 +11469,7 @@ function CollectionEditPage({ collectionId }) {
|
|
|
10205
11469
|
seoOgDescription: "",
|
|
10206
11470
|
seoOgImage: ""
|
|
10207
11471
|
});
|
|
10208
|
-
(0,
|
|
11472
|
+
(0, import_react45.useEffect)(() => {
|
|
10209
11473
|
let cancelled = false;
|
|
10210
11474
|
(async () => {
|
|
10211
11475
|
try {
|
|
@@ -10378,8 +11642,8 @@ function CollectionEditPage({ collectionId }) {
|
|
|
10378
11642
|
subtitle: create ? "Create a new collection" : "Update collection and page content",
|
|
10379
11643
|
closeHref: "/admin/collections",
|
|
10380
11644
|
menuItems: [
|
|
10381
|
-
{ label: saving ? "Saving..." : "Save", onClick: handleSave },
|
|
10382
|
-
{ label: active ? "Deactivate" : "Activate", onClick: () => setActive(!active) }
|
|
11645
|
+
{ label: saving ? "Saving..." : "Save", icon: import_lucide_react35.Save, onClick: handleSave },
|
|
11646
|
+
{ label: active ? "Deactivate" : "Activate", icon: import_lucide_react35.Power, onClick: () => setActive(!active) }
|
|
10383
11647
|
]
|
|
10384
11648
|
}
|
|
10385
11649
|
),
|
|
@@ -10522,8 +11786,8 @@ function CollectionEditPage({ collectionId }) {
|
|
|
10522
11786
|
}
|
|
10523
11787
|
|
|
10524
11788
|
// src/admin/pages/RolesPage.tsx
|
|
10525
|
-
var
|
|
10526
|
-
var
|
|
11789
|
+
var import_react46 = require("react");
|
|
11790
|
+
var import_react47 = require("next-auth/react");
|
|
10527
11791
|
var import_lucide_react36 = require("lucide-react");
|
|
10528
11792
|
|
|
10529
11793
|
// src/auth/permission-entities.ts
|
|
@@ -10554,18 +11818,19 @@ function RoleListItem({
|
|
|
10554
11818
|
);
|
|
10555
11819
|
}
|
|
10556
11820
|
function RolesPage() {
|
|
10557
|
-
const { data: session, status } = (0,
|
|
11821
|
+
const { data: session, status } = (0, import_react47.useSession)();
|
|
10558
11822
|
const u = session?.user;
|
|
10559
11823
|
const canManage = !!u?.isRBACAdmin;
|
|
10560
|
-
const [entities, setEntities] = (0,
|
|
10561
|
-
const [groups, setGroups] = (0,
|
|
10562
|
-
const [selectedId, setSelectedId] = (0,
|
|
10563
|
-
const [matrix, setMatrix] = (0,
|
|
10564
|
-
const [loading, setLoading] = (0,
|
|
10565
|
-
const [saving, setSaving] = (0,
|
|
10566
|
-
const [newName, setNewName] = (0,
|
|
10567
|
-
const [
|
|
10568
|
-
const
|
|
11824
|
+
const [entities, setEntities] = (0, import_react46.useState)([]);
|
|
11825
|
+
const [groups, setGroups] = (0, import_react46.useState)([]);
|
|
11826
|
+
const [selectedId, setSelectedId] = (0, import_react46.useState)(null);
|
|
11827
|
+
const [matrix, setMatrix] = (0, import_react46.useState)({});
|
|
11828
|
+
const [loading, setLoading] = (0, import_react46.useState)(true);
|
|
11829
|
+
const [saving, setSaving] = (0, import_react46.useState)(false);
|
|
11830
|
+
const [newName, setNewName] = (0, import_react46.useState)("");
|
|
11831
|
+
const [deleteRoleOpen, setDeleteRoleOpen] = (0, import_react46.useState)(false);
|
|
11832
|
+
const [error, setError] = (0, import_react46.useState)(null);
|
|
11833
|
+
const load = (0, import_react46.useCallback)(async () => {
|
|
10569
11834
|
setLoading(true);
|
|
10570
11835
|
setError(null);
|
|
10571
11836
|
try {
|
|
@@ -10590,11 +11855,11 @@ function RolesPage() {
|
|
|
10590
11855
|
setLoading(false);
|
|
10591
11856
|
}
|
|
10592
11857
|
}, []);
|
|
10593
|
-
(0,
|
|
11858
|
+
(0, import_react46.useEffect)(() => {
|
|
10594
11859
|
if (status === "authenticated" && canManage) load();
|
|
10595
11860
|
}, [status, canManage, load]);
|
|
10596
11861
|
const selected = groups.find((g) => g.id === selectedId);
|
|
10597
|
-
(0,
|
|
11862
|
+
(0, import_react46.useEffect)(() => {
|
|
10598
11863
|
if (!selected || !entities.length) {
|
|
10599
11864
|
setMatrix({});
|
|
10600
11865
|
return;
|
|
@@ -10619,6 +11884,45 @@ function RolesPage() {
|
|
|
10619
11884
|
[entity]: { ...prev[entity], [key]: !prev[entity][key] }
|
|
10620
11885
|
}));
|
|
10621
11886
|
};
|
|
11887
|
+
const PERM_KEYS = [
|
|
11888
|
+
"canCreate",
|
|
11889
|
+
"canRead",
|
|
11890
|
+
"canUpdate",
|
|
11891
|
+
"canDelete"
|
|
11892
|
+
];
|
|
11893
|
+
const isColumnAllChecked = (key) => entities.length > 0 && entities.every((entity) => matrix[entity]?.[key]);
|
|
11894
|
+
const toggleColumn = (key) => {
|
|
11895
|
+
const nextValue = !isColumnAllChecked(key);
|
|
11896
|
+
setMatrix((prev) => {
|
|
11897
|
+
const next = { ...prev };
|
|
11898
|
+
for (const entity of entities) {
|
|
11899
|
+
if (!next[entity]) continue;
|
|
11900
|
+
next[entity] = { ...next[entity], [key]: nextValue };
|
|
11901
|
+
}
|
|
11902
|
+
return next;
|
|
11903
|
+
});
|
|
11904
|
+
};
|
|
11905
|
+
const isAllChecked = entities.length > 0 && entities.every((entity) => {
|
|
11906
|
+
const row = matrix[entity];
|
|
11907
|
+
return row ? PERM_KEYS.every((k) => row[k]) : false;
|
|
11908
|
+
});
|
|
11909
|
+
const toggleAll = () => {
|
|
11910
|
+
const nextValue = !isAllChecked;
|
|
11911
|
+
setMatrix((prev) => {
|
|
11912
|
+
const next = { ...prev };
|
|
11913
|
+
for (const entity of entities) {
|
|
11914
|
+
if (!next[entity]) continue;
|
|
11915
|
+
next[entity] = {
|
|
11916
|
+
...next[entity],
|
|
11917
|
+
canCreate: nextValue,
|
|
11918
|
+
canRead: nextValue,
|
|
11919
|
+
canUpdate: nextValue,
|
|
11920
|
+
canDelete: nextValue
|
|
11921
|
+
};
|
|
11922
|
+
}
|
|
11923
|
+
return next;
|
|
11924
|
+
});
|
|
11925
|
+
};
|
|
10622
11926
|
const saveMatrix = async () => {
|
|
10623
11927
|
if (!selectedId) return;
|
|
10624
11928
|
setSaving(true);
|
|
@@ -10667,7 +11971,6 @@ function RolesPage() {
|
|
|
10667
11971
|
};
|
|
10668
11972
|
const deleteGroup = async () => {
|
|
10669
11973
|
if (!selectedId || !selected || isSuperAdminGroupName(selected.name)) return;
|
|
10670
|
-
if (!confirm(`Delete group "${selected.name}"?`)) return;
|
|
10671
11974
|
try {
|
|
10672
11975
|
const res = await fetch(`/api/admin/roles/${selectedId}`, { method: "DELETE" });
|
|
10673
11976
|
if (!res.ok) {
|
|
@@ -10676,6 +11979,7 @@ function RolesPage() {
|
|
|
10676
11979
|
return;
|
|
10677
11980
|
}
|
|
10678
11981
|
setSelectedId(null);
|
|
11982
|
+
setDeleteRoleOpen(false);
|
|
10679
11983
|
await load();
|
|
10680
11984
|
} catch {
|
|
10681
11985
|
setError("Delete failed");
|
|
@@ -10729,18 +12033,33 @@ function RolesPage() {
|
|
|
10729
12033
|
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(import_lucide_react36.Save, { className: "h-3.5 w-3.5" }),
|
|
10730
12034
|
saving ? "Saving\u2026" : "Save"
|
|
10731
12035
|
] }),
|
|
10732
|
-
selected && !isSuperAdminGroupName(selected.name) && /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(Button, { size: "sm", variant: "outline", className: "gap-1 text-red-600", onClick:
|
|
12036
|
+
selected && !isSuperAdminGroupName(selected.name) && /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(Button, { size: "sm", variant: "outline", className: "gap-1 text-red-600", onClick: () => setDeleteRoleOpen(true), children: [
|
|
10733
12037
|
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(import_lucide_react36.Trash2, { className: "h-3.5 w-3.5" }),
|
|
10734
12038
|
"Delete role"
|
|
10735
12039
|
] })
|
|
10736
12040
|
] }),
|
|
10737
12041
|
entities.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("div", { className: "overflow-x-auto rounded-md border border-gray-200 dark:border-gray-600", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("table", { className: "w-full text-sm", children: [
|
|
10738
12042
|
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("tr", { className: "border-b bg-gray-50 dark:bg-gray-900/50", children: [
|
|
10739
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("th", { className: "p-3 text-left font-medium", children: "
|
|
10740
|
-
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("th", { className: "p-2", children: "
|
|
12043
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("th", { className: "p-3 text-left font-medium", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
12044
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("input", { type: "checkbox", checked: isAllChecked, onChange: toggleAll }),
|
|
12045
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: "Resource" })
|
|
12046
|
+
] }) }),
|
|
12047
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("th", { className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
|
|
12048
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: "C" }),
|
|
12049
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("input", { type: "checkbox", checked: isColumnAllChecked("canCreate"), onChange: () => toggleColumn("canCreate") })
|
|
12050
|
+
] }) }),
|
|
12051
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("th", { className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
|
|
12052
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: "R" }),
|
|
12053
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("input", { type: "checkbox", checked: isColumnAllChecked("canRead"), onChange: () => toggleColumn("canRead") })
|
|
12054
|
+
] }) }),
|
|
12055
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("th", { className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
|
|
12056
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: "U" }),
|
|
12057
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("input", { type: "checkbox", checked: isColumnAllChecked("canUpdate"), onChange: () => toggleColumn("canUpdate") })
|
|
12058
|
+
] }) }),
|
|
12059
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("th", { className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)("div", { className: "flex items-center justify-center gap-1", children: [
|
|
12060
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("span", { children: "D" }),
|
|
12061
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("input", { type: "checkbox", checked: isColumnAllChecked("canDelete"), onChange: () => toggleColumn("canDelete") })
|
|
12062
|
+
] }) })
|
|
10744
12063
|
] }) }),
|
|
10745
12064
|
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("tbody", { children: entities.map((entity) => {
|
|
10746
12065
|
const row = matrix[entity];
|
|
@@ -10764,7 +12083,15 @@ function RolesPage() {
|
|
|
10764
12083
|
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Input, { placeholder: "New role name", value: newName, onChange: (e) => setNewName(e.target.value), onKeyDown: (e) => e.key === "Enter" && createGroup() }),
|
|
10765
12084
|
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { type: "button", onClick: createGroup, children: "Add role" })
|
|
10766
12085
|
] })
|
|
10767
|
-
] })
|
|
12086
|
+
] }),
|
|
12087
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Dialog, { open: deleteRoleOpen, onOpenChange: setDeleteRoleOpen, children: /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(DialogContent, { children: [
|
|
12088
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(DialogTitle, { children: "Delete Role" }) }),
|
|
12089
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { className: "text-sm text-gray-600", children: selected ? `Delete group "${selected.name}"?` : "Delete selected role?" }),
|
|
12090
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(DialogFooter, { children: [
|
|
12091
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { variant: "outline", onClick: () => setDeleteRoleOpen(false), children: "Cancel" }),
|
|
12092
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Button, { variant: "destructive", onClick: deleteGroup, children: "Delete" })
|
|
12093
|
+
] })
|
|
12094
|
+
] }) })
|
|
10768
12095
|
] });
|
|
10769
12096
|
}
|
|
10770
12097
|
|
|
@@ -11042,9 +12369,9 @@ var CRUD_CONFIGS = {
|
|
|
11042
12369
|
}
|
|
11043
12370
|
};
|
|
11044
12371
|
function BlogEditorWrapper({ blogId }) {
|
|
11045
|
-
const [blog, setBlog] = (0,
|
|
11046
|
-
const [loading, setLoading] = (0,
|
|
11047
|
-
(0,
|
|
12372
|
+
const [blog, setBlog] = (0, import_react48.useState)(null);
|
|
12373
|
+
const [loading, setLoading] = (0, import_react48.useState)(!!blogId);
|
|
12374
|
+
(0, import_react48.useEffect)(() => {
|
|
11048
12375
|
if (!blogId) return;
|
|
11049
12376
|
fetch(`/api/blogs/${blogId}`).then((res) => res.ok ? res.json() : null).then((data) => setBlog(data)).finally(() => setLoading(false));
|
|
11050
12377
|
}, [blogId]);
|
|
@@ -11057,10 +12384,10 @@ function BlogEditorWrapper({ blogId }) {
|
|
|
11057
12384
|
return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(BlogEditor, { existingBlog: blog });
|
|
11058
12385
|
}
|
|
11059
12386
|
function AdminPageResolver({ slug }) {
|
|
11060
|
-
const router = (0,
|
|
11061
|
-
const { customCrudConfigs, storeEnabled } = (0,
|
|
12387
|
+
const router = (0, import_navigation21.useRouter)();
|
|
12388
|
+
const { customCrudConfigs, storeEnabled } = (0, import_react48.useContext)(AdminConfigContext);
|
|
11062
12389
|
const key = slug?.[0] || "dashboard";
|
|
11063
|
-
(0,
|
|
12390
|
+
(0, import_react48.useEffect)(() => {
|
|
11064
12391
|
if (key === "layout-settings") {
|
|
11065
12392
|
router.replace("/admin/settings?tab=navbar");
|
|
11066
12393
|
}
|
|
@@ -11120,7 +12447,7 @@ function AdminPageResolver({ slug }) {
|
|
|
11120
12447
|
{ field: "orderCount", displayName: "Orders" },
|
|
11121
12448
|
{ field: "totalPaid", displayName: "Total paid" }
|
|
11122
12449
|
] : crud.columns;
|
|
11123
|
-
const extraListParams = (0,
|
|
12450
|
+
const extraListParams = (0, import_react48.useMemo)(
|
|
11124
12451
|
() => isContactsWithStore ? { includeSummary: "1" } : void 0,
|
|
11125
12452
|
[isContactsWithStore]
|
|
11126
12453
|
);
|
|
@@ -11143,22 +12470,22 @@ function AdminPageResolver({ slug }) {
|
|
|
11143
12470
|
}
|
|
11144
12471
|
|
|
11145
12472
|
// src/admin/pages/LayoutSettingsPage.tsx
|
|
11146
|
-
var
|
|
12473
|
+
var import_react49 = require("react");
|
|
11147
12474
|
var import_lucide_react37 = require("lucide-react");
|
|
11148
12475
|
var import_jsx_runtime64 = require("react/jsx-runtime");
|
|
11149
12476
|
function LayoutSettingsPage() {
|
|
11150
|
-
const { theme } = (0,
|
|
11151
|
-
const [activeTab, setActiveTab] = (0,
|
|
11152
|
-
const [navbarConfig, setNavbarConfig] = (0,
|
|
12477
|
+
const { theme } = (0, import_react49.useContext)(AdminConfigContext);
|
|
12478
|
+
const [activeTab, setActiveTab] = (0, import_react49.useState)("navbar");
|
|
12479
|
+
const [navbarConfig, setNavbarConfig] = (0, import_react49.useState)({
|
|
11153
12480
|
logo: "",
|
|
11154
12481
|
items: [],
|
|
11155
12482
|
ctaLabel: "",
|
|
11156
12483
|
ctaUrl: ""
|
|
11157
12484
|
});
|
|
11158
|
-
const [footerValues, setFooterValues] = (0,
|
|
11159
|
-
const [saving, setSaving] = (0,
|
|
11160
|
-
const [loading, setLoading] = (0,
|
|
11161
|
-
(0,
|
|
12485
|
+
const [footerValues, setFooterValues] = (0, import_react49.useState)({});
|
|
12486
|
+
const [saving, setSaving] = (0, import_react49.useState)(false);
|
|
12487
|
+
const [loading, setLoading] = (0, import_react49.useState)(true);
|
|
12488
|
+
(0, import_react49.useEffect)(() => {
|
|
11162
12489
|
fetch("/api/settings/theme").then((r) => r.ok ? r.json() : null).then((data) => {
|
|
11163
12490
|
if (!data) return;
|
|
11164
12491
|
if (data.navbar) {
|
|
@@ -11268,14 +12595,14 @@ var DEFAULT_ADMIN_NAV = [
|
|
|
11268
12595
|
];
|
|
11269
12596
|
|
|
11270
12597
|
// src/admin/CmsProviders.tsx
|
|
11271
|
-
var
|
|
12598
|
+
var import_react50 = require("next-auth/react");
|
|
11272
12599
|
var import_next_themes = require("next-themes");
|
|
11273
|
-
var
|
|
12600
|
+
var import_sonner8 = require("sonner");
|
|
11274
12601
|
var import_jsx_runtime65 = require("react/jsx-runtime");
|
|
11275
12602
|
function CmsProviders({ children }) {
|
|
11276
|
-
return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
12603
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(import_react50.SessionProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(import_next_themes.ThemeProvider, { attribute: "class", defaultTheme: "system", enableSystem: true, children: [
|
|
11277
12604
|
children,
|
|
11278
|
-
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
12605
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(import_sonner8.Toaster, { position: "top-right" })
|
|
11279
12606
|
] }) });
|
|
11280
12607
|
}
|
|
11281
12608
|
// Annotate the CommonJS export names for ESM import in node:
|