@multiplatform.one/frappe-ui 7.28.1 → 7.29.0

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.
Files changed (325) hide show
  1. package/dist/cjs/FrappeCalendar.cjs +3 -2
  2. package/dist/cjs/FrappeCalendar.native.js +3 -2
  3. package/dist/cjs/FrappeCalendar.native.js.map +1 -1
  4. package/dist/cjs/FrappeForm.cjs +148 -464
  5. package/dist/cjs/FrappeForm.native.js +152 -633
  6. package/dist/cjs/FrappeForm.native.js.map +1 -1
  7. package/dist/cjs/FrappeListView.cjs +5 -27
  8. package/dist/cjs/FrappeListView.native.js +11 -32
  9. package/dist/cjs/FrappeListView.native.js.map +1 -1
  10. package/dist/cjs/FrappeReportBuilder.cjs +523 -309
  11. package/dist/cjs/FrappeReportBuilder.native.js +658 -353
  12. package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
  13. package/dist/cjs/FrappeReportView.cjs +35 -234
  14. package/dist/cjs/FrappeReportView.native.js +39 -283
  15. package/dist/cjs/FrappeReportView.native.js.map +1 -1
  16. package/dist/cjs/dependsOn.cjs +2 -2
  17. package/dist/cjs/dependsOn.native.js +1 -2
  18. package/dist/cjs/dependsOn.native.js.map +1 -1
  19. package/dist/cjs/fields/shared/EditorPreviewPanes.cjs +2 -2
  20. package/dist/cjs/fields/shared/EditorPreviewPanes.native.js +2 -2
  21. package/dist/cjs/fields/shared/EditorPreviewPanes.native.js.map +1 -1
  22. package/dist/cjs/formEngine/docPermissions.cjs +20 -0
  23. package/dist/cjs/formEngine/docPermissions.native.js +69 -0
  24. package/dist/cjs/formEngine/docPermissions.native.js.map +1 -1
  25. package/dist/cjs/formEngine/index.cjs +2 -0
  26. package/dist/cjs/formEngine/index.native.js +2 -0
  27. package/dist/cjs/formEngine/index.native.js.map +1 -1
  28. package/dist/cjs/index.cjs +67 -7
  29. package/dist/cjs/index.native.js +67 -7
  30. package/dist/cjs/index.native.js.map +1 -1
  31. package/dist/cjs/liveErrors.cjs +8 -2
  32. package/dist/cjs/liveErrors.native.js +8 -2
  33. package/dist/cjs/liveErrors.native.js.map +1 -1
  34. package/dist/cjs/measures.cjs +31 -0
  35. package/dist/cjs/measures.native.js +34 -0
  36. package/dist/cjs/measures.native.js.map +1 -0
  37. package/dist/cjs/recordShape.cjs +103 -0
  38. package/dist/cjs/recordShape.native.js +115 -0
  39. package/dist/cjs/recordShape.native.js.map +1 -0
  40. package/dist/cjs/report/ReportGrid.cjs +453 -0
  41. package/dist/cjs/report/ReportGrid.native.js +510 -0
  42. package/dist/cjs/report/ReportGrid.native.js.map +1 -0
  43. package/dist/cjs/report/ReportViewToolbar.cjs +253 -0
  44. package/dist/cjs/report/ReportViewToolbar.native.js +303 -0
  45. package/dist/cjs/report/ReportViewToolbar.native.js.map +1 -0
  46. package/dist/cjs/report/queryReport.cjs +444 -0
  47. package/dist/cjs/report/queryReport.native.js +588 -0
  48. package/dist/cjs/report/queryReport.native.js.map +1 -0
  49. package/dist/cjs/report/queryReportColumns.cjs +131 -0
  50. package/dist/cjs/report/queryReportColumns.native.js +159 -0
  51. package/dist/cjs/report/queryReportColumns.native.js.map +1 -0
  52. package/dist/cjs/report/reportViewModel.cjs +386 -0
  53. package/dist/cjs/report/reportViewModel.native.js +657 -0
  54. package/dist/cjs/report/reportViewModel.native.js.map +1 -0
  55. package/dist/cjs/report/savedReports.cjs +137 -0
  56. package/dist/cjs/report/savedReports.native.js +169 -0
  57. package/dist/cjs/report/savedReports.native.js.map +1 -0
  58. package/dist/cjs/report/useFrappeReport.cjs +184 -0
  59. package/dist/cjs/report/useFrappeReport.native.js +214 -0
  60. package/dist/cjs/report/useFrappeReport.native.js.map +1 -0
  61. package/dist/cjs/report/useQueryReport.cjs +201 -0
  62. package/dist/cjs/report/useQueryReport.native.js +233 -0
  63. package/dist/cjs/report/useQueryReport.native.js.map +1 -0
  64. package/dist/cjs/useFrappeForm.cjs +558 -0
  65. package/dist/cjs/useFrappeForm.native.js +714 -0
  66. package/dist/cjs/useFrappeForm.native.js.map +1 -0
  67. package/dist/esm/FrappeCalendar.mjs +4 -3
  68. package/dist/esm/FrappeCalendar.mjs.map +1 -1
  69. package/dist/esm/FrappeCalendar.native.js +4 -3
  70. package/dist/esm/FrappeCalendar.native.js.map +1 -1
  71. package/dist/esm/FrappeForm.mjs +151 -467
  72. package/dist/esm/FrappeForm.mjs.map +1 -1
  73. package/dist/esm/FrappeForm.native.js +155 -636
  74. package/dist/esm/FrappeForm.native.js.map +1 -1
  75. package/dist/esm/FrappeListView.mjs +6 -28
  76. package/dist/esm/FrappeListView.mjs.map +1 -1
  77. package/dist/esm/FrappeListView.native.js +12 -33
  78. package/dist/esm/FrappeListView.native.js.map +1 -1
  79. package/dist/esm/FrappeReportBuilder.mjs +526 -312
  80. package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
  81. package/dist/esm/FrappeReportBuilder.native.js +661 -356
  82. package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
  83. package/dist/esm/FrappeReportView.mjs +30 -229
  84. package/dist/esm/FrappeReportView.mjs.map +1 -1
  85. package/dist/esm/FrappeReportView.native.js +34 -278
  86. package/dist/esm/FrappeReportView.native.js.map +1 -1
  87. package/dist/esm/dependsOn.mjs +2 -2
  88. package/dist/esm/dependsOn.mjs.map +1 -1
  89. package/dist/esm/dependsOn.native.js +1 -2
  90. package/dist/esm/dependsOn.native.js.map +1 -1
  91. package/dist/esm/fields/shared/EditorPreviewPanes.mjs +2 -2
  92. package/dist/esm/fields/shared/EditorPreviewPanes.mjs.map +1 -1
  93. package/dist/esm/fields/shared/EditorPreviewPanes.native.js +2 -2
  94. package/dist/esm/fields/shared/EditorPreviewPanes.native.js.map +1 -1
  95. package/dist/esm/formEngine/docPermissions.mjs +19 -1
  96. package/dist/esm/formEngine/docPermissions.mjs.map +1 -1
  97. package/dist/esm/formEngine/docPermissions.native.js +68 -1
  98. package/dist/esm/formEngine/docPermissions.native.js.map +1 -1
  99. package/dist/esm/formEngine/index.mjs +2 -2
  100. package/dist/esm/formEngine/index.mjs.map +1 -1
  101. package/dist/esm/formEngine/index.native.js +2 -2
  102. package/dist/esm/formEngine/index.native.js.map +1 -1
  103. package/dist/esm/index.mjs +16 -4
  104. package/dist/esm/index.mjs.map +1 -1
  105. package/dist/esm/index.native.js +16 -4
  106. package/dist/esm/index.native.js.map +1 -1
  107. package/dist/esm/liveErrors.mjs +8 -3
  108. package/dist/esm/liveErrors.mjs.map +1 -1
  109. package/dist/esm/liveErrors.native.js +8 -3
  110. package/dist/esm/liveErrors.native.js.map +1 -1
  111. package/dist/esm/measures.mjs +5 -0
  112. package/dist/esm/measures.mjs.map +1 -0
  113. package/dist/esm/measures.native.js +5 -0
  114. package/dist/esm/measures.native.js.map +1 -0
  115. package/dist/esm/recordShape.mjs +74 -0
  116. package/dist/esm/recordShape.mjs.map +1 -0
  117. package/dist/esm/recordShape.native.js +83 -0
  118. package/dist/esm/recordShape.native.js.map +1 -0
  119. package/dist/esm/report/ReportGrid.mjs +428 -0
  120. package/dist/esm/report/ReportGrid.mjs.map +1 -0
  121. package/dist/esm/report/ReportGrid.native.js +471 -0
  122. package/dist/esm/report/ReportGrid.native.js.map +1 -0
  123. package/dist/esm/report/ReportViewToolbar.mjs +228 -0
  124. package/dist/esm/report/ReportViewToolbar.mjs.map +1 -0
  125. package/dist/esm/report/ReportViewToolbar.native.js +264 -0
  126. package/dist/esm/report/ReportViewToolbar.native.js.map +1 -0
  127. package/dist/esm/report/queryReport.mjs +404 -0
  128. package/dist/esm/report/queryReport.mjs.map +1 -0
  129. package/dist/esm/report/queryReport.native.js +545 -0
  130. package/dist/esm/report/queryReport.native.js.map +1 -0
  131. package/dist/esm/report/queryReportColumns.mjs +105 -0
  132. package/dist/esm/report/queryReportColumns.mjs.map +1 -0
  133. package/dist/esm/report/queryReportColumns.native.js +119 -0
  134. package/dist/esm/report/queryReportColumns.native.js.map +1 -0
  135. package/dist/esm/report/reportViewModel.mjs +343 -0
  136. package/dist/esm/report/reportViewModel.mjs.map +1 -0
  137. package/dist/esm/report/reportViewModel.native.js +611 -0
  138. package/dist/esm/report/reportViewModel.native.js.map +1 -0
  139. package/dist/esm/report/savedReports.mjs +112 -0
  140. package/dist/esm/report/savedReports.mjs.map +1 -0
  141. package/dist/esm/report/savedReports.native.js +141 -0
  142. package/dist/esm/report/savedReports.native.js.map +1 -0
  143. package/dist/esm/report/useFrappeReport.mjs +158 -0
  144. package/dist/esm/report/useFrappeReport.mjs.map +1 -0
  145. package/dist/esm/report/useFrappeReport.native.js +185 -0
  146. package/dist/esm/report/useFrappeReport.native.js.map +1 -0
  147. package/dist/esm/report/useQueryReport.mjs +176 -0
  148. package/dist/esm/report/useQueryReport.mjs.map +1 -0
  149. package/dist/esm/report/useQueryReport.native.js +205 -0
  150. package/dist/esm/report/useQueryReport.native.js.map +1 -0
  151. package/dist/esm/useFrappeForm.mjs +533 -0
  152. package/dist/esm/useFrappeForm.mjs.map +1 -0
  153. package/dist/esm/useFrappeForm.native.js +686 -0
  154. package/dist/esm/useFrappeForm.native.js.map +1 -0
  155. package/dist/jsx/FrappeCalendar.mjs +4 -3
  156. package/dist/jsx/FrappeCalendar.mjs.map +1 -1
  157. package/dist/jsx/FrappeCalendar.native.js +3 -2
  158. package/dist/jsx/FrappeCalendar.native.js.map +1 -1
  159. package/dist/jsx/FrappeForm.mjs +151 -467
  160. package/dist/jsx/FrappeForm.mjs.map +1 -1
  161. package/dist/jsx/FrappeForm.native.js +152 -633
  162. package/dist/jsx/FrappeForm.native.js.map +1 -1
  163. package/dist/jsx/FrappeListView.mjs +6 -28
  164. package/dist/jsx/FrappeListView.mjs.map +1 -1
  165. package/dist/jsx/FrappeListView.native.js +11 -32
  166. package/dist/jsx/FrappeListView.native.js.map +1 -1
  167. package/dist/jsx/FrappeReportBuilder.mjs +526 -312
  168. package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
  169. package/dist/jsx/FrappeReportBuilder.native.js +658 -353
  170. package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
  171. package/dist/jsx/FrappeReportView.mjs +30 -229
  172. package/dist/jsx/FrappeReportView.mjs.map +1 -1
  173. package/dist/jsx/FrappeReportView.native.js +39 -283
  174. package/dist/jsx/FrappeReportView.native.js.map +1 -1
  175. package/dist/jsx/dependsOn.mjs +2 -2
  176. package/dist/jsx/dependsOn.mjs.map +1 -1
  177. package/dist/jsx/dependsOn.native.js +1 -2
  178. package/dist/jsx/dependsOn.native.js.map +1 -1
  179. package/dist/jsx/fields/shared/EditorPreviewPanes.mjs +2 -2
  180. package/dist/jsx/fields/shared/EditorPreviewPanes.mjs.map +1 -1
  181. package/dist/jsx/fields/shared/EditorPreviewPanes.native.js +2 -2
  182. package/dist/jsx/fields/shared/EditorPreviewPanes.native.js.map +1 -1
  183. package/dist/jsx/formEngine/docPermissions.mjs +19 -1
  184. package/dist/jsx/formEngine/docPermissions.mjs.map +1 -1
  185. package/dist/jsx/formEngine/docPermissions.native.js +69 -0
  186. package/dist/jsx/formEngine/docPermissions.native.js.map +1 -1
  187. package/dist/jsx/formEngine/index.mjs +2 -2
  188. package/dist/jsx/formEngine/index.mjs.map +1 -1
  189. package/dist/jsx/formEngine/index.native.js +2 -0
  190. package/dist/jsx/formEngine/index.native.js.map +1 -1
  191. package/dist/jsx/index.js +16 -4
  192. package/dist/jsx/index.js.map +1 -1
  193. package/dist/jsx/index.mjs +16 -4
  194. package/dist/jsx/index.mjs.map +1 -1
  195. package/dist/jsx/index.native.js +67 -7
  196. package/dist/jsx/index.native.js.map +1 -1
  197. package/dist/jsx/liveErrors.mjs +8 -3
  198. package/dist/jsx/liveErrors.mjs.map +1 -1
  199. package/dist/jsx/liveErrors.native.js +8 -2
  200. package/dist/jsx/liveErrors.native.js.map +1 -1
  201. package/dist/jsx/measures.mjs +5 -0
  202. package/dist/jsx/measures.mjs.map +1 -0
  203. package/dist/jsx/measures.native.js +34 -0
  204. package/dist/jsx/measures.native.js.map +1 -0
  205. package/dist/jsx/recordShape.mjs +74 -0
  206. package/dist/jsx/recordShape.mjs.map +1 -0
  207. package/dist/jsx/recordShape.native.js +115 -0
  208. package/dist/jsx/recordShape.native.js.map +1 -0
  209. package/dist/jsx/report/ReportGrid.mjs +428 -0
  210. package/dist/jsx/report/ReportGrid.mjs.map +1 -0
  211. package/dist/jsx/report/ReportGrid.native.js +510 -0
  212. package/dist/jsx/report/ReportGrid.native.js.map +1 -0
  213. package/dist/jsx/report/ReportViewToolbar.mjs +228 -0
  214. package/dist/jsx/report/ReportViewToolbar.mjs.map +1 -0
  215. package/dist/jsx/report/ReportViewToolbar.native.js +303 -0
  216. package/dist/jsx/report/ReportViewToolbar.native.js.map +1 -0
  217. package/dist/jsx/report/queryReport.mjs +404 -0
  218. package/dist/jsx/report/queryReport.mjs.map +1 -0
  219. package/dist/jsx/report/queryReport.native.js +588 -0
  220. package/dist/jsx/report/queryReport.native.js.map +1 -0
  221. package/dist/jsx/report/queryReportColumns.mjs +105 -0
  222. package/dist/jsx/report/queryReportColumns.mjs.map +1 -0
  223. package/dist/jsx/report/queryReportColumns.native.js +159 -0
  224. package/dist/jsx/report/queryReportColumns.native.js.map +1 -0
  225. package/dist/jsx/report/reportViewModel.mjs +343 -0
  226. package/dist/jsx/report/reportViewModel.mjs.map +1 -0
  227. package/dist/jsx/report/reportViewModel.native.js +657 -0
  228. package/dist/jsx/report/reportViewModel.native.js.map +1 -0
  229. package/dist/jsx/report/savedReports.mjs +112 -0
  230. package/dist/jsx/report/savedReports.mjs.map +1 -0
  231. package/dist/jsx/report/savedReports.native.js +169 -0
  232. package/dist/jsx/report/savedReports.native.js.map +1 -0
  233. package/dist/jsx/report/useFrappeReport.mjs +158 -0
  234. package/dist/jsx/report/useFrappeReport.mjs.map +1 -0
  235. package/dist/jsx/report/useFrappeReport.native.js +214 -0
  236. package/dist/jsx/report/useFrappeReport.native.js.map +1 -0
  237. package/dist/jsx/report/useQueryReport.mjs +176 -0
  238. package/dist/jsx/report/useQueryReport.mjs.map +1 -0
  239. package/dist/jsx/report/useQueryReport.native.js +233 -0
  240. package/dist/jsx/report/useQueryReport.native.js.map +1 -0
  241. package/dist/jsx/useFrappeForm.mjs +533 -0
  242. package/dist/jsx/useFrappeForm.mjs.map +1 -0
  243. package/dist/jsx/useFrappeForm.native.js +714 -0
  244. package/dist/jsx/useFrappeForm.native.js.map +1 -0
  245. package/package.json +16 -16
  246. package/src/FrappeCalendar.tsx +8 -2
  247. package/src/FrappeForm.stories.tsx +52 -0
  248. package/src/FrappeForm.tsx +176 -565
  249. package/src/FrappeListView.cards.stories.tsx +3 -6
  250. package/src/FrappeListView.tsx +15 -55
  251. package/src/FrappeReportBuilder.stories.tsx +34 -7
  252. package/src/FrappeReportBuilder.tsx +790 -380
  253. package/src/FrappeReportView.spec.tsx +10 -6
  254. package/src/FrappeReportView.stories.tsx +20 -1
  255. package/src/FrappeReportView.tsx +89 -415
  256. package/src/dependsOn.ts +4 -1
  257. package/src/fields/shared/EditorPreviewPanes.tsx +2 -2
  258. package/src/formEngine/docPermissions.spec.ts +36 -1
  259. package/src/formEngine/docPermissions.ts +34 -0
  260. package/src/formEngine/index.ts +8 -1
  261. package/src/frappeRemainder.designLaw.spec.ts +9 -1
  262. package/src/frappeRoutes.spec.tsx +9 -0
  263. package/src/frappeViews.designLaw.spec.ts +15 -2
  264. package/src/guestFallback.spec.tsx +236 -0
  265. package/src/index.ts +117 -16
  266. package/src/liveErrors.spec.ts +10 -0
  267. package/src/liveErrors.ts +16 -3
  268. package/src/measures.ts +11 -0
  269. package/src/recordShape.spec.ts +100 -0
  270. package/src/recordShape.ts +133 -0
  271. package/src/report/ReportGrid.spec.tsx +175 -0
  272. package/src/report/ReportGrid.tsx +530 -0
  273. package/src/report/ReportViewToolbar.spec.tsx +90 -0
  274. package/src/report/ReportViewToolbar.tsx +314 -0
  275. package/src/report/queryReport.spec.ts +483 -0
  276. package/src/report/queryReport.ts +722 -0
  277. package/src/report/queryReportColumns.tsx +123 -0
  278. package/src/report/reportViewModel.spec.ts +393 -0
  279. package/src/report/reportViewModel.ts +617 -0
  280. package/src/report/savedReports.ts +172 -0
  281. package/src/report/seam.spec.ts +216 -0
  282. package/src/report/useFrappeReport.ts +241 -0
  283. package/src/report/useQueryReport.spec.tsx +131 -0
  284. package/src/report/useQueryReport.ts +309 -0
  285. package/src/useFrappeForm.spec.tsx +209 -0
  286. package/src/useFrappeForm.ts +778 -0
  287. package/types/FrappeCalendar.d.ts.map +1 -1
  288. package/types/FrappeForm.d.ts +21 -1
  289. package/types/FrappeForm.d.ts.map +1 -1
  290. package/types/FrappeListView.d.ts.map +1 -1
  291. package/types/FrappeReportBuilder.d.ts +66 -16
  292. package/types/FrappeReportBuilder.d.ts.map +1 -1
  293. package/types/FrappeReportView.d.ts +9 -127
  294. package/types/FrappeReportView.d.ts.map +1 -1
  295. package/types/dependsOn.d.ts.map +1 -1
  296. package/types/formEngine/docPermissions.d.ts +14 -0
  297. package/types/formEngine/docPermissions.d.ts.map +1 -1
  298. package/types/formEngine/index.d.ts +1 -1
  299. package/types/formEngine/index.d.ts.map +1 -1
  300. package/types/index.d.ts +15 -3
  301. package/types/index.d.ts.map +1 -1
  302. package/types/liveErrors.d.ts +9 -1
  303. package/types/liveErrors.d.ts.map +1 -1
  304. package/types/measures.d.ts +3 -0
  305. package/types/measures.d.ts.map +1 -0
  306. package/types/recordShape.d.ts +45 -0
  307. package/types/recordShape.d.ts.map +1 -0
  308. package/types/report/ReportGrid.d.ts +20 -0
  309. package/types/report/ReportGrid.d.ts.map +1 -0
  310. package/types/report/ReportViewToolbar.d.ts +31 -0
  311. package/types/report/ReportViewToolbar.d.ts.map +1 -0
  312. package/types/report/queryReport.d.ts +181 -0
  313. package/types/report/queryReport.d.ts.map +1 -0
  314. package/types/report/queryReportColumns.d.ts +12 -0
  315. package/types/report/queryReportColumns.d.ts.map +1 -0
  316. package/types/report/reportViewModel.d.ts +146 -0
  317. package/types/report/reportViewModel.d.ts.map +1 -0
  318. package/types/report/savedReports.d.ts +27 -0
  319. package/types/report/savedReports.d.ts.map +1 -0
  320. package/types/report/useFrappeReport.d.ts +60 -0
  321. package/types/report/useFrappeReport.d.ts.map +1 -0
  322. package/types/report/useQueryReport.d.ts +37 -0
  323. package/types/report/useQueryReport.d.ts.map +1 -0
  324. package/types/useFrappeForm.d.ts +142 -0
  325. package/types/useFrappeForm.d.ts.map +1 -0
