@multiplatform.one/frappe-ui 7.28.1 → 7.29.1

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