@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
@@ -0,0 +1,314 @@
1
+ // The report view's ONE control row (audit: the old surface stacked three
2
+ // toolbar bands with the column count shown twice). Left: column picker,
3
+ // group-by, saved-report selector + save affordances. Right: filter chips
4
+ // (?f= base filters + column-filter summary) and Export. Page size lives in
5
+ // the footer pager (list-screen parity), not here.
6
+ import {
7
+ Chip,
8
+ DropdownMenu,
9
+ Popover,
10
+ PopoverContent,
11
+ XStack,
12
+ YStack,
13
+ } from "@multiplatform.one/components";
14
+ import { Button, Input } from "@multiplatform.one/forms";
15
+ import { useResolvedKnobs } from "@multiplatform.one/theme";
16
+ import {
17
+ ColumnsIcon,
18
+ DownloadSimpleIcon,
19
+ FloppyDiskIcon,
20
+ RowsIcon,
21
+ StackIcon,
22
+ } from "@phosphor-icons/react";
23
+ import React, { useState } from "react";
24
+ import { useTranslation } from "react-i18next";
25
+ import { filterTupleLabel } from "../filterFields";
26
+ import { dialogBodyMinWidth, overlayListMaxHeight } from "../measures";
27
+ import type { ReportColumnDef, ReportFilters } from "./reportViewModel";
28
+ import type { SavedReportSummary } from "./savedReports";
29
+
30
+ export interface ReportViewToolbarProps {
31
+ /** Every pickable column (reportColumnsFromMeta). */
32
+ columns: readonly ReportColumnDef[];
33
+ /** Selected (visible) fieldnames, in render order. */
34
+ selected: readonly string[];
35
+ onToggleColumn: (fieldname: string) => void;
36
+ onResetColumns: () => void;
37
+ groupBy: string | null;
38
+ onGroupByChange: (fieldname: string | null) => void;
39
+ /** Base filters (?f= deep link) — chips with a clear affordance. */
40
+ baseFilters: ReportFilters;
41
+ onClearBaseFilters?: () => void;
42
+ /** Count of active column-row filters (chip + clear-all). */
43
+ columnFilterCount: number;
44
+ onClearColumnFilters: () => void;
45
+ savedReports: readonly SavedReportSummary[];
46
+ /** Selected saved report name (null = unsaved/new). */
47
+ currentReport: string | null;
48
+ onSelectReport: (name: string | null) => void;
49
+ /** Save under a (new or existing) name. Absent, the view saves nothing. */
50
+ onSaveReport?: (name: string) => void;
51
+ /** Absent, a saved report cannot be deleted from here. */
52
+ onDeleteReport?: (name: string) => void;
53
+ /** Absent, the Export button is left out (no download path here). */
54
+ onExport?: () => void;
55
+ exportDisabled?: boolean;
56
+ }
57
+
58
+ export function ReportViewToolbar({
59
+ columns,
60
+ selected,
61
+ onToggleColumn,
62
+ onResetColumns,
63
+ groupBy,
64
+ onGroupByChange,
65
+ baseFilters,
66
+ onClearBaseFilters,
67
+ columnFilterCount,
68
+ onClearColumnFilters,
69
+ savedReports,
70
+ currentReport,
71
+ onSelectReport,
72
+ onSaveReport,
73
+ onDeleteReport,
74
+ onExport,
75
+ exportDisabled,
76
+ }: ReportViewToolbarProps) {
77
+ const { t } = useTranslation();
78
+ const { knobProps } = useResolvedKnobs();
79
+ const [saveOpen, setSaveOpen] = useState(false);
80
+ const [saveName, setSaveName] = useState("");
81
+
82
+ const selectedSet = new Set(selected);
83
+ const groupByColumn = groupBy
84
+ ? columns.find((column) => column.fieldname === groupBy)
85
+ : undefined;
86
+ const currentSummary = currentReport
87
+ ? savedReports.find((report) => report.name === currentReport)
88
+ : undefined;
89
+
90
+ const handleSaveAs = () => {
91
+ const name = saveName.trim();
92
+ if (!name || !onSaveReport) return;
93
+ onSaveReport(name);
94
+ setSaveOpen(false);
95
+ setSaveName("");
96
+ };
97
+
98
+ return (
99
+ <XStack
100
+ alignItems="center"
101
+ flexWrap="wrap"
102
+ gap="$2"
103
+ {...knobProps.panelPadding}
104
+ paddingVertical="$2"
105
+ >
106
+ {/* Column add/remove picker (persisted per doctype) */}
107
+ <DropdownMenu>
108
+ <DropdownMenu.Trigger asChild>
109
+ <Button size="$3" chromeless icon={ColumnsIcon}>
110
+ {t("report.columns", {
111
+ defaultValue: "Columns ({{enabled}}/{{total}})",
112
+ enabled: selected.length,
113
+ total: columns.length,
114
+ })}
115
+ </Button>
116
+ </DropdownMenu.Trigger>
117
+ <DropdownMenu.Content maxHeight={overlayListMaxHeight}>
118
+ <DropdownMenu.Label>{t("report.pickColumns", "Report columns")}</DropdownMenu.Label>
119
+ {columns.map((column) => {
120
+ const isSelected = selectedSet.has(column.fieldname);
121
+ const lastSelected = isSelected && selected.length === 1;
122
+ return (
123
+ <DropdownMenu.CheckboxItem
124
+ key={column.fieldname}
125
+ checked={isSelected}
126
+ disabled={lastSelected}
127
+ disabledReason={
128
+ lastSelected
129
+ ? t("report.lastColumn", "At least one column stays visible")
130
+ : undefined
131
+ }
132
+ onCheckedChange={() => onToggleColumn(column.fieldname)}
133
+ >
134
+ {column.label}
135
+ </DropdownMenu.CheckboxItem>
136
+ );
137
+ })}
138
+ <DropdownMenu.Separator />
139
+ <DropdownMenu.Item onSelect={onResetColumns}>
140
+ {t("report.resetColumns", "Reset to default columns")}
141
+ </DropdownMenu.Item>
142
+ </DropdownMenu.Content>
143
+ </DropdownMenu>
144
+
145
+ {/* Group-by (rendered as in-grid group header rows) */}
146
+ <DropdownMenu>
147
+ <DropdownMenu.Trigger asChild>
148
+ <Button size="$3" chromeless icon={RowsIcon}>
149
+ {groupByColumn
150
+ ? t("report.groupByActive", {
151
+ defaultValue: "Group by: {{field}}",
152
+ field: groupByColumn.label,
153
+ })
154
+ : t("report.groupBy", "Group by")}
155
+ </Button>
156
+ </DropdownMenu.Trigger>
157
+ <DropdownMenu.Content maxHeight={overlayListMaxHeight}>
158
+ <DropdownMenu.CheckboxItem
159
+ checked={!groupBy}
160
+ closeOnSelect
161
+ onCheckedChange={() => onGroupByChange(null)}
162
+ >
163
+ {t("report.groupByNone", "No grouping")}
164
+ </DropdownMenu.CheckboxItem>
165
+ <DropdownMenu.Separator />
166
+ {columns.map((column) => (
167
+ <DropdownMenu.CheckboxItem
168
+ key={column.fieldname}
169
+ checked={groupBy === column.fieldname}
170
+ closeOnSelect
171
+ onCheckedChange={() => onGroupByChange(column.fieldname)}
172
+ >
173
+ {column.label}
174
+ </DropdownMenu.CheckboxItem>
175
+ ))}
176
+ </DropdownMenu.Content>
177
+ </DropdownMenu>
178
+
179
+ {/* Saved-report selector (desk "Select Report": Report Builder docs) */}
180
+ {(onSaveReport || savedReports.length > 0) && (
181
+ <DropdownMenu>
182
+ <DropdownMenu.Trigger asChild>
183
+ <Button size="$3" chromeless icon={StackIcon}>
184
+ {currentSummary
185
+ ? t("report.savedReportActive", {
186
+ defaultValue: "Report: {{name}}",
187
+ name: currentSummary.reportName,
188
+ })
189
+ : t("report.savedReport", "Select Report")}
190
+ </Button>
191
+ </DropdownMenu.Trigger>
192
+ <DropdownMenu.Content maxHeight={overlayListMaxHeight}>
193
+ <DropdownMenu.CheckboxItem
194
+ checked={!currentReport}
195
+ closeOnSelect
196
+ onCheckedChange={() => onSelectReport(null)}
197
+ >
198
+ {t("report.savedReportNone", "New report (unsaved)")}
199
+ </DropdownMenu.CheckboxItem>
200
+ {savedReports.length > 0 && <DropdownMenu.Separator />}
201
+ {savedReports.map((report) => (
202
+ <DropdownMenu.CheckboxItem
203
+ key={report.name}
204
+ checked={currentReport === report.name}
205
+ closeOnSelect
206
+ onCheckedChange={() => onSelectReport(report.name)}
207
+ >
208
+ {report.reportName}
209
+ </DropdownMenu.CheckboxItem>
210
+ ))}
211
+ {currentSummary && onDeleteReport && (
212
+ <>
213
+ <DropdownMenu.Separator />
214
+ <DropdownMenu.Item destructive onSelect={() => onDeleteReport(currentSummary.name)}>
215
+ {t("report.deleteReport", {
216
+ defaultValue: "Delete “{{name}}”",
217
+ name: currentSummary.reportName,
218
+ })}
219
+ </DropdownMenu.Item>
220
+ </>
221
+ )}
222
+ </DropdownMenu.Content>
223
+ </DropdownMenu>
224
+ )}
225
+
226
+ {/* Save (overwrite the selected report) + Save As (new Report doc) */}
227
+ {currentSummary && onSaveReport && (
228
+ <Button size="$3" icon={FloppyDiskIcon} onPress={() => onSaveReport(currentSummary.name)}>
229
+ {t("report.save", "Save")}
230
+ </Button>
231
+ )}
232
+ {onSaveReport && (
233
+ <Popover placement="bottom-start" open={saveOpen} onOpenChange={setSaveOpen}>
234
+ <Popover.Trigger asChild>
235
+ <Button
236
+ size="$3"
237
+ chromeless={!!currentSummary}
238
+ icon={currentSummary ? undefined : FloppyDiskIcon}
239
+ >
240
+ {t("report.saveAs", "Save As…")}
241
+ </Button>
242
+ </Popover.Trigger>
243
+ {/* House overlay surface (elevatedSurface knob recipe) — raw
244
+ Popover.Content has no `elevate` variant in tamagui 2.0, so the
245
+ bare boolean leaked to the DOM (LC-46 class). */}
246
+ <PopoverContent {...knobProps.panelPadding}>
247
+ <Popover.Arrow borderWidth={1} borderColor="$borderColor" />
248
+ <XStack gap="$2" alignItems="center" minWidth={dialogBodyMinWidth}>
249
+ <YStack flex={1} minWidth={0}>
250
+ <Input
251
+ size="$3"
252
+ compact
253
+ value={saveName}
254
+ onChangeText={setSaveName}
255
+ placeholder={t("report.reportNamePlaceholder", "New report name")}
256
+ inputProps={{
257
+ "aria-label": t("report.reportNamePlaceholder", "New report name"),
258
+ }}
259
+ />
260
+ </YStack>
261
+ <Button
262
+ size="$3"
263
+ accent
264
+ disabled={!saveName.trim()}
265
+ disabledReason={t("report.saveNeedsName", "Name the report to save it")}
266
+ disabledReasonPlacement="inline"
267
+ onPress={handleSaveAs}
268
+ >
269
+ {t("report.save", "Save")}
270
+ </Button>
271
+ </XStack>
272
+ </PopoverContent>
273
+ </Popover>
274
+ )}
275
+
276
+ <XStack flex={1} />
277
+
278
+ {/* Base filter chips (?f= deep link) */}
279
+ {baseFilters.map((tuple, index) => (
280
+ <Chip key={`${tuple[0]}-${index}`} size="$2" variant="subtle" color="gray">
281
+ {filterTupleLabel([...columns], tuple)}
282
+ </Chip>
283
+ ))}
284
+ {onClearBaseFilters && baseFilters.length > 0 && (
285
+ <Button size="$3" chromeless onPress={onClearBaseFilters}>
286
+ {t("report.clearFilters", "Clear filters")}
287
+ </Button>
288
+ )}
289
+
290
+ {/* Column filter-row summary + clear-all */}
291
+ {columnFilterCount > 0 && (
292
+ <Button size="$3" chromeless onPress={onClearColumnFilters}>
293
+ {t("report.clearColumnFilters", {
294
+ defaultValue: "Clear column filters ({{count}})",
295
+ count: columnFilterCount,
296
+ })}
297
+ </Button>
298
+ )}
299
+
300
+ {onExport && (
301
+ <Button
302
+ size="$3"
303
+ icon={DownloadSimpleIcon}
304
+ disabled={exportDisabled}
305
+ disabledReason={exportDisabled ? t("report.exportEmpty", "No rows to export") : undefined}
306
+ disabledReasonPlacement="inline"
307
+ onPress={onExport}
308
+ >
309
+ {t("report.export", "Export CSV")}
310
+ </Button>
311
+ )}
312
+ </XStack>
313
+ );
314
+ }