@@ -25,64 +25,168 @@ __export(FrappeReportBuilder_exports, {
25
25
  FrappeReportBuilder: () => FrappeReportBuilder
26
26
  });
27
27
  module.exports = __toCommonJS(FrappeReportBuilder_exports);
28
- var import_frappe = require("@multiplatform.one/frappe");
29
- var import_platform = require("@multiplatform.one/platform");
30
28
  var import_components = require("@multiplatform.one/components");
29
+ var import_frappe = require("@multiplatform.one/frappe");
31
30
  var import_forms = require("@multiplatform.one/forms");
31
+ var import_platform = require("@multiplatform.one/platform");
32
32
  var import_store = require("@multiplatform.one/store");
33
+ var import_table = require("@multiplatform.one/table");
34
+ var import_theme = require("@multiplatform.one/theme");
33
35
  var import_react = require("react");
34
36
  var import_react_i18next = require("react-i18next");
35
- var import_tamagui = require("tamagui");
36
- var import_react2 = require("@phosphor-icons/react");
37
- var import_theme = require("@multiplatform.one/theme");
37
+ var import_FrappeNotConfigured = require("./FrappeNotConfigured.cjs");
38
38
  var import_FrappeProvider = require("./FrappeProvider.cjs");
39
+ var import_liveErrors = require("./liveErrors.cjs");
40
+ var import_recordShape = require("./recordShape.cjs");
41
+ var import_ReportGrid = require("./report/ReportGrid.cjs");
42
+ var import_ReportViewToolbar = require("./report/ReportViewToolbar.cjs");
43
+ var import_reportViewModel = require("./report/reportViewModel.cjs");
44
+ var import_savedReports = require("./report/savedReports.cjs");
39
45
  var import_useFrappeRows = require("./useFrappeRows.cjs");
