@multiplatform.one/frappe-ui 6.6.0 → 7.0.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 (516) hide show
  1. package/dist/cjs/FrappeAssignment.cjs +2 -0
  2. package/dist/cjs/FrappeAssignment.native.js +2 -0
  3. package/dist/cjs/FrappeAssignment.native.js.map +1 -1
  4. package/dist/cjs/FrappeCalendar.cjs +5 -7
  5. package/dist/cjs/FrappeCalendar.native.js +5 -9
  6. package/dist/cjs/FrappeCalendar.native.js.map +1 -1
  7. package/dist/cjs/FrappeChartCard.cjs +2 -0
  8. package/dist/cjs/FrappeChartCard.native.js +2 -0
  9. package/dist/cjs/FrappeChartCard.native.js.map +1 -1
  10. package/dist/cjs/FrappeDashboard.cjs +11 -22
  11. package/dist/cjs/FrappeDashboard.native.js +20 -22
  12. package/dist/cjs/FrappeDashboard.native.js.map +1 -1
  13. package/dist/cjs/FrappeForm.cjs +3 -1
  14. package/dist/cjs/FrappeForm.native.js +3 -1
  15. package/dist/cjs/FrappeForm.native.js.map +1 -1
  16. package/dist/cjs/FrappeGantt.cjs +5 -7
  17. package/dist/cjs/FrappeGantt.native.js +5 -9
  18. package/dist/cjs/FrappeGantt.native.js.map +1 -1
  19. package/dist/cjs/FrappeImageView.cjs +3 -1
  20. package/dist/cjs/FrappeImageView.native.js +3 -1
  21. package/dist/cjs/FrappeImageView.native.js.map +1 -1
  22. package/dist/cjs/FrappeKPICard.cjs +2 -0
  23. package/dist/cjs/FrappeKPICard.native.js +2 -0
  24. package/dist/cjs/FrappeKPICard.native.js.map +1 -1
  25. package/dist/cjs/FrappeKanban.cjs +3 -1
  26. package/dist/cjs/FrappeKanban.native.js +3 -1
  27. package/dist/cjs/FrappeKanban.native.js.map +1 -1
  28. package/dist/cjs/FrappeLikedBy.cjs +2 -0
  29. package/dist/cjs/FrappeLikedBy.native.js +2 -0
  30. package/dist/cjs/FrappeLikedBy.native.js.map +1 -1
  31. package/dist/cjs/FrappeList.cjs +3 -1
  32. package/dist/cjs/FrappeList.native.js +3 -1
  33. package/dist/cjs/FrappeList.native.js.map +1 -1
  34. package/dist/cjs/FrappeListView.cjs +6 -12
  35. package/dist/cjs/FrappeListView.native.js +13 -12
  36. package/dist/cjs/FrappeListView.native.js.map +1 -1
  37. package/dist/cjs/FrappeProvider.cjs +5 -1
  38. package/dist/cjs/FrappeProvider.native.js +5 -1
  39. package/dist/cjs/FrappeProvider.native.js.map +1 -1
  40. package/dist/cjs/FrappeReportBuilder.cjs +2 -0
  41. package/dist/cjs/FrappeReportBuilder.native.js +2 -0
  42. package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
  43. package/dist/cjs/FrappeReportView.cjs +23 -45
  44. package/dist/cjs/FrappeReportView.native.js +35 -50
  45. package/dist/cjs/FrappeReportView.native.js.map +1 -1
  46. package/dist/cjs/FrappeTable.cjs +105 -31
  47. package/dist/cjs/FrappeTable.native.js +140 -42
  48. package/dist/cjs/FrappeTable.native.js.map +1 -1
  49. package/dist/cjs/FrappeTreeView.cjs +3 -1
  50. package/dist/cjs/FrappeTreeView.native.js +3 -1
  51. package/dist/cjs/FrappeTreeView.native.js.map +1 -1
  52. package/dist/cjs/SyncErrorNotice.cjs +76 -0
  53. package/dist/cjs/SyncErrorNotice.native.js +92 -0
  54. package/dist/cjs/SyncErrorNotice.native.js.map +1 -0
  55. package/dist/cjs/commentActions.cjs +120 -0
  56. package/dist/cjs/commentActions.native.js +129 -0
  57. package/dist/cjs/commentActions.native.js.map +1 -0
  58. package/dist/cjs/components/attachments.cjs +2 -0
  59. package/dist/cjs/components/attachments.native.js +2 -0
  60. package/dist/cjs/components/attachments.native.js.map +1 -1
  61. package/dist/cjs/components/tags.cjs +5 -0
  62. package/dist/cjs/components/tags.native.js +5 -0
  63. package/dist/cjs/components/tags.native.js.map +1 -1
  64. package/dist/cjs/components/timeline.cjs +29 -2
  65. package/dist/cjs/components/timeline.native.js +33 -2
  66. package/dist/cjs/components/timeline.native.js.map +1 -1
  67. package/dist/cjs/desk/DeskFormSidebar.cjs +2 -0
  68. package/dist/cjs/desk/DeskFormSidebar.native.js +2 -0
  69. package/dist/cjs/desk/DeskFormSidebar.native.js.map +1 -1
  70. package/dist/cjs/fieldRegistry.cjs +2 -0
  71. package/dist/cjs/fieldRegistry.native.js +2 -0
  72. package/dist/cjs/fieldRegistry.native.js.map +1 -1
  73. package/dist/cjs/fields/Date/index.cjs +71 -7
  74. package/dist/cjs/fields/Date/index.native.js +325 -37
  75. package/dist/cjs/fields/Date/index.native.js.map +1 -1
  76. package/dist/cjs/fields/Date/types.cjs +18 -0
  77. package/dist/cjs/fields/Date/types.native.js +21 -0
  78. package/dist/cjs/fields/Date/types.native.js.map +1 -0
  79. package/dist/cjs/fields/Date/wire.cjs +4 -8
  80. package/dist/cjs/fields/Date/wire.native.js +4 -11
  81. package/dist/cjs/fields/Date/wire.native.js.map +1 -1
  82. package/dist/cjs/fields/DynamicLink.cjs +2 -0
  83. package/dist/cjs/fields/DynamicLink.native.js +2 -0
  84. package/dist/cjs/fields/DynamicLink.native.js.map +1 -1
  85. package/dist/cjs/fields/FileUpload.cjs +2 -0
  86. package/dist/cjs/fields/FileUpload.native.js +2 -0
  87. package/dist/cjs/fields/FileUpload.native.js.map +1 -1
  88. package/dist/cjs/fields/TableMultiSelect.cjs +6 -1
  89. package/dist/cjs/fields/TableMultiSelect.native.js +6 -1
  90. package/dist/cjs/fields/TableMultiSelect.native.js.map +1 -1
  91. package/dist/cjs/frappeStoryTarget.cjs +88 -0
  92. package/dist/cjs/frappeStoryTarget.native.js +100 -0
  93. package/dist/cjs/frappeStoryTarget.native.js.map +1 -1
  94. package/dist/cjs/frappeUsers.cjs +2 -0
  95. package/dist/cjs/frappeUsers.native.js +2 -0
  96. package/dist/cjs/frappeUsers.native.js.map +1 -1
  97. package/dist/cjs/index.cjs +17 -4
  98. package/dist/cjs/index.native.js +17 -4
  99. package/dist/cjs/index.native.js.map +1 -1
  100. package/dist/cjs/liveErrors.cjs +5 -1
  101. package/dist/cjs/liveErrors.native.js +5 -1
  102. package/dist/cjs/liveErrors.native.js.map +1 -1
  103. package/dist/cjs/useFrappeAggregate.cjs +13 -1
  104. package/dist/cjs/useFrappeAggregate.native.js +14 -1
  105. package/dist/cjs/useFrappeAggregate.native.js.map +1 -1
  106. package/dist/cjs/useFrappeFacets.cjs +329 -0
  107. package/dist/cjs/useFrappeFacets.native.js +632 -0
  108. package/dist/cjs/useFrappeFacets.native.js.map +1 -0
  109. package/dist/esm/FrappeAssignment.mjs +2 -0
  110. package/dist/esm/FrappeAssignment.mjs.map +1 -1
  111. package/dist/esm/FrappeAssignment.native.js +2 -0
  112. package/dist/esm/FrappeAssignment.native.js.map +1 -1
  113. package/dist/esm/FrappeCalendar.mjs +6 -8
  114. package/dist/esm/FrappeCalendar.mjs.map +1 -1
  115. package/dist/esm/FrappeCalendar.native.js +6 -10
  116. package/dist/esm/FrappeCalendar.native.js.map +1 -1
  117. package/dist/esm/FrappeChartCard.mjs +2 -0
  118. package/dist/esm/FrappeChartCard.mjs.map +1 -1
  119. package/dist/esm/FrappeChartCard.native.js +2 -0
  120. package/dist/esm/FrappeChartCard.native.js.map +1 -1
  121. package/dist/esm/FrappeDashboard.mjs +12 -19
  122. package/dist/esm/FrappeDashboard.mjs.map +1 -1
  123. package/dist/esm/FrappeDashboard.native.js +23 -21
  124. package/dist/esm/FrappeDashboard.native.js.map +1 -1
  125. package/dist/esm/FrappeForm.mjs +3 -1
  126. package/dist/esm/FrappeForm.mjs.map +1 -1
  127. package/dist/esm/FrappeForm.native.js +3 -1
  128. package/dist/esm/FrappeForm.native.js.map +1 -1
  129. package/dist/esm/FrappeGantt.mjs +6 -8
  130. package/dist/esm/FrappeGantt.mjs.map +1 -1
  131. package/dist/esm/FrappeGantt.native.js +6 -10
  132. package/dist/esm/FrappeGantt.native.js.map +1 -1
  133. package/dist/esm/FrappeImageView.mjs +3 -1
  134. package/dist/esm/FrappeImageView.mjs.map +1 -1
  135. package/dist/esm/FrappeImageView.native.js +3 -1
  136. package/dist/esm/FrappeImageView.native.js.map +1 -1
  137. package/dist/esm/FrappeKPICard.mjs +2 -0
  138. package/dist/esm/FrappeKPICard.mjs.map +1 -1
  139. package/dist/esm/FrappeKPICard.native.js +2 -0
  140. package/dist/esm/FrappeKPICard.native.js.map +1 -1
  141. package/dist/esm/FrappeKanban.mjs +3 -1
  142. package/dist/esm/FrappeKanban.mjs.map +1 -1
  143. package/dist/esm/FrappeKanban.native.js +3 -1
  144. package/dist/esm/FrappeKanban.native.js.map +1 -1
  145. package/dist/esm/FrappeLikedBy.mjs +2 -0
  146. package/dist/esm/FrappeLikedBy.mjs.map +1 -1
  147. package/dist/esm/FrappeLikedBy.native.js +2 -0
  148. package/dist/esm/FrappeLikedBy.native.js.map +1 -1
  149. package/dist/esm/FrappeList.mjs +3 -1
  150. package/dist/esm/FrappeList.mjs.map +1 -1
  151. package/dist/esm/FrappeList.native.js +3 -1
  152. package/dist/esm/FrappeList.native.js.map +1 -1
  153. package/dist/esm/FrappeListView.mjs +7 -13
  154. package/dist/esm/FrappeListView.mjs.map +1 -1
  155. package/dist/esm/FrappeListView.native.js +15 -14
  156. package/dist/esm/FrappeListView.native.js.map +1 -1
  157. package/dist/esm/FrappeProvider.mjs +5 -1
  158. package/dist/esm/FrappeProvider.mjs.map +1 -1
  159. package/dist/esm/FrappeProvider.native.js +5 -1
  160. package/dist/esm/FrappeProvider.native.js.map +1 -1
  161. package/dist/esm/FrappeReportBuilder.mjs +2 -0
  162. package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
  163. package/dist/esm/FrappeReportBuilder.native.js +2 -0
  164. package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
  165. package/dist/esm/FrappeReportView.mjs +26 -49
  166. package/dist/esm/FrappeReportView.mjs.map +1 -1
  167. package/dist/esm/FrappeReportView.native.js +38 -54
  168. package/dist/esm/FrappeReportView.native.js.map +1 -1
  169. package/dist/esm/FrappeTable.mjs +107 -33
  170. package/dist/esm/FrappeTable.mjs.map +1 -1
  171. package/dist/esm/FrappeTable.native.js +142 -44
  172. package/dist/esm/FrappeTable.native.js.map +1 -1
  173. package/dist/esm/FrappeTreeView.mjs +3 -1
  174. package/dist/esm/FrappeTreeView.mjs.map +1 -1
  175. package/dist/esm/FrappeTreeView.native.js +3 -1
  176. package/dist/esm/FrappeTreeView.native.js.map +1 -1
  177. package/dist/esm/SyncErrorNotice.mjs +51 -0
  178. package/dist/esm/SyncErrorNotice.mjs.map +1 -0
  179. package/dist/esm/SyncErrorNotice.native.js +53 -0
  180. package/dist/esm/SyncErrorNotice.native.js.map +1 -0
  181. package/dist/esm/commentActions.mjs +95 -0
  182. package/dist/esm/commentActions.mjs.map +1 -0
  183. package/dist/esm/commentActions.native.js +101 -0
  184. package/dist/esm/commentActions.native.js.map +1 -0
  185. package/dist/esm/components/attachments.mjs +2 -0
  186. package/dist/esm/components/attachments.mjs.map +1 -1
  187. package/dist/esm/components/attachments.native.js +2 -0
  188. package/dist/esm/components/attachments.native.js.map +1 -1
  189. package/dist/esm/components/tags.mjs +5 -0
  190. package/dist/esm/components/tags.mjs.map +1 -1
  191. package/dist/esm/components/tags.native.js +5 -0
  192. package/dist/esm/components/tags.native.js.map +1 -1
  193. package/dist/esm/components/timeline.mjs +31 -4
  194. package/dist/esm/components/timeline.mjs.map +1 -1
  195. package/dist/esm/components/timeline.native.js +35 -4
  196. package/dist/esm/components/timeline.native.js.map +1 -1
  197. package/dist/esm/desk/DeskFormSidebar.mjs +2 -0
  198. package/dist/esm/desk/DeskFormSidebar.mjs.map +1 -1
  199. package/dist/esm/desk/DeskFormSidebar.native.js +2 -0
  200. package/dist/esm/desk/DeskFormSidebar.native.js.map +1 -1
  201. package/dist/esm/fieldRegistry.mjs +2 -0
  202. package/dist/esm/fieldRegistry.mjs.map +1 -1
  203. package/dist/esm/fieldRegistry.native.js +2 -0
  204. package/dist/esm/fieldRegistry.native.js.map +1 -1
  205. package/dist/esm/fields/Date/index.mjs +73 -9
  206. package/dist/esm/fields/Date/index.mjs.map +1 -1
  207. package/dist/esm/fields/Date/index.native.js +315 -38
  208. package/dist/esm/fields/Date/index.native.js.map +1 -1
  209. package/dist/esm/fields/Date/types.mjs +2 -0
  210. package/dist/esm/fields/Date/types.mjs.map +1 -0
  211. package/dist/esm/fields/Date/types.native.js +2 -0
  212. package/dist/esm/fields/Date/types.native.js.map +1 -0
  213. package/dist/esm/fields/Date/wire.mjs +4 -8
  214. package/dist/esm/fields/Date/wire.mjs.map +1 -1
  215. package/dist/esm/fields/Date/wire.native.js +4 -11
  216. package/dist/esm/fields/Date/wire.native.js.map +1 -1
  217. package/dist/esm/fields/DynamicLink.mjs +2 -0
  218. package/dist/esm/fields/DynamicLink.mjs.map +1 -1
  219. package/dist/esm/fields/DynamicLink.native.js +2 -0
  220. package/dist/esm/fields/DynamicLink.native.js.map +1 -1
  221. package/dist/esm/fields/FileUpload.mjs +2 -0
  222. package/dist/esm/fields/FileUpload.mjs.map +1 -1
  223. package/dist/esm/fields/FileUpload.native.js +2 -0
  224. package/dist/esm/fields/FileUpload.native.js.map +1 -1
  225. package/dist/esm/fields/TableMultiSelect.mjs +7 -2
  226. package/dist/esm/fields/TableMultiSelect.mjs.map +1 -1
  227. package/dist/esm/fields/TableMultiSelect.native.js +7 -2
  228. package/dist/esm/fields/TableMultiSelect.native.js.map +1 -1
  229. package/dist/esm/frappeStoryTarget.mjs +88 -2
  230. package/dist/esm/frappeStoryTarget.mjs.map +1 -1
  231. package/dist/esm/frappeStoryTarget.native.js +100 -2
  232. package/dist/esm/frappeStoryTarget.native.js.map +1 -1
  233. package/dist/esm/frappeUsers.mjs +2 -0
  234. package/dist/esm/frappeUsers.mjs.map +1 -1
  235. package/dist/esm/frappeUsers.native.js +2 -0
  236. package/dist/esm/frappeUsers.native.js.map +1 -1
  237. package/dist/esm/index.mjs +7 -3
  238. package/dist/esm/index.mjs.map +1 -1
  239. package/dist/esm/index.native.js +7 -3
  240. package/dist/esm/index.native.js.map +1 -1
  241. package/dist/esm/liveErrors.mjs +5 -1
  242. package/dist/esm/liveErrors.mjs.map +1 -1
  243. package/dist/esm/liveErrors.native.js +5 -1
  244. package/dist/esm/liveErrors.native.js.map +1 -1
  245. package/dist/esm/useFrappeAggregate.mjs +13 -1
  246. package/dist/esm/useFrappeAggregate.mjs.map +1 -1
  247. package/dist/esm/useFrappeAggregate.native.js +14 -1
  248. package/dist/esm/useFrappeAggregate.native.js.map +1 -1
  249. package/dist/esm/useFrappeFacets.mjs +299 -0
  250. package/dist/esm/useFrappeFacets.mjs.map +1 -0
  251. package/dist/esm/useFrappeFacets.native.js +599 -0
  252. package/dist/esm/useFrappeFacets.native.js.map +1 -0
  253. package/dist/jsx/FrappeAssignment.mjs +2 -0
  254. package/dist/jsx/FrappeAssignment.mjs.map +1 -1
  255. package/dist/jsx/FrappeAssignment.native.js +2 -0
  256. package/dist/jsx/FrappeAssignment.native.js.map +1 -1
  257. package/dist/jsx/FrappeCalendar.mjs +6 -8
  258. package/dist/jsx/FrappeCalendar.mjs.map +1 -1
  259. package/dist/jsx/FrappeCalendar.native.js +5 -9
  260. package/dist/jsx/FrappeCalendar.native.js.map +1 -1
  261. package/dist/jsx/FrappeChartCard.mjs +2 -0
  262. package/dist/jsx/FrappeChartCard.mjs.map +1 -1
  263. package/dist/jsx/FrappeChartCard.native.js +2 -0
  264. package/dist/jsx/FrappeChartCard.native.js.map +1 -1
  265. package/dist/jsx/FrappeDashboard.mjs +12 -19
  266. package/dist/jsx/FrappeDashboard.mjs.map +1 -1
  267. package/dist/jsx/FrappeDashboard.native.js +20 -22
  268. package/dist/jsx/FrappeDashboard.native.js.map +1 -1
  269. package/dist/jsx/FrappeForm.mjs +3 -1
  270. package/dist/jsx/FrappeForm.mjs.map +1 -1
  271. package/dist/jsx/FrappeForm.native.js +3 -1
  272. package/dist/jsx/FrappeForm.native.js.map +1 -1
  273. package/dist/jsx/FrappeGantt.mjs +6 -8
  274. package/dist/jsx/FrappeGantt.mjs.map +1 -1
  275. package/dist/jsx/FrappeGantt.native.js +5 -9
  276. package/dist/jsx/FrappeGantt.native.js.map +1 -1
  277. package/dist/jsx/FrappeImageView.mjs +3 -1
  278. package/dist/jsx/FrappeImageView.mjs.map +1 -1
  279. package/dist/jsx/FrappeImageView.native.js +3 -1
  280. package/dist/jsx/FrappeImageView.native.js.map +1 -1
  281. package/dist/jsx/FrappeKPICard.mjs +2 -0
  282. package/dist/jsx/FrappeKPICard.mjs.map +1 -1
  283. package/dist/jsx/FrappeKPICard.native.js +2 -0
  284. package/dist/jsx/FrappeKPICard.native.js.map +1 -1
  285. package/dist/jsx/FrappeKanban.mjs +3 -1
  286. package/dist/jsx/FrappeKanban.mjs.map +1 -1
  287. package/dist/jsx/FrappeKanban.native.js +3 -1
  288. package/dist/jsx/FrappeKanban.native.js.map +1 -1
  289. package/dist/jsx/FrappeLikedBy.mjs +2 -0
  290. package/dist/jsx/FrappeLikedBy.mjs.map +1 -1
  291. package/dist/jsx/FrappeLikedBy.native.js +2 -0
  292. package/dist/jsx/FrappeLikedBy.native.js.map +1 -1
  293. package/dist/jsx/FrappeList.mjs +3 -1
  294. package/dist/jsx/FrappeList.mjs.map +1 -1
  295. package/dist/jsx/FrappeList.native.js +3 -1
  296. package/dist/jsx/FrappeList.native.js.map +1 -1
  297. package/dist/jsx/FrappeListView.mjs +7 -13
  298. package/dist/jsx/FrappeListView.mjs.map +1 -1
  299. package/dist/jsx/FrappeListView.native.js +13 -12
  300. package/dist/jsx/FrappeListView.native.js.map +1 -1
  301. package/dist/jsx/FrappeProvider.mjs +5 -1
  302. package/dist/jsx/FrappeProvider.mjs.map +1 -1
  303. package/dist/jsx/FrappeProvider.native.js +5 -1
  304. package/dist/jsx/FrappeProvider.native.js.map +1 -1
  305. package/dist/jsx/FrappeReportBuilder.mjs +2 -0
  306. package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
  307. package/dist/jsx/FrappeReportBuilder.native.js +2 -0
  308. package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
  309. package/dist/jsx/FrappeReportView.mjs +26 -49
  310. package/dist/jsx/FrappeReportView.mjs.map +1 -1
  311. package/dist/jsx/FrappeReportView.native.js +35 -50
  312. package/dist/jsx/FrappeReportView.native.js.map +1 -1
  313. package/dist/jsx/FrappeTable.mjs +107 -33
  314. package/dist/jsx/FrappeTable.mjs.map +1 -1
  315. package/dist/jsx/FrappeTable.native.js +140 -42
  316. package/dist/jsx/FrappeTable.native.js.map +1 -1
  317. package/dist/jsx/FrappeTreeView.mjs +3 -1
  318. package/dist/jsx/FrappeTreeView.mjs.map +1 -1
  319. package/dist/jsx/FrappeTreeView.native.js +3 -1
  320. package/dist/jsx/FrappeTreeView.native.js.map +1 -1
  321. package/dist/jsx/SyncErrorNotice.mjs +51 -0
  322. package/dist/jsx/SyncErrorNotice.mjs.map +1 -0
  323. package/dist/jsx/SyncErrorNotice.native.js +92 -0
  324. package/dist/jsx/SyncErrorNotice.native.js.map +1 -0
  325. package/dist/jsx/commentActions.mjs +95 -0
  326. package/dist/jsx/commentActions.mjs.map +1 -0
  327. package/dist/jsx/commentActions.native.js +129 -0
  328. package/dist/jsx/commentActions.native.js.map +1 -0
  329. package/dist/jsx/components/attachments.mjs +2 -0
  330. package/dist/jsx/components/attachments.mjs.map +1 -1
  331. package/dist/jsx/components/attachments.native.js +2 -0
  332. package/dist/jsx/components/attachments.native.js.map +1 -1
  333. package/dist/jsx/components/tags.mjs +5 -0
  334. package/dist/jsx/components/tags.mjs.map +1 -1
  335. package/dist/jsx/components/tags.native.js +5 -0
  336. package/dist/jsx/components/tags.native.js.map +1 -1
  337. package/dist/jsx/components/timeline.mjs +31 -4
  338. package/dist/jsx/components/timeline.mjs.map +1 -1
  339. package/dist/jsx/components/timeline.native.js +33 -2
  340. package/dist/jsx/components/timeline.native.js.map +1 -1
  341. package/dist/jsx/desk/DeskFormSidebar.mjs +2 -0
  342. package/dist/jsx/desk/DeskFormSidebar.mjs.map +1 -1
  343. package/dist/jsx/desk/DeskFormSidebar.native.js +2 -0
  344. package/dist/jsx/desk/DeskFormSidebar.native.js.map +1 -1
  345. package/dist/jsx/fieldRegistry.mjs +2 -0
  346. package/dist/jsx/fieldRegistry.mjs.map +1 -1
  347. package/dist/jsx/fieldRegistry.native.js +2 -0
  348. package/dist/jsx/fieldRegistry.native.js.map +1 -1
  349. package/dist/jsx/fields/Date/index.mjs +73 -9
  350. package/dist/jsx/fields/Date/index.mjs.map +1 -1
  351. package/dist/jsx/fields/Date/index.native.js +325 -37
  352. package/dist/jsx/fields/Date/index.native.js.map +1 -1
  353. package/dist/jsx/fields/Date/types.mjs +2 -0
  354. package/dist/jsx/fields/Date/types.mjs.map +1 -0
  355. package/dist/jsx/fields/Date/types.native.js +21 -0
  356. package/dist/jsx/fields/Date/types.native.js.map +1 -0
  357. package/dist/jsx/fields/Date/wire.mjs +4 -8
  358. package/dist/jsx/fields/Date/wire.mjs.map +1 -1
  359. package/dist/jsx/fields/Date/wire.native.js +4 -11
  360. package/dist/jsx/fields/Date/wire.native.js.map +1 -1
  361. package/dist/jsx/fields/DynamicLink.mjs +2 -0
  362. package/dist/jsx/fields/DynamicLink.mjs.map +1 -1
  363. package/dist/jsx/fields/DynamicLink.native.js +2 -0
  364. package/dist/jsx/fields/DynamicLink.native.js.map +1 -1
  365. package/dist/jsx/fields/FileUpload.mjs +2 -0
  366. package/dist/jsx/fields/FileUpload.mjs.map +1 -1
  367. package/dist/jsx/fields/FileUpload.native.js +2 -0
  368. package/dist/jsx/fields/FileUpload.native.js.map +1 -1
  369. package/dist/jsx/fields/TableMultiSelect.mjs +7 -2
  370. package/dist/jsx/fields/TableMultiSelect.mjs.map +1 -1
  371. package/dist/jsx/fields/TableMultiSelect.native.js +6 -1
  372. package/dist/jsx/fields/TableMultiSelect.native.js.map +1 -1
  373. package/dist/jsx/frappeStoryTarget.mjs +88 -2
  374. package/dist/jsx/frappeStoryTarget.mjs.map +1 -1
  375. package/dist/jsx/frappeStoryTarget.native.js +100 -0
  376. package/dist/jsx/frappeStoryTarget.native.js.map +1 -1
  377. package/dist/jsx/frappeUsers.mjs +2 -0
  378. package/dist/jsx/frappeUsers.mjs.map +1 -1
  379. package/dist/jsx/frappeUsers.native.js +2 -0
  380. package/dist/jsx/frappeUsers.native.js.map +1 -1
  381. package/dist/jsx/index.js +7 -3
  382. package/dist/jsx/index.js.map +1 -1
  383. package/dist/jsx/index.mjs +7 -3
  384. package/dist/jsx/index.mjs.map +1 -1
  385. package/dist/jsx/index.native.js +17 -4
  386. package/dist/jsx/index.native.js.map +1 -1
  387. package/dist/jsx/liveErrors.mjs +5 -1
  388. package/dist/jsx/liveErrors.mjs.map +1 -1
  389. package/dist/jsx/liveErrors.native.js +5 -1
  390. package/dist/jsx/liveErrors.native.js.map +1 -1
  391. package/dist/jsx/useFrappeAggregate.mjs +13 -1
  392. package/dist/jsx/useFrappeAggregate.mjs.map +1 -1
  393. package/dist/jsx/useFrappeAggregate.native.js +14 -1
  394. package/dist/jsx/useFrappeAggregate.native.js.map +1 -1
  395. package/dist/jsx/useFrappeFacets.mjs +299 -0
  396. package/dist/jsx/useFrappeFacets.mjs.map +1 -0
  397. package/dist/jsx/useFrappeFacets.native.js +632 -0
  398. package/dist/jsx/useFrappeFacets.native.js.map +1 -0
  399. package/package.json +21 -21
  400. package/src/FormSidebar.stories.tsx +88 -10
  401. package/src/FrappeAssignment.tsx +2 -0
  402. package/src/FrappeCalendar.tsx +12 -7
  403. package/src/FrappeChartCard.tsx +9 -2
  404. package/src/FrappeDashboard.spec.tsx +95 -0
  405. package/src/FrappeDashboard.tsx +20 -41
  406. package/src/FrappeForm.stories.tsx +53 -46
  407. package/src/FrappeForm.tsx +11 -1
  408. package/src/FrappeGantt.tsx +12 -7
  409. package/src/FrappeImageView.tsx +11 -1
  410. package/src/FrappeKPICard.stories.tsx +4 -1
  411. package/src/FrappeKPICard.tsx +2 -0
  412. package/src/FrappeKanban.tsx +4 -0
  413. package/src/FrappeLikedBy.stories.tsx +26 -10
  414. package/src/FrappeLikedBy.tsx +2 -0
  415. package/src/FrappeList.tsx +11 -1
  416. package/src/FrappeListView.spec.tsx +144 -0
  417. package/src/FrappeListView.tsx +11 -23
  418. package/src/FrappeProvider.tsx +30 -2
  419. package/src/FrappeReportBuilder.tsx +2 -0
  420. package/src/FrappeReportView.spec.tsx +140 -16
  421. package/src/FrappeReportView.stories.tsx +12 -6
  422. package/src/FrappeReportView.tsx +62 -61
  423. package/src/FrappeTable.keepRows.spec.tsx +200 -0
  424. package/src/FrappeTable.retryStorm.spec.tsx +140 -0
  425. package/src/FrappeTable.spec.tsx +222 -0
  426. package/src/FrappeTable.tsx +211 -54
  427. package/src/FrappeTreeView.tsx +11 -1
  428. package/src/SyncErrorNotice.spec.tsx +60 -0
  429. package/src/SyncErrorNotice.tsx +81 -0
  430. package/src/commentActions.ts +157 -0
  431. package/src/components/attachments.tsx +2 -0
  432. package/src/components/tags.tsx +14 -1
  433. package/src/components/timeline.optimistic.spec.tsx +106 -0
  434. package/src/components/timeline.tsx +36 -4
  435. package/src/desk/DeskFormSidebar.tsx +2 -0
  436. package/src/fieldRegistry.ts +2 -0
  437. package/src/fields/Date/index.native.tsx +410 -0
  438. package/src/fields/Date/index.tsx +138 -57
  439. package/src/fields/Date/nativePresentation.spec.tsx +251 -0
  440. package/src/fields/Date/typeParity.spec.tsx +480 -0
  441. package/src/fields/Date/types.ts +53 -0
  442. package/src/fields/Date/wire.spec.ts +63 -1
  443. package/src/fields/Date/wire.ts +17 -17
  444. package/src/fields/DynamicLink.tsx +2 -0
  445. package/src/fields/FileUpload.tsx +2 -0
  446. package/src/fields/TableMultiSelect.tsx +6 -3
  447. package/src/frappeStoryTarget.tsx +104 -1
  448. package/src/frappeUsers.ts +2 -0
  449. package/src/index.spec.ts +62 -0
  450. package/src/index.ts +36 -2
  451. package/src/liveErrors.spec.ts +50 -0
  452. package/src/liveErrors.ts +11 -1
  453. package/src/useFrappeAggregate.ts +20 -1
  454. package/src/useFrappeFacets.spec.tsx +436 -0
  455. package/src/useFrappeFacets.ts +568 -0
  456. package/types/FrappeAssignment.d.ts.map +1 -1
  457. package/types/FrappeCalendar.d.ts.map +1 -1
  458. package/types/FrappeChartCard.d.ts +1 -2
  459. package/types/FrappeChartCard.d.ts.map +1 -1
  460. package/types/FrappeDashboard.d.ts +0 -1
  461. package/types/FrappeDashboard.d.ts.map +1 -1
  462. package/types/FrappeForm.d.ts.map +1 -1
  463. package/types/FrappeGantt.d.ts.map +1 -1
  464. package/types/FrappeKPICard.d.ts.map +1 -1
  465. package/types/FrappeKanban.d.ts.map +1 -1
  466. package/types/FrappeLikedBy.d.ts.map +1 -1
  467. package/types/FrappeList.d.ts.map +1 -1
  468. package/types/FrappeListView.d.ts.map +1 -1
  469. package/types/FrappeProvider.d.ts +18 -1
  470. package/types/FrappeProvider.d.ts.map +1 -1
  471. package/types/FrappeReportBuilder.d.ts.map +1 -1
  472. package/types/FrappeReportView.d.ts +22 -15
  473. package/types/FrappeReportView.d.ts.map +1 -1
  474. package/types/FrappeTable.d.ts +21 -2
  475. package/types/FrappeTable.d.ts.map +1 -1
  476. package/types/FrappeTreeView.d.ts.map +1 -1
  477. package/types/SyncErrorNotice.d.ts +38 -0
  478. package/types/SyncErrorNotice.d.ts.map +1 -0
  479. package/types/commentActions.d.ts +46 -0
  480. package/types/commentActions.d.ts.map +1 -0
  481. package/types/components/attachments.d.ts.map +1 -1
  482. package/types/components/tags.d.ts.map +1 -1
  483. package/types/components/timeline.d.ts.map +1 -1
  484. package/types/desk/DeskFormSidebar.d.ts.map +1 -1
  485. package/types/fieldRegistry.d.ts.map +1 -1
  486. package/types/fields/Date/index.d.ts +22 -12
  487. package/types/fields/Date/index.d.ts.map +1 -1
  488. package/types/fields/Date/index.native.d.ts +14 -0
  489. package/types/fields/Date/index.native.d.ts.map +1 -0
  490. package/types/fields/Date/types.d.ts +27 -0
  491. package/types/fields/Date/types.d.ts.map +1 -0
  492. package/types/fields/Date/wire.d.ts +13 -9
  493. package/types/fields/Date/wire.d.ts.map +1 -1
  494. package/types/fields/DynamicLink.d.ts.map +1 -1
  495. package/types/fields/FileUpload.d.ts.map +1 -1
  496. package/types/fields/TableMultiSelect.d.ts.map +1 -1
  497. package/types/frappeStoryTarget.d.ts +19 -1
  498. package/types/frappeStoryTarget.d.ts.map +1 -1
  499. package/types/frappeUsers.d.ts.map +1 -1
  500. package/types/index.d.ts +6 -2
  501. package/types/index.d.ts.map +1 -1
  502. package/types/liveErrors.d.ts +4 -0
  503. package/types/liveErrors.d.ts.map +1 -1
  504. package/types/useFrappeAggregate.d.ts +4 -0
  505. package/types/useFrappeAggregate.d.ts.map +1 -1
  506. package/types/useFrappeFacets.d.ts +143 -0
  507. package/types/useFrappeFacets.d.ts.map +1 -0
  508. package/dist/cjs/fields/Date/date.native.js +0 -277
  509. package/dist/cjs/fields/Date/date.native.js.map +0 -1
  510. package/dist/esm/fields/Date/date.native.js +0 -238
  511. package/dist/esm/fields/Date/date.native.js.map +0 -1
  512. package/dist/jsx/fields/Date/date.native.js +0 -277
  513. package/dist/jsx/fields/Date/date.native.js.map +0 -1
  514. package/src/fields/Date/date.native.tsx +0 -299
  515. package/types/fields/Date/date.native.d.ts +0 -19
  516. package/types/fields/Date/date.native.d.ts.map +0 -1