40
46
  var import_jsx_runtime = require("react/jsx-runtime");
41
- function getSavedReportsKey(doctype) {
42
- return `frappe-report-${doctype}`;
47
+ const EMPTY_FILTERS = [];
48
+ const COLUMN_FILTER_DEBOUNCE_MS = 350;
49
+ function columnsStorageKey(doctype) {
50
+ return `backoffice.report.columns.${doctype}`;
43
51
  }
44
- async function loadSavedReports(doctype) {
52
+ async function readStoredColumns(doctype) {
45
53
  try {
46
- const raw = await Promise.resolve(import_store.storage.getItem(getSavedReportsKey(doctype)));
47
- if (raw) return JSON.parse(raw);
54
+ const raw = await Promise.resolve(import_store.storage.getItem(columnsStorageKey(doctype)));
55
+ if (!raw) return void 0;
56
+ const parsed = JSON.parse(raw);
57
+ if (Array.isArray(parsed) && parsed.every(entry => typeof entry === "string")) {
58
+ return parsed;
59
+ }
48
60
  } catch {}
49
- return [];
61
+ return void 0;
50
62
  }
51
- async function persistSavedReports(doctype, reports) {
63
+ function persistStoredColumns(doctype, fieldnames) {
52
64
  try {
53
- await Promise.resolve(import_store.storage.setItem(getSavedReportsKey(doctype), JSON.stringify(reports)));
65
+ void Promise.resolve(import_store.storage.setItem(columnsStorageKey(doctype), JSON.stringify(fieldnames)));
54
66
  } catch {}
55
67
  }
68
+ function toReportFilters(filters) {
69
+ if (!filters) return void 0;
70
+ if (Array.isArray(filters)) return filters.length > 0 ? filters : void 0;
71
+ const tuples = Object.entries(filters).map(([field, value]) => [field, "=", value]);
72
+ return tuples.length > 0 ? tuples : void 0;
73
+ }
74
+ function initialFieldnames(columns) {
75
+ if (!columns) return null;
76
+ const fieldnames = columns.flatMap(column => typeof column === "string" ? [column] : column.enabled ? [column.fieldname] : []);
77
+ return fieldnames.length > 0 ? fieldnames : null;
78
+ }
79
+ function asFieldType(fieldtype) {
80
+ return import_frappe.frappeFieldTypes.find(known => known === fieldtype) ?? "Data";
81
+ }
82
+ function refusedMetaFields(initialColumns, recordFields) {
83
+ const declared = (initialColumns ?? []).map(column => typeof column === "string" ? {
84
+ fieldname: column,
85
+ label: (0, import_recordShape.recordKeyLabel)(column),
86
+ fieldtype: "Data"
87
+ } : {
88
+ fieldname: column.fieldname,
89
+ label: column.label,
90
+ fieldtype: asFieldType(column.fieldtype)
91
+ });
92
+ return (declared.length > 0 ? declared : recordFields ?? []).filter(field => field.fieldname !== "name").map(field => ({
93
+ ...field,
94
+ read_only: 1,
95
+ in_list_view: 1
96
+ }));
97
+ }
98
+ function asError(error) {
99
+ return error instanceof Error ? error : new Error(String(error));
100
+ }
101
+ function groupField(groupBy) {
102
+ if (!groupBy) return null;
103
+ return typeof groupBy === "string" ? groupBy : groupBy.field;
104
+ }
105
+ function aggregateGroups(rows, field, config) {
106
+ const groups = /* @__PURE__ */new Map();
107
+ for (const row of rows) {
108
+ const key = row[field] === null || row[field] === void 0 ? "" : String(row[field]);
109
+ const bucket = groups.get(key);
110
+ if (bucket) bucket.push(row);else groups.set(key, [row]);
111
+ }
112
+ const numbers = group => group.map(row => Number(config?.aggregationField ? row[config.aggregationField] : 0) || 0);
113
+ return [...groups.entries()].map(([label, group]) => {
114
+ switch (config?.aggregationField ? config.aggregation : "count") {
115
+ case "sum":
116
+ return {
117
+ label,
118
+ value: numbers(group).reduce((sum, value) => sum + value, 0)
119
+ };
120
+ case "avg":
121
+ return {
122
+ label,
123
+ value: numbers(group).reduce((sum, value) => sum + value, 0) / group.length
124
+ };
125
+ case "min":
126
+ return {
127
+ label,
128
+ value: Math.min(...numbers(group))
129
+ };
130
+ case "max":
131
+ return {
132
+ label,
133
+ value: Math.max(...numbers(group))
134
+ };
135
+ default:
136
+ return {
137
+ label,
138
+ value: group.length
139
+ };
140
+ }
141
+ });
142
+ }
56
143
  function canDownloadCsv() {
57
144
  return typeof document !== "undefined" && typeof document.createElement === "function" && typeof Blob !== "undefined" && typeof URL !== "undefined" && typeof URL.createObjectURL === "function";
58
145
  }
59
- function FrappeReportBuilder({
146
+ function FrappeReportBuilder(props) {
147
+ const config = (0, import_FrappeProvider.useFrappeConfig)();
148
+ if (!(props.baseURL ?? config?.baseURL) && !config?.fixtures) {
149
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_FrappeNotConfigured.FrappeNotConfigured, {
150
+ doctype: props.doctype
151
+ });
152
+ }
153
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(FrappeReportBuilderBody, {
154
+ ...props
155
+ });
156
+ }
157
+ function FrappeReportBuilderBody({
60
158
  doctype,
159
+ filters,
160
+ scopeFilters,
161
+ onClearFilters,
61
162
  initialColumns,
62
163
  initialGroupBy,
63
- filters,
64
164
  showChart = false,
65
- onExport,
165
+ onOpenRow,
66
166
  onRowPress,
167
+ onOpenLink,
168
+ report: reportProp,
169
+ onReportChange,
170
+ onExport,
171
+ loading: loadingFallback,
172
+ error: errorFallback,
67
173
  baseURL,
68
174
  auth,
69
175
  syncOptions,
70
176
  ...stackProps
71
177
  }) {
72
- const config = (0, import_FrappeProvider.useFrappeConfig)();
73
- const {
74
- knobProps
75
- } = (0, import_theme.useResolvedKnobs)();
76
178
  const {
77
179
  t
78
180
  } = (0, import_react_i18next.useTranslation)();
181
+ const {
182
+ knobProps
183
+ } = (0, import_theme.useResolvedKnobs)();
184
+ const config = (0, import_FrappeProvider.useFrappeConfig)();
79
185
  const finalBaseURL = baseURL ?? config?.baseURL;
80
186
  const finalAuth = auth ?? config?.auth;
81
187
  const finalSyncOptions = syncOptions ?? config?.syncOptions;
82
- if (!finalBaseURL && !config?.fixtures) {
83
- throw new Error("FrappeReportBuilder requires baseURL prop or FrappeProvider with baseURL or fixtures");
84
- }
85
- const connectionConfig = {
188
+ const fixtureMode = Boolean(config?.fixtures);
189
+ const connection = (0, import_react.useMemo)(() => ({
86
190
  baseURL: finalBaseURL,
87
191
  socketPort: config?.socketPort,
88
192
  siteName: config?.siteName,
@@ -91,329 +195,439 @@ function FrappeReportBuilder({
91
195
  syncOptions: finalSyncOptions,
92
196
  realtime: config?.realtime,
93
197
  fixtures: config?.fixtures
94
- };
198
+ }), [finalBaseURL, config?.socketPort, config?.siteName, config?.socketBaseURL, finalAuth, finalSyncOptions, config?.realtime, config?.fixtures]);
95
199
  const {
96
200
  meta,
97
- isLoading: isMetaLoading,
201
+ isLoading: metaLoading,
98
202
  error: metaError
99
- } = (0, import_frappe.useDocTypeMeta)(connectionConfig, doctype);
203
+ } = (0, import_frappe.useDocTypeMeta)(connection, doctype);
204
+ const metaRefused = !meta && metaError !== void 0 && (0, import_liveErrors.isPermissionError)(metaError);
205
+ const initialColumnsJson = JSON.stringify(initialColumns ?? []);
206
+ const declaresColumns = initialColumnsJson !== "[]";
207
+ const recordShape = (0, import_recordShape.useRecordShape)(connection, doctype, metaRefused && !declaresColumns);
208
+ const layoutMeta = (0, import_react.useMemo)(() => {
209
+ if (meta) return meta;
210
+ if (!metaRefused) return void 0;
211
+ const fields = refusedMetaFields(JSON.parse(initialColumnsJson), recordShape.fields);
212
+ return fields.length > 0 ? {
213
+ fields
214
+ } : void 0;
215
+ }, [meta, metaRefused, initialColumnsJson, recordShape.fields]);
216
+ const readsWithoutMeta = !meta && layoutMeta !== void 0;
217
+ const savedReports = (0, import_savedReports.useSavedReports)(doctype, (0, import_react.useMemo)(() => ({
218
+ baseURL: metaRefused ? "" : connection.baseURL ?? "",
219
+ auth: connection.auth,
220
+ fixtures: connection.fixtures
221
+ }), [metaRefused, connection.baseURL, connection.auth, connection.fixtures]));
222
+ const [internalReport, setInternalReport] = (0, import_react.useState)(null);
223
+ const currentReport = reportProp !== void 0 ? reportProp : internalReport;
100
224
  const allColumns = (0, import_react.useMemo)(() => {
101
- if (initialColumns) return initialColumns;
102
- if (!meta) return [];
103
- return meta.fields.filter(f => f.fieldtype !== "Section Break" && f.fieldtype !== "Column Break" && f.fieldtype !== "Tab Break").map(f => ({
104
- fieldname: f.fieldname,
105
- label: f.label || f.fieldname,
106
- fieldtype: f.fieldtype,
107
- enabled: !!f.in_list_view
108
- }));
109
- }, [initialColumns, meta]);
110
- const fetchFields = (0, import_react.useMemo)(() => {
111
- const fieldNames = allColumns.map(c => c.fieldname);
112
- if (!fieldNames.includes("name")) {
113
- fieldNames.unshift("name");
114
- }
115
- return fieldNames;
116
- }, [allColumns]);
117
- const {
118
- rows,
119
- isQueryLoading: isLoading,
120
- isError
121
- } = (0, import_useFrappeRows.useFrappeRows)(connectionConfig, doctype, {
122
- fields: fetchFields,
123
- filters,
124
- limit: null
125
- });
126
- const data = rows;
127
- const [isLiveStale, setIsLiveStale] = (0, import_react.useState)(false);
128
- const staleTimerRef = (0, import_react.useRef)(void 0);
129
- const clearStaleTimer = (0, import_react.useCallback)(() => {
130
- if (staleTimerRef.current) {
131
- clearTimeout(staleTimerRef.current);
132
- staleTimerRef.current = void 0;
133
- }
134
- }, []);
135
- (0, import_frappe.useFrappeDoctypeEvents)(connectionConfig, doctype, event => {
136
- if (event.type !== "doc_update" && event.type !== "list_update" && event.type !== "doc_deleted" && event.type !== "doc_changed") {
137
- return;
138
- }
139
- setIsLiveStale(true);
140
- clearStaleTimer();
141
- staleTimerRef.current = setTimeout(() => setIsLiveStale(false), 2500);
142
- });
143
- const isFirstData = (0, import_react.useRef)(true);
225
+ const columns = (0, import_reportViewModel.reportColumnsFromMeta)(layoutMeta);
226
+ if (!readsWithoutMeta || !layoutMeta) return columns;
227
+ const own = new Set(layoutMeta.fields.map(field => field.fieldname));
228
+ return columns.filter(column => column.fieldname === "name" || own.has(column.fieldname));
229
+ }, [layoutMeta, readsWithoutMeta]);
230
+ const defaultFieldnames = (0, import_react.useMemo)(() => readsWithoutMeta && layoutMeta ? ["name", ...layoutMeta.fields.map(field => field.fieldname)] : (0, import_reportViewModel.defaultReportFieldnames)(layoutMeta), [layoutMeta, readsWithoutMeta]);
231
+ const initialColumnsKey = JSON.stringify(initialFieldnames(initialColumns));
232
+ const startFieldnames = (0, import_react.useMemo)(() => JSON.parse(initialColumnsKey), [initialColumnsKey]);
233
+ const [storedColumns, setStoredColumns] = (0, import_react.useState)(void 0);
234
+ const [selectedOverride, setSelectedOverride] = (0, import_react.useState)(null);
144
235
  (0, import_react.useEffect)(() => {
145
- if (isFirstData.current) {
146
- isFirstData.current = false;
147
- return;
148
- }
149
- setIsLiveStale(false);
150
- clearStaleTimer();
151
- }, [data, clearStaleTimer]);
152
- (0, import_react.useEffect)(() => clearStaleTimer, [clearStaleTimer]);
153
- const [groupBy, setGroupBy] = (0, import_react.useState)(initialGroupBy ?? null);
154
- const [enabledColumns, setEnabledColumns] = (0, import_react.useState)([]);
236
+ if (fixtureMode) return;
237
+ let cancelled = false;
238
+ void readStoredColumns(doctype).then(stored => {
239
+ if (!cancelled) setStoredColumns(stored);
240
+ });
241
+ return () => {
242
+ cancelled = true;
243
+ };
244
+ }, [doctype, fixtureMode]);
245
+ const selectedFieldnames = selectedOverride ?? startFieldnames ?? storedColumns ?? defaultFieldnames;
246
+ const selectedColumns = (0, import_react.useMemo)(() => (0, import_reportViewModel.resolveSelectedColumns)(allColumns, selectedFieldnames), [allColumns, selectedFieldnames]);
247
+ const filtersKey = JSON.stringify(filters ?? null);
248
+ const propFilters = (0, import_react.useMemo)(() => toReportFilters(JSON.parse(filtersKey)), [filtersKey]);
249
+ const [filtersCleared, setFiltersCleared] = (0, import_react.useState)(false);
250
+ const [reportFilters, setReportFilters] = (0, import_react.useState)(null);
251
+ const baseFilters = filtersCleared ? EMPTY_FILTERS : reportFilters ?? propFilters ?? EMPTY_FILTERS;
252
+ const [filterDrafts, setFilterDrafts] = (0, import_react.useState)({});
253
+ const [committedFilters, setCommittedFilters] = (0, import_react.useState)({});
155
254
  (0, import_react.useEffect)(() => {
156
- if (allColumns.length > 0 && enabledColumns.length === 0) {
157
- const initial = allColumns.filter(c => c.enabled).map(c => c.fieldname);
158
- if (initial.length === 0) {
159
- setEnabledColumns(allColumns.slice(0, 5).map(c => c.fieldname));
160
- } else {
161
- setEnabledColumns(initial);
162
- }
163
- }
164
- }, [allColumns, enabledColumns.length]);
165
- const [savedReports, setSavedReports] = (0, import_react.useState)([]);
166
- const [saveDialogOpen, setSaveDialogOpen] = (0, import_react.useState)(false);
167
- const [saveReportName, setSaveReportName] = (0, import_react.useState)("");
255
+ const handle = setTimeout(() => {
256
+ setCommittedFilters(previous => JSON.stringify(previous) === JSON.stringify(filterDrafts) ? previous : filterDrafts);
257
+ }, COLUMN_FILTER_DEBOUNCE_MS);
258
+ return () => clearTimeout(handle);
259
+ }, [filterDrafts]);
260
+ const columnTuples = (0, import_react.useMemo)(() => (0, import_reportViewModel.columnFilterTuples)(selectedColumns, committedFilters), [selectedColumns, committedFilters]);
261
+ const scopeKey = JSON.stringify(scopeFilters ?? []);
262
+ const scope = (0, import_react.useMemo)(() => JSON.parse(scopeKey), [scopeKey]);
263
+ const effectiveFilters = (0, import_react.useMemo)(() => [...scope, ...baseFilters, ...columnTuples], [scope, baseFilters, columnTuples]);
264
+ const metaSort = (0, import_react.useMemo)(() => ({
265
+ field: (meta?.sort_field || "modified").split(" ")[0],
266
+ order: (meta?.sort_order || "DESC").toLowerCase() === "asc" ? "asc" : "desc"
267
+ }), [meta?.sort_field, meta?.sort_order]);
268
+ const [sortOverride, setSortOverride] = (0, import_react.useState)(null);
269
+ const sort = sortOverride ?? metaSort;
270
+ const startGroupBy = groupField(initialGroupBy);
271
+ const chartConfig = typeof initialGroupBy === "object" ? initialGroupBy : void 0;
272
+ const [groupBy, setGroupBy] = (0, import_react.useState)(startGroupBy);
273
+ const [pageSize, setPageSize] = (0, import_react.useState)(import_reportViewModel.DEFAULT_REPORT_PAGE_SIZE);
274
+ const [page, setPage] = (0, import_react.useState)(0);
275
+ (0, import_react.useEffect)(() => {
276
+ setSelectedOverride(null);
277
+ setStoredColumns(void 0);
278
+ setSortOverride(null);
279
+ setGroupBy(startGroupBy);
280
+ setPage(0);
281
+ setPageSize(import_reportViewModel.DEFAULT_REPORT_PAGE_SIZE);
282
+ setFilterDrafts({});
283
+ setCommittedFilters({});
284
+ setFiltersCleared(false);
285
+ setReportFilters(null);
286
+ }, [doctype]);
287
+ (0, import_react.useEffect)(() => setFiltersCleared(false), [filtersKey]);
288
+ const effectiveFiltersKey = (0, import_react.useMemo)(() => JSON.stringify(effectiveFilters), [effectiveFilters]);
289
+ (0, import_react.useEffect)(() => {
290
+ setPage(0);
291
+ }, [effectiveFiltersKey, pageSize, groupBy]);
168
292
  (0, import_react.useEffect)(() => {
293
+ if (!currentReport) {
294
+ setReportFilters(null);
295
+ return;
296
+ }
169
297
  let cancelled = false;
170
- void (async () => {
171
- const reports = await loadSavedReports(doctype);
172
- if (!cancelled) {
173
- setSavedReports(reports);
298
+ void savedReports.load(currentReport).then(saved => {
299
+ if (cancelled || !saved) return;
300
+ setSelectedOverride(saved.fieldnames.length > 0 ? saved.fieldnames : null);
301
+ setReportFilters(saved.filters);
302
+ setFilterDrafts({});
303
+ setCommittedFilters({});
304
+ setFiltersCleared(false);
305
+ if (saved.sort) setSortOverride(saved.sort);
306
+ if (saved.pageSize && import_reportViewModel.REPORT_PAGE_SIZES.some(size => size === saved.pageSize)) {
307
+ setPageSize(saved.pageSize);
174
308
  }
175
- })();
309
+ setGroupBy(saved.groupBy ?? null);
310
+ });
176
311
  return () => {
177
312
  cancelled = true;
178
313
  };
179
- }, [doctype]);
180
- const handleSaveReport = (0, import_react.useCallback)(() => {
181
- if (!saveReportName.trim()) return;
182
- const report = {
183
- name: saveReportName.trim(),
184
- columns: enabledColumns,
314
+ }, [currentReport, doctype]);
315
+ const handleToggleColumn = (0, import_react.useCallback)(fieldname => {
316
+ setSelectedOverride(previous => {
317
+ const current = previous ?? startFieldnames ?? storedColumns ?? defaultFieldnames;
318
+ const next = current.includes(fieldname) ? current.length === 1 ? current : current.filter(entry => entry !== fieldname) : [...current, fieldname];
319
+ if (!fixtureMode && !currentReport) persistStoredColumns(doctype, next);
320
+ return next;
321
+ });
322
+ }, [startFieldnames, storedColumns, defaultFieldnames, fixtureMode, currentReport, doctype]);
323
+ const handleResetColumns = (0, import_react.useCallback)(() => {
324
+ setSelectedOverride(defaultFieldnames);
325
+ setStoredColumns(void 0);
326
+ if (!fixtureMode) persistStoredColumns(doctype, defaultFieldnames);
327
+ }, [defaultFieldnames, fixtureMode, doctype]);
328
+ const fetchFields = (0, import_react.useMemo)(() => {
329
+ const fields = /* @__PURE__ */new Set(["name", sort.field]);
330
+ for (const column of selectedColumns) fields.add(column.fieldname);
331
+ if (groupBy) fields.add(groupBy);
332
+ if (chartConfig?.aggregationField) fields.add(chartConfig.aggregationField);
333
+ return [...fields];
334
+ }, [selectedColumns, sort.field, groupBy, chartConfig?.aggregationField]);
335
+ const orderBy = (0, import_react.useMemo)(() => [{
336
+ field: sort.field,
337
+ order: sort.order === "asc" ? import_frappe.Order.Asc : import_frappe.Order.Desc
338
+ }], [sort.field, sort.order]);
339
+ const {
340
+ rows: rowsData,
341
+ syncError,
342
+ isLoading: rowsLoading
343
+ } = (0, import_useFrappeRows.useFrappeRows)(connection, doctype, {
344
+ fields: fetchFields,
345
+ filters: effectiveFilters,
346
+ orderBy,
347
+ limit: null,
348
+ enabled: layoutMeta !== void 0,
349
+ mapError: asError
350
+ });
351
+ const orderedRows = (0, import_react.useMemo)(() => (0, import_reportViewModel.orderReportRows)(rowsData, sort, groupBy), [rowsData, sort, groupBy]);
352
+ const total = orderedRows.length;
353
+ const pageCount = Math.max(1, Math.ceil(total / pageSize));
354
+ const effectivePage = Math.min(page, pageCount - 1);
355
+ const gridRows = (0, import_react.useMemo)(() => (0, import_reportViewModel.buildGridRows)(orderedRows, effectivePage * pageSize, pageSize, groupBy), [orderedRows, effectivePage, pageSize, groupBy]);
356
+ const totals = (0, import_react.useMemo)(() => (0, import_reportViewModel.computeTotals)(selectedColumns, orderedRows), [selectedColumns, orderedRows]);
357
+ const groupByColumn = groupBy ? allColumns.find(column => column.fieldname === groupBy) : void 0;
358
+ const chartData = (0, import_react.useMemo)(() => showChart && groupBy ? aggregateGroups(orderedRows, groupBy, chartConfig) : [], [showChart, groupBy, orderedRows, chartConfig]);
359
+ const rowsPressable = Boolean(onOpenRow || onRowPress);
360
+ const handleOpenRow = (0, import_react.useCallback)(name => {
361
+ onOpenRow?.(name);
362
+ if (onRowPress) {
363
+ const index = orderedRows.findIndex(row => row.name === name);
364
+ if (index >= 0) onRowPress(orderedRows[index], index);
365
+ }
366
+ }, [onOpenRow, onRowPress, orderedRows]);
367
+ const handleSelectReport = (0, import_react.useCallback)(name => {
368
+ if (reportProp === void 0) setInternalReport(name);
369
+ onReportChange?.(name);
370
+ if (!name) {
371
+ setReportFilters(null);
372
+ setSelectedOverride(null);
373
+ setSortOverride(null);
374
+ setGroupBy(null);
375
+ }
376
+ }, [reportProp, onReportChange]);
377
+ const handleClearBaseFilters = (0, import_react.useCallback)(() => {
378
+ setFiltersCleared(true);
379
+ if (propFilters && !reportFilters) onClearFilters?.();
380
+ }, [propFilters, reportFilters, onClearFilters]);
381
+ const handleSaveReport = (0, import_react.useCallback)(name => {
382
+ void savedReports.save(name, {
383
+ fieldnames: [...selectedFieldnames],
384
+ filters: effectiveFilters,
385
+ sort,
386
+ pageSize,
185
387
  groupBy,
186
- filters
187
- };
188
- const updated = [...savedReports.filter(r => r.name !== report.name), report];
189
- setSavedReports(updated);
190
- void persistSavedReports(doctype, updated);
191
- setSaveDialogOpen(false);
192
- setSaveReportName("");
193
- }, [saveReportName, enabledColumns, groupBy, filters, savedReports, doctype]);
194
- const handleLoadReport = (0, import_react.useCallback)(report => {
195
- setEnabledColumns(report.columns);
196
- setGroupBy(report.groupBy);
197
- }, []);
388
+ addTotals: totals.length > 0
389
+ }).then(savedName => {
390
+ (0, import_components.notify)({
391
+ severity: "success",
392
+ scope: "page",
393
+ body: t("report.saveDone", {
394
+ defaultValue: "Saved report \u201C{{name}}\u201D",
395
+ name: savedName
396
+ })
397
+ });
398
+ if (savedName !== currentReport) handleSelectReport(savedName);
399
+ }).catch(error => {
400
+ (0, import_components.notify)({
401
+ severity: "error",
402
+ scope: "page",
403
+ body: error instanceof Error ? error.message : t("report.saveFailed", "Could not save the report")
404
+ });
405
+ });
406
+ }, [savedReports, selectedFieldnames, effectiveFilters, sort, pageSize, groupBy, totals.length, currentReport, handleSelectReport, t]);
198
407
  const handleDeleteReport = (0, import_react.useCallback)(name => {
199
- const updated = savedReports.filter(r => r.name !== name);
200
- setSavedReports(updated);
201
- void persistSavedReports(doctype, updated);
202
- }, [savedReports, doctype]);
203
- const handleExport = (0, import_react.useCallback)(exportData => {
408
+ void savedReports.remove(name).then(() => {
409
+ (0, import_components.notify)({
410
+ severity: "success",
411
+ scope: "page",
412
+ body: t("report.deleteDone", {
413
+ defaultValue: "Deleted report \u201C{{name}}\u201D",
414
+ name
415
+ })
416
+ });
417
+ if (name === currentReport) handleSelectReport(null);
418
+ }).catch(error => {
419
+ (0, import_components.notify)({
420
+ severity: "error",
421
+ scope: "page",
422
+ body: error instanceof Error ? error.message : t("report.deleteFailed", "Could not delete the report")
423
+ });
424
+ });
425
+ }, [savedReports, currentReport, handleSelectReport, t]);
426
+ const exportEnabled = Boolean(onExport) || canDownloadCsv();
427
+ const handleExport = (0, import_react.useCallback)(() => {
204
428
  if (onExport) {
205
- onExport(exportData);
429
+ onExport(orderedRows);
206
430
  return;
207
431
  }
208
- const cols = allColumns.filter(c => enabledColumns.includes(c.fieldname));
209
- const headers = cols.map(c => c.label);
210
- const csvRows = [headers.join(",")];
211
- for (const row of exportData) {
212
- const values = cols.map(col => {
213
- const val = row[col.fieldname];
214
- if (val === null || val === void 0) return "";
215
- const str = String(val);
216
- if (str.includes(",") || str.includes('"') || str.includes("\n")) {
217
- return `"${str.replace(/"/g, '""')}"`;
218
- }
219
- return str;
220
- });
221
- csvRows.push(values.join(","));
222
- }
223
- const csvContent = csvRows.join("\n");
224
- const blob = new Blob([csvContent], {
432
+ (0, import_platform.downloadBlob)(new Blob([(0, import_reportViewModel.buildReportCsv)(selectedColumns, orderedRows)], {
225
433
  type: "text/csv;charset=utf-8;"
434
+ }), `${doctype}_report.csv`);
435
+ (0, import_components.notify)({
436
+ severity: "success",
437
+ scope: "page",
438
+ body: t("report.exportDone", {
439
+ count: orderedRows.length,
440
+ defaultValue_one: "Exported {{count}} row to CSV",
441
+ defaultValue_other: "Exported {{count}} rows to CSV"
442
+ })
226
443
  });
227
- (0, import_platform.downloadBlob)(blob, `${doctype}_report.csv`);
228
- }, [allColumns, enabledColumns, onExport, doctype]);
229
- const exportEnabled = Boolean(onExport) || canDownloadCsv();
230
- if (isMetaLoading) {
231
- return /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.YStack, {
232
- ...knobProps.panelPadding,
233
- alignItems: "center",
234
- justifyContent: "center",
235
- gap: "$2",
236
- ...stackProps,
237
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Spinner, {
238
- size: "large"
239
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
240
- color: "$color10",
241
- children: t("Loading {{doctype}} schema...", {
242
- doctype
444
+ }, [onExport, selectedColumns, orderedRows, doctype, t]);
445
+ const handleColumnFilterChange = (0, import_react.useCallback)((fieldname, value) => {
446
+ setFilterDrafts(previous => {
447
+ if (value === "") {
448
+ if (!(fieldname in previous)) return previous;
449
+ const {
450
+ [fieldname]: _dropped,
451
+ ...rest
452
+ } = previous;
453
+ return rest;
454
+ }
455
+ return {
456
+ ...previous,
457
+ [fieldname]: value
458
+ };
459
+ });
460
+ }, []);
461
+ const handleClearColumnFilters = (0, import_react.useCallback)(() => {
462
+ setFilterDrafts({});
463
+ setCommittedFilters({});
464
+ }, []);
465
+ if (metaLoading && !meta) {
466
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {
467
+ children: loadingFallback ?? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.YStack, {
468
+ flex: 1,
469
+ alignItems: "center",
470
+ justifyContent: "center",
471
+ ...stackProps,
472
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.Spinner, {
473
+ size: "large"
243
474
  })
244
- })]
475
+ })
245
476
  });
246
477
  }
247
- if (metaError && !meta) {
248
- return /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.YStack, {
249
- ...knobProps.panelPadding,
250
- alignItems: "center",
251
- ...knobProps.gap,
252
- ...stackProps,
253
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
254
- color: "$red11",
255
- children: t("Failed to load {{doctype}} schema", {
256
- doctype
478
+ if (metaError && metaRefused && !layoutMeta) {
479
+ if (recordShape.status === "pending") {
480
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {
481
+ children: loadingFallback ?? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.YStack, {
482
+ flex: 1,
483
+ alignItems: "center",
484
+ justifyContent: "center",
485
+ ...stackProps,
486
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.Spinner, {
487
+ size: "large"
488
+ })
257
489
  })
258
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
259
- color: "$color10",
260
- fontSize: "$2",
261
- children: metaError.message
262
- })]
490
+ });
491
+ }
492
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {
493
+ children: errorFallback ? errorFallback(metaError) : /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.EmptyState, {
494
+ testID: "frappe-report-not-permitted",
495
+ title: t("report.notPermitted", {
496
+ defaultValue: "You can't open the {{doctype}} report",
497
+ doctype
498
+ }),
499
+ description: (0, import_liveErrors.refusalReason)(metaError)
500
+ })
263
501
  });
264
502
  }