@@ -0,0 +1,436 @@
1
+ // useFrappeFacets — fixture-mode counts (InMemoryFixtureProvider +
2
+ // matchesFilters), live-mode RPC payloads (scoped fetch mock over the two
3
+ // desk endpoints), the exclude-own-field rule, stale-while-visible
4
+ // revalidation, and the promoted desk-parity helpers.
5
+ import { InMemoryFixtureProvider } from "@multiplatform.one/frappe";
6
+ import { act, cleanup, renderHook, waitFor } from "@testing-library/react";
7
+ import type { ReactNode } from "react";
8
+ import { afterEach, describe, expect, it, vi } from "vitest";
9
+ import { FrappeProvider } from "./FrappeProvider";
10
+ import {
11
+ NO_TAGS_LABEL,
12
+ filtersExcludingField,
13
+ isFacetFilterActive,
14
+ scrubUserFacetRows,
15
+ toggleFacetFilter,
16
+ useFrappeFacets,
17
+ type FrappeFacetFilters,
18
+ } from "./useFrappeFacets";
19
+
20
+ const FIXTURE_HOST = "https://facets.spec.test";
21
+
22
+ const taskDocs = [
23
+ {
24
+ name: "TASK-1",
25
+ doctype: "QaTask",
26
+ owner: "alice@example.com",
27
+ _assign: '["alice@example.com"]',
28
+ _user_tags: "urgent,backend",
29
+ status: "Open",
30
+ module: "Core",
31
+ },
32
+ {
33
+ name: "TASK-2",
34
+ doctype: "QaTask",
35
+ owner: "alice@example.com",
36
+ _assign: '["bob@example.com","alice@example.com"]',
37
+ _user_tags: "urgent",
38
+ status: "Open",
39
+ module: "Core",
40
+ },
41
+ {
42
+ name: "TASK-3",
43
+ doctype: "QaTask",
44
+ owner: "bob@example.com",
45
+ _assign: "",
46
+ _user_tags: "",
47
+ status: "Working",
48
+ module: "Website",
49
+ },
50
+ {
51
+ name: "TASK-4",
52
+ doctype: "QaTask",
53
+ owner: "carol@example.com",
54
+ _assign: '["alice@example.com"]',
55
+ _user_tags: "frontend",
56
+ status: "Closed",
57
+ module: "Core",
58
+ },
59
+ ];
60
+
61
+ function fixtureWrapper(): ({ children }: { children: ReactNode }) => ReactNode {
62
+ const fixtures = new InMemoryFixtureProvider({ QaTask: taskDocs });
63
+ return ({ children }: { children: ReactNode }) => (
64
+ <FrappeProvider baseURL={FIXTURE_HOST} fixtures={fixtures}>
65
+ {children}
66
+ </FrappeProvider>
67
+ );
68
+ }
69
+
70
+ afterEach(() => {
71
+ cleanup();
72
+ vi.restoreAllMocks();
73
+ });
74
+
75
+ describe("useFrappeFacets", () => {
76
+ describe("fixture mode", () => {
77
+ it("counts assignments from the JSON-array _assign column", async () => {
78
+ const { result } = renderHook(() => useFrappeFacets("QaTask", []), {
79
+ wrapper: fixtureWrapper(),
80
+ });
81
+ await waitFor(() => expect(result.current.assigned.isLoading).toBe(false));
82
+ expect(result.current.assigned.options).toEqual([
83
+ { value: "alice@example.com", count: 3 },
84
+ { value: "bob@example.com", count: 1 },
85
+ ]);
86
+ });
87
+
88
+ it("counts owners sorted by count then value", async () => {
89
+ const { result } = renderHook(() => useFrappeFacets("QaTask", []), {
90
+ wrapper: fixtureWrapper(),
91
+ });
92
+ await waitFor(() => expect(result.current.owner.isLoading).toBe(false));
93
+ expect(result.current.owner.options).toEqual([
94
+ { value: "alice@example.com", count: 2 },
95
+ { value: "bob@example.com", count: 1 },
96
+ { value: "carol@example.com", count: 1 },
97
+ ]);
98
+ });
99
+
100
+ it("splits comma tags and appends the synthetic No Tags row", async () => {
101
+ const { result } = renderHook(() => useFrappeFacets("QaTask", []), {
102
+ wrapper: fixtureWrapper(),
103
+ });
104
+ await waitFor(() => expect(result.current.tags.isLoading).toBe(false));
105
+ expect(result.current.tags.options).toEqual([
106
+ { value: "urgent", count: 2 },
107
+ { value: "backend", count: 1 },
108
+ { value: "frontend", count: 1 },
109
+ { value: NO_TAGS_LABEL, count: 1, isNoTags: true },
110
+ ]);
111
+ });
112
+
113
+ it("serves the status facet when statusFieldname is set", async () => {
114
+ const { result } = renderHook(
115
+ () => useFrappeFacets("QaTask", [], { statusFieldname: "status" }),
116
+ { wrapper: fixtureWrapper() },
117
+ );
118
+ await waitFor(() => expect(result.current.status.isLoading).toBe(false));
119
+ expect(result.current.status.options).toEqual([
120
+ { value: "Open", count: 2 },
121
+ { value: "Closed", count: 1 },
122
+ { value: "Working", count: 1 },
123
+ ]);
124
+ });
125
+
126
+ it("keeps the status facet idle without a statusFieldname", async () => {
127
+ const { result } = renderHook(() => useFrappeFacets("QaTask", []), {
128
+ wrapper: fixtureWrapper(),
129
+ });
130
+ await waitFor(() => expect(result.current.assigned.isLoading).toBe(false));
131
+ expect(result.current.status).toEqual({ isLoading: false, isStale: false });
132
+ });
133
+
134
+ it("serves arbitrary column facets (e.g. module)", async () => {
135
+ const { result } = renderHook(
136
+ () => useFrappeFacets("QaTask", [], { columns: ["module"] }),
137
+ { wrapper: fixtureWrapper() },
138
+ );
139
+ await waitFor(() => expect(result.current.columns.module?.isLoading).toBe(false));
140
+ expect(result.current.columns.module?.options).toEqual([
141
+ { value: "Core", count: 3 },
142
+ { value: "Website", count: 1 },
143
+ ]);
144
+ });
145
+
146
+ it("excludes each facet's own fieldname from the applied filters", async () => {
147
+ // A filter on owner narrows the OTHER facets but not the owner facet
148
+ // itself (desk group-by sidebar parity).
149
+ const filters: FrappeFacetFilters = [["owner", "=", "alice@example.com"]];
150
+ const { result } = renderHook(() => useFrappeFacets("QaTask", filters), {
151
+ wrapper: fixtureWrapper(),
152
+ });
153
+ await waitFor(() => expect(result.current.owner.isLoading).toBe(false));
154
+ await waitFor(() => expect(result.current.tags.isLoading).toBe(false));
155
+ // Owner facet ignores its own filter — all three owners stay counted.
156
+ expect(result.current.owner.options?.length).toBe(3);
157
+ // Tags facet respects it — only alice's two tasks contribute.
158
+ expect(result.current.tags.options).toEqual([
159
+ { value: "urgent", count: 2 },
160
+ { value: "backend", count: 1 },
161
+ ]);
162
+ });
163
+
164
+ it("revalidates stale-while-visible on a filter change", async () => {
165
+ const wrapper = fixtureWrapper();
166
+ const { result, rerender } = renderHook(
167
+ ({ filters }: { filters: FrappeFacetFilters }) => useFrappeFacets("QaTask", filters),
168
+ { wrapper, initialProps: { filters: [] as FrappeFacetFilters } },
169
+ );
170
+ await waitFor(() => expect(result.current.tags.isLoading).toBe(false));
171
+ const before = result.current.tags.options;
172
+ expect(before?.length).toBeGreaterThan(0);
173
+
174
+ rerender({ filters: [["status", "=", "Open"]] });
175
+ // The prior options stay VISIBLE while the refetch is in flight —
176
+ // isStale flags the revalidation, isLoading stays false.
177
+ expect(result.current.tags.options).toBe(before);
178
+ expect(result.current.tags.isLoading).toBe(false);
179
+ await waitFor(() => expect(result.current.tags.isStale).toBe(false));
180
+ expect(result.current.tags.options).toEqual([
181
+ { value: "urgent", count: 2 },
182
+ { value: "backend", count: 1 },
183
+ ]);
184
+ });
185
+
186
+ it("resets hard on a doctype swap (no stale foreign options)", async () => {
187
+ const fixtures = new InMemoryFixtureProvider({
188
+ QaTask: taskDocs,
189
+ QaNote: [{ name: "NOTE-1", doctype: "QaNote", owner: "dave@example.com" }],
190
+ });
191
+ const wrapper = ({ children }: { children: ReactNode }) => (
192
+ <FrappeProvider baseURL={FIXTURE_HOST} fixtures={fixtures}>
193
+ {children}
194
+ </FrappeProvider>
195
+ );
196
+ const { result, rerender } = renderHook(
197
+ ({ doctype }: { doctype: string }) => useFrappeFacets(doctype, []),
198
+ { wrapper, initialProps: { doctype: "QaTask" } },
199
+ );
200
+ await waitFor(() => expect(result.current.owner.isLoading).toBe(false));
201
+ rerender({ doctype: "QaNote" });
202
+ // Hard reset: the swap must not show QaTask owners under QaNote.
203
+ expect(result.current.owner.options).toBeUndefined();
204
+ expect(result.current.owner.isLoading).toBe(true);
205
+ await waitFor(() => expect(result.current.owner.isLoading).toBe(false));
206
+ expect(result.current.owner.options).toEqual([{ value: "dave@example.com", count: 1 }]);
207
+ });
208
+
209
+ it("refresh() refetches in place", async () => {
210
+ const fixtures = new InMemoryFixtureProvider({ QaTask: taskDocs });
211
+ const getList = vi.spyOn(fixtures, "getList");
212
+ const wrapper = ({ children }: { children: ReactNode }) => (
213
+ <FrappeProvider baseURL={FIXTURE_HOST} fixtures={fixtures}>
214
+ {children}
215
+ </FrappeProvider>
216
+ );
217
+ const { result } = renderHook(() => useFrappeFacets("QaTask", []), { wrapper });
218
+ await waitFor(() => expect(result.current.assigned.isLoading).toBe(false));
219
+ const callsBefore = getList.mock.calls.length;
220
+ act(() => result.current.refresh());
221
+ await waitFor(() => expect(getList.mock.calls.length).toBeGreaterThan(callsBefore));
222
+ await waitFor(() => expect(result.current.assigned.isStale).toBe(false));
223
+ expect(result.current.assigned.options?.length).toBe(2);
224
+ });
225
+ });
226
+
227
+ describe("live mode (desk RPC payloads)", () => {
228
+ const LIVE_HOST = "https://facets-live.spec.test";
229
+
230
+ function mockDeskEndpoints() {
231
+ return vi.spyOn(globalThis, "fetch").mockImplementation(async (input, init) => {
232
+ const url = String(input);
233
+ if (!url.startsWith(LIVE_HOST)) {
234
+ throw new Error(`unexpected fetch ${url}`);
235
+ }
236
+ const body = JSON.parse(String(init?.body ?? "{}")) as Record<string, string>;
237
+ if (url.includes("frappe.desk.listview.get_group_by_count")) {
238
+ const rows =
239
+ body.field === "assigned_to"
240
+ ? [
241
+ { name: "alice@example.com", count: 3 },
242
+ { name: "Administrator", count: 2 },
243
+ { name: null, count: 5 },
244
+ { name: "bob@example.com", count: 0 },
245
+ ]
246
+ : [
247
+ { name: "carol@example.com", count: 4 },
248
+ { name: "Guest", count: 1 },
249
+ ];
250
+ return new Response(JSON.stringify({ message: rows }), {
251
+ status: 200,
252
+ headers: { "Content-Type": "application/json" },
253
+ });
254
+ }
255
+ if (url.includes("frappe.desk.reportview.get_sidebar_stats")) {
256
+ const stats = JSON.parse(body.stats) as string[];
257
+ const fieldname = stats[0];
258
+ const entries =
259
+ fieldname === "_user_tags"
260
+ ? [
261
+ ["urgent", 2],
262
+ [NO_TAGS_LABEL, 1],
263
+ ]
264
+ : [
265
+ ["Website", 1],
266
+ ["Core", 3],
267
+ ];
268
+ return new Response(JSON.stringify({ message: { stats: { [fieldname]: entries } } }), {
269
+ status: 200,
270
+ headers: { "Content-Type": "application/json" },
271
+ });
272
+ }
273
+ throw new Error(`unexpected method ${url}`);
274
+ });
275
+ }
276
+
277
+ function liveWrapper({ children }: { children: ReactNode }) {
278
+ return <FrappeProvider baseURL={LIVE_HOST}>{children}</FrappeProvider>;
279
+ }
280
+
281
+ it("maps get_group_by_count rows and drops null/zero groups unscrubbed", async () => {
282
+ mockDeskEndpoints();
283
+ const { result } = renderHook(() => useFrappeFacets("QaTask", []), {
284
+ wrapper: liveWrapper,
285
+ });
286
+ await waitFor(() => expect(result.current.assigned.isLoading).toBe(false));
287
+ // Null groups and zero counts dropped; Administrator KEPT (the scrub
288
+ // is a render-time concern — scrubUserFacetRows).
289
+ expect(result.current.assigned.options).toEqual([
290
+ { value: "alice@example.com", count: 3 },
291
+ { value: "Administrator", count: 2 },
292
+ ]);
293
+ await waitFor(() => expect(result.current.owner.isLoading).toBe(false));
294
+ expect(result.current.owner.options).toEqual([
295
+ { value: "carol@example.com", count: 4 },
296
+ { value: "Guest", count: 1 },
297
+ ]);
298
+ });
299
+
300
+ it("JSON-encodes the RPC params and excludes each facet's own field", async () => {
301
+ const fetchMock = mockDeskEndpoints();
302
+ const filters: FrappeFacetFilters = [
303
+ ["owner", "=", "alice@example.com"],
304
+ ["status", "=", "Open"],
305
+ ];
306
+ const { result } = renderHook(
307
+ () => useFrappeFacets("QaTask", filters, { statusFieldname: "status" }),
308
+ { wrapper: liveWrapper },
309
+ );
310
+ await waitFor(() => expect(result.current.status.isLoading).toBe(false));
311
+
312
+ const bodies = fetchMock.mock.calls.map(([input, init]) => ({
313
+ url: String(input),
314
+ body: JSON.parse(String(init?.body ?? "{}")) as Record<string, string>,
315
+ }));
316
+ const ownerCall = bodies.find(
317
+ (call) =>
318
+ call.url.includes("get_group_by_count") &&
319
+ call.body.field === "owner" &&
320
+ call.body.current_filters !== "[]",
321
+ );
322
+ // The tuple on the facet's own fieldname is excluded; others survive.
323
+ expect(ownerCall?.body.current_filters).toBe(JSON.stringify([["status", "=", "Open"]]));
324
+
325
+ const statusCall = bodies.find(
326
+ (call) => call.url.includes("get_sidebar_stats") && call.body.stats?.includes("status"),
327
+ );
328
+ // stats and filters ride as JSON-encoded STRINGS (the endpoint
329
+ // json.loads() them and 500s on raw arrays).
330
+ expect(statusCall?.body.stats).toBe(JSON.stringify(["status"]));
331
+ expect(statusCall?.body.filters).toBe(
332
+ JSON.stringify([["owner", "=", "alice@example.com"]]),
333
+ );
334
+ });
335
+
336
+ it("maps tag stats with the synthetic No Tags row flagged", async () => {
337
+ mockDeskEndpoints();
338
+ const { result } = renderHook(() => useFrappeFacets("QaTask", []), {
339
+ wrapper: liveWrapper,
340
+ });
341
+ await waitFor(() => expect(result.current.tags.isLoading).toBe(false));
342
+ expect(result.current.tags.options).toEqual([
343
+ { value: "urgent", count: 2, isNoTags: false },
344
+ { value: NO_TAGS_LABEL, count: 1, isNoTags: true },
345
+ ]);
346
+ });
347
+
348
+ it("sorts column facet stats by count descending", async () => {
349
+ mockDeskEndpoints();
350
+ const { result } = renderHook(
351
+ () => useFrappeFacets("QaTask", [], { columns: ["module"] }),
352
+ { wrapper: liveWrapper },
353
+ );
354
+ await waitFor(() => expect(result.current.columns.module?.isLoading).toBe(false));
355
+ expect(result.current.columns.module?.options).toEqual([
356
+ { value: "Core", count: 3 },
357
+ { value: "Website", count: 1 },
358
+ ]);
359
+ });
360
+
361
+ it("surfaces per-facet errors without dropping the other facets", async () => {
362
+ vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => {
363
+ const url = String(input);
364
+ if (url.includes("get_group_by_count")) {
365
+ // 403: permission errors do not retry. 5xx would sit in
366
+ // HttpClient's backoff and leave isLoading true past waitFor.
367
+ return new Response("boom", { status: 403 });
368
+ }
369
+ return new Response(
370
+ JSON.stringify({ message: { stats: { _user_tags: [["urgent", 1]] } } }),
371
+ { status: 200, headers: { "Content-Type": "application/json" } },
372
+ );
373
+ });
374
+ const { result } = renderHook(() => useFrappeFacets("QaTask", []), {
375
+ wrapper: liveWrapper,
376
+ });
377
+ await waitFor(() => expect(result.current.assigned.isLoading).toBe(false));
378
+ await waitFor(() => expect(result.current.tags.isLoading).toBe(false));
379
+ expect(result.current.assigned.error).toBeInstanceOf(Error);
380
+ expect(result.current.tags.error).toBeUndefined();
381
+ expect(result.current.tags.options).toEqual([
382
+ { value: "urgent", count: 1, isNoTags: false },
383
+ ]);
384
+ });
385
+ });
386
+
387
+ describe("desk-parity helpers", () => {
388
+ it("filtersExcludingField removes only the facet's own tuples", () => {
389
+ const filters: FrappeFacetFilters = [
390
+ ["owner", "=", "a"],
391
+ ["status", "=", "Open"],
392
+ ["owner", "like", "%b%"],
393
+ ];
394
+ expect(filtersExcludingField(filters, "owner")).toEqual([["status", "=", "Open"]]);
395
+ });
396
+
397
+ it("isFacetFilterActive matches tuples case-insensitively on operator", () => {
398
+ const filters: FrappeFacetFilters = [["_assign", "LIKE", "%alice%"]];
399
+ expect(isFacetFilterActive(filters, ["_assign", "like", "%alice%"])).toBe(true);
400
+ expect(isFacetFilterActive(filters, ["_assign", "like", "%bob%"])).toBe(false);
401
+ });
402
+
403
+ it("toggleFacetFilter replaces same-field tuples and toggles off", () => {
404
+ const open: FrappeFacetFilters = [["status", "=", "Open"]];
405
+ // Replace: a different value on the same fieldname swaps in.
406
+ expect(toggleFacetFilter(open, ["status", "=", "Closed"])).toEqual([
407
+ ["status", "=", "Closed"],
408
+ ]);
409
+ // Toggle off: the active tuple clears the facet.
410
+ expect(toggleFacetFilter(open, ["status", "=", "Open"])).toEqual([]);
411
+ // Other fieldnames survive.
412
+ expect(
413
+ toggleFacetFilter([["owner", "=", "a"]], ["status", "=", "Open"]),
414
+ ).toEqual([
415
+ ["owner", "=", "a"],
416
+ ["status", "=", "Open"],
417
+ ]);
418
+ });
419
+
420
+ it("scrubUserFacetRows pins the session user and hides system users", () => {
421
+ const rows = [
422
+ { value: "Administrator", count: 9 },
423
+ { value: "alice@example.com", count: 3 },
424
+ { value: "Guest", count: 2 },
425
+ { value: "bob@example.com", count: 1 },
426
+ ];
427
+ // Session user pinned first ("Me"); Guest/Administrator scrubbed.
428
+ expect(scrubUserFacetRows(rows, "alice@example.com")).toEqual([
429
+ { value: "alice@example.com", count: 3 },
430
+ { value: "bob@example.com", count: 1 },
431
+ ]);
432
+ // Unknown session: untouched (Administrator-only benches keep rows).
433
+ expect(scrubUserFacetRows(rows, undefined)).toEqual(rows);
434
+ });
435
+ });
436
+ });