265
- if (isLoading) {
266
- return /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.YStack, {
267
- ...knobProps.panelPadding,
268
- alignItems: "center",
269
- gap: "$1",
270
- ...stackProps,
271
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Spinner, {
272
- size: "small"
273
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
274
- color: "$color10",
275
- fontSize: "$2",
276
- children: t("Loading data...")
277
- })]
503
+ if (metaError && !meta && !readsWithoutMeta) {
504
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {
505
+ children: errorFallback ? errorFallback(metaError) : /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.EmptyState, {
506
+ intent: "error",
507
+ title: t("report.metaFailed", {
508
+ defaultValue: "Couldn't load the {{doctype}} report",
509
+ doctype
510
+ }),
511
+ description: metaError.message
512
+ })
278
513
  });
279
514
  }
280
- if (isError) {
281
- return /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.YStack, {
282
- ...knobProps.panelPadding,
515
+ let body;
516
+ if (syncError && (0, import_liveErrors.isPermissionError)(syncError)) {
517
+ body = /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.EmptyState, {
518
+ testID: "frappe-report-not-permitted",
519
+ title: t("report.notPermitted", {
520
+ defaultValue: "You can't open the {{doctype}} report",
521
+ doctype
522
+ }),
523
+ description: (0, import_liveErrors.refusalReason)(syncError)
524
+ });
525
+ } else if (syncError) {
526
+ body = /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.EmptyState, {
527
+ intent: "error",
528
+ title: t("report.loadFailed", "Couldn't load report rows"),
529
+ description: syncError.message
530
+ });
531
+ } else if (rowsLoading && total === 0) {
532
+ body = /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.YStack, {
533
+ flex: 1,
283
534
  alignItems: "center",
535
+ justifyContent: "center",
536
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.Spinner, {
537
+ size: "large"
538
+ })
539
+ });
540
+ } else if (total === 0) {
541
+ const hasFilters = effectiveFilters.length > 0;
542
+ body = /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.EmptyState, {
543
+ title: t("report.emptyTitle", "No results"),
544
+ description: hasFilters ? t("report.emptyFiltered", "No rows match the current filters") : t("report.emptyDescription", {
545
+ defaultValue: "There are no {{doctype}} records yet",
546
+ doctype
547
+ }),
548
+ action: hasFilters ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
549
+ size: "$3",
550
+ onPress: () => {
551
+ handleClearColumnFilters();
552
+ handleClearBaseFilters();
553
+ },
554
+ children: t("report.clearAllFilters", "Clear all filters")
555
+ }) : void 0
556
+ });
557
+ } else {
558
+ body = /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_components.YStack, {
559
+ flex: 1,
560
+ minWidth: 0,
284
561
  ...knobProps.gap,
285
- ...stackProps,
286
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
287
- color: "$red11",
288
- children: t("Failed to load {{doctype}} data", {
289
- doctype
562
+ children: [chartData.length > 0 && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.YStack, {
563
+ ...knobProps.surface,
564
+ ...knobProps.containerRadius,
565
+ ...knobProps.panelPadding,
566
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.BarChart, {
567
+ data: chartData
290
568
  })
291
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
292
- color: "$color10",
293
- fontSize: "$2",
294
- children: t("The live query could not load report rows.")
569
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_ReportGrid.ReportGrid, {
570
+ columns: selectedColumns,
571
+ rows: gridRows,
572
+ sort,
573
+ onSortChange: setSortOverride,
574
+ columnFilters: filterDrafts,
575
+ onColumnFilterChange: handleColumnFilterChange,
576
+ onOpenRow: rowsPressable ? handleOpenRow : void 0,
577
+ onOpenLink,
578
+ totals,
579
+ groupByColumn
295
580
  })]
296
581
  });
297
582
  }
298
- return /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.YStack, {
583
+ const content = /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_components.YStack, {
299
584
  flex: 1,
300
- ...knobProps.borderRadius,
585
+ minWidth: 0,
301
586
  ...stackProps,
302
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.XStack, {
303
- ...knobProps.panelPadding,
304
- ...knobProps.surface,
305
- ...knobProps.gap,
306
- alignItems: "center",
307
- borderBottomWidth: 1,
308
- borderBottomColor: "$borderColor",
309
- children: [isLiveStale ? /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.XStack, {
310
- gap: "$2",
311
- alignItems: "center",
312
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Spinner, {
313
- size: "small",
314
- "aria-label": t("Updating")
315
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
316
- ...knobProps.label,
317
- color: import_forms.formCommonColors.muted,
318
- children: t("Updating...")
319
- })]
320
- }) : null, /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.XStack, {
321
- flex: 1
322
- }), /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.Popover, {
323
- placement: "bottom-end",
324
- open: saveDialogOpen,
325
- onOpenChange: setSaveDialogOpen,
326
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Popover.Trigger, {
327
- asChild: true,
328
- children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
329
- size: "$2",
330
- icon: import_react2.FloppyDiskIcon,
331
- children: t("Save")
332
- })
333
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.PopoverContent, {
334
- children: /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.YStack, {
335
- ...knobProps.gap,
336
- minWidth: 220,
337
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
338
- ...knobProps.label,
339
- fontWeight: "400",
340
- children: t("Save Report")
341
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Input, {
342
- label: t("Report name"),
343
- labelHidden: true,
344
- placeholder: t("Report name"),
345
- value: saveReportName,
346
- onChangeText: setSaveReportName,
347
- inputProps: {
348
- onSubmitEditing: handleSaveReport
349
- }
350
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
351
- size: "$2",
352
- onPress: handleSaveReport,
353
- disabled: !saveReportName.trim(),
354
- children: t("Save")
355
- })]
356
- })
357
- })]
358
- }), savedReports.length > 0 && /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.Popover, {
359
- placement: "bottom-end",
360
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Popover.Trigger, {
361
- asChild: true,
362
- children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
363
- size: "$2",
364
- icon: import_react2.FolderOpenIcon,
365
- children: t("Load")
366
- })
367
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.PopoverContent, {
368
- children: /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.YStack, {
369
- ...knobProps.gap,
370
- minWidth: 180,
371
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
372
- ...knobProps.label,
373
- fontWeight: "400",
374
- color: import_forms.formCommonColors.muted,
375
- children: t("Saved Reports")
376
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.Separator, {}), savedReports.map(report => /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.XStack, {
377
- alignItems: "center",
378
- justifyContent: "space-between",
379
- padding: "$2",
380
- ...knobProps.containerRadius,
381
- hoverStyle: {
382
- backgroundColor: "$color2"
383
- },
384
- cursor: "pointer",
385
- onPress: () => handleLoadReport(report),
386
- children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
387
- ...knobProps.body,
388
- flex: 1,
389
- children: report.name
390
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
391
- size: "$2",
392
- circular: true,
393
- chromeless: true,
394
- icon: import_react2.TrashIcon,
395
- onPress: e => {
396
- e.stopPropagation();
397
- handleDeleteReport(report.name);
398
- },
399
- "aria-label": t("Delete {{name}}", {
400
- name: report.name
401
- })
402
- })]
403
- }, report.name))]
404
- })
405
- })]
406
- })]
407
- }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.ReportBuilder, {
587
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_ReportViewToolbar.ReportViewToolbar, {
408
588
  columns: allColumns,
409
- data,
410
- groupBy: groupBy ?? void 0,
589
+ selected: selectedFieldnames,
590
+ onToggleColumn: handleToggleColumn,
591
+ onResetColumns: handleResetColumns,
592
+ groupBy,
411
593
  onGroupByChange: setGroupBy,
412
- onColumnsChange: setEnabledColumns,
594
+ baseFilters,
595
+ onClearBaseFilters: baseFilters.length > 0 ? handleClearBaseFilters : void 0,
596
+ columnFilterCount: columnTuples.length,
597
+ onClearColumnFilters: handleClearColumnFilters,
598
+ savedReports: savedReports.reports,
599
+ currentReport,
600
+ onSelectReport: handleSelectReport,
601
+ onSaveReport: readsWithoutMeta ? void 0 : handleSaveReport,
602
+ onDeleteReport: readsWithoutMeta ? void 0 : handleDeleteReport,
413
603
  onExport: exportEnabled ? handleExport : void 0,
414
- onRowPress,
415
- showChart,
416
- flex: 1
604
+ exportDisabled: total === 0
605
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.YStack, {
606
+ flex: 1,
607
+ minWidth: 0,
608
+ paddingHorizontal: "$3",
609
+ children: body
610
+ }), total > 0 && !syncError && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.YStack, {
611
+ paddingHorizontal: "$3",
612
+ paddingVertical: "$2",
613
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_table.DataTablePagination, {
614
+ pagination: {
615
+ pageIndex: effectivePage,
616
+ pageSize,
617
+ totalCount: total,
618
+ totalPages: pageCount
619
+ },
620
+ onPaginationChange: next => {
621
+ setPage(next.pageIndex);
622
+ setPageSize(next.pageSize);
623
+ },
624
+ pageSizeOptions: [...import_reportViewModel.REPORT_PAGE_SIZES],
625
+ compact: true
626
+ })
417
627
  })]
418
628
  });
629
+ return import_components.isWeb ? content : /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_components.ScrollView, {
630
+ flex: 1,
631
+ children: content
632
+ });
419
633
  }