@multiplatform.one/frappe-ui 7.7.6 → 7.8.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 (491) hide show
  1. package/dist/cjs/ChartCard.native.js.map +1 -1
  2. package/dist/cjs/FileBrowser/AttachLibrary.native.js.map +1 -1
  3. package/dist/cjs/FileBrowser/folderTree.native.js.map +1 -1
  4. package/dist/cjs/FileBrowser/index.native.js.map +1 -1
  5. package/dist/cjs/FileBrowser/types.native.js.map +1 -1
  6. package/dist/cjs/FileBrowser/viewPersistence.native.js.map +1 -1
  7. package/dist/cjs/FormSidebar.native.js.map +1 -1
  8. package/dist/cjs/FrappeAssignment.native.js.map +1 -1
  9. package/dist/cjs/FrappeCalendar.cjs +20 -17
  10. package/dist/cjs/FrappeCalendar.native.js +22 -21
  11. package/dist/cjs/FrappeCalendar.native.js.map +1 -1
  12. package/dist/cjs/FrappeChartCard.native.js.map +1 -1
  13. package/dist/cjs/FrappeConnectionIndicator.native.js.map +1 -1
  14. package/dist/cjs/FrappeDashboard.native.js.map +1 -1
  15. package/dist/cjs/FrappeForm.cjs +1 -1
  16. package/dist/cjs/FrappeForm.native.js +2 -2
  17. package/dist/cjs/FrappeForm.native.js.map +1 -1
  18. package/dist/cjs/FrappeGantt.cjs +25 -34
  19. package/dist/cjs/FrappeGantt.native.js +27 -38
  20. package/dist/cjs/FrappeGantt.native.js.map +1 -1
  21. package/dist/cjs/FrappeImageView.cjs +4 -2
  22. package/dist/cjs/FrappeImageView.native.js +8 -4
  23. package/dist/cjs/FrappeImageView.native.js.map +1 -1
  24. package/dist/cjs/FrappeKPICard.cjs +26 -1
  25. package/dist/cjs/FrappeKPICard.native.js +26 -1
  26. package/dist/cjs/FrappeKPICard.native.js.map +1 -1
  27. package/dist/cjs/FrappeKanban.cjs +16 -17
  28. package/dist/cjs/FrappeKanban.native.js +18 -21
  29. package/dist/cjs/FrappeKanban.native.js.map +1 -1
  30. package/dist/cjs/FrappeLikedBy.cjs +1 -1
  31. package/dist/cjs/FrappeLikedBy.native.js +1 -1
  32. package/dist/cjs/FrappeLikedBy.native.js.map +1 -1
  33. package/dist/cjs/FrappeList.cjs +2 -2
  34. package/dist/cjs/FrappeList.native.js +2 -2
  35. package/dist/cjs/FrappeList.native.js.map +1 -1
  36. package/dist/cjs/FrappeListView.native.js.map +1 -1
  37. package/dist/cjs/FrappeProvider.native.js.map +1 -1
  38. package/dist/cjs/FrappeReconnectIndicator.native.js.map +1 -1
  39. package/dist/cjs/FrappeReportBuilder.cjs +2 -2
  40. package/dist/cjs/FrappeReportBuilder.native.js +2 -2
  41. package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
  42. package/dist/cjs/FrappeReportView.native.js +3 -3
  43. package/dist/cjs/FrappeReportView.native.js.map +1 -1
  44. package/dist/cjs/FrappeTable.cjs +35 -38
  45. package/dist/cjs/FrappeTable.native.js +38 -39
  46. package/dist/cjs/FrappeTable.native.js.map +1 -1
  47. package/dist/cjs/FrappeTreeView.cjs +85 -50
  48. package/dist/cjs/FrappeTreeView.native.js +129 -50
  49. package/dist/cjs/FrappeTreeView.native.js.map +1 -1
  50. package/dist/cjs/SyncErrorNotice.native.js.map +1 -1
  51. package/dist/cjs/commentActions.cjs +1 -1
  52. package/dist/cjs/commentActions.native.js +1 -1
  53. package/dist/cjs/commentActions.native.js.map +1 -1
  54. package/dist/cjs/components/attachments.native.js.map +1 -1
  55. package/dist/cjs/components/tags.native.js.map +1 -1
  56. package/dist/cjs/components/timeline.cjs +1 -1
  57. package/dist/cjs/components/timeline.native.js +1 -1
  58. package/dist/cjs/components/timeline.native.js.map +1 -1
  59. package/dist/cjs/dependsOn.native.js.map +1 -1
  60. package/dist/cjs/desk/DeskFormPage.native.js.map +1 -1
  61. package/dist/cjs/desk/DeskFormSidebar.native.js.map +1 -1
  62. package/dist/cjs/desk/DeskListPage.native.js.map +1 -1
  63. package/dist/cjs/desk/DeskShell.cjs +2 -0
  64. package/dist/cjs/desk/DeskShell.native.js +2 -0
  65. package/dist/cjs/desk/DeskShell.native.js.map +1 -1
  66. package/dist/cjs/desk/DeskSidebar.native.js.map +1 -1
  67. package/dist/cjs/desk/awesomebar.cjs +1 -1
  68. package/dist/cjs/desk/awesomebar.native.js +1 -1
  69. package/dist/cjs/desk/awesomebar.native.js.map +1 -1
  70. package/dist/cjs/fieldOverrides.native.js.map +1 -1
  71. package/dist/cjs/fieldRegistry.native.js +31 -31
  72. package/dist/cjs/fieldRegistry.native.js.map +1 -1
  73. package/dist/cjs/fields/Barcode/index.native.js.map +1 -1
  74. package/dist/cjs/fields/Check/index.native.js.map +1 -1
  75. package/dist/cjs/fields/CodeEditor/index.native.js.map +1 -1
  76. package/dist/cjs/fields/Color/index.native.js.map +1 -1
  77. package/dist/cjs/fields/Data/index.native.js.map +1 -1
  78. package/dist/cjs/fields/Date/sheetKeyboard.native.js.map +1 -1
  79. package/dist/cjs/fields/Date/types.native.js.map +1 -1
  80. package/dist/cjs/fields/Date/wire.native.js.map +1 -1
  81. package/dist/cjs/fields/DateRange/index.native.js.map +1 -1
  82. package/dist/cjs/fields/Duration/index.native.js.map +1 -1
  83. package/dist/cjs/fields/DynamicLink.native.js.map +1 -1
  84. package/dist/cjs/fields/FileUpload.native.js.map +1 -1
  85. package/dist/cjs/fields/FileUploadField/index.native.js.map +1 -1
  86. package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
  87. package/dist/cjs/fields/HTML/index.native.js.map +1 -1
  88. package/dist/cjs/fields/Heading/index.native.js.map +1 -1
  89. package/dist/cjs/fields/Icon/index.native.js.map +1 -1
  90. package/dist/cjs/fields/Link.native.js.map +1 -1
  91. package/dist/cjs/fields/LinkTree.native.js.map +1 -1
  92. package/dist/cjs/fields/LongText/index.native.js.map +1 -1
  93. package/dist/cjs/fields/MentionInput/index.cjs +4 -1
  94. package/dist/cjs/fields/MentionInput/index.native.js +4 -1
  95. package/dist/cjs/fields/MentionInput/index.native.js.map +1 -1
  96. package/dist/cjs/fields/Numeric/index.native.js.map +1 -1
  97. package/dist/cjs/fields/OTPInput/index.native.js.map +1 -1
  98. package/dist/cjs/fields/Password/index.native.js.map +1 -1
  99. package/dist/cjs/fields/Phone/index.native.js.map +1 -1
  100. package/dist/cjs/fields/Progress/index.native.js.map +1 -1
  101. package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
  102. package/dist/cjs/fields/Rating/index.native.js.map +1 -1
  103. package/dist/cjs/fields/ReadOnly/index.native.js.map +1 -1
  104. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  105. package/dist/cjs/fields/Signature/index.native.js.map +1 -1
  106. package/dist/cjs/fields/Slider/index.native.js.map +1 -1
  107. package/dist/cjs/fields/SmallText/index.native.js.map +1 -1
  108. package/dist/cjs/fields/Switch/index.native.js.map +1 -1
  109. package/dist/cjs/fields/TableMultiSelect.native.js.map +1 -1
  110. package/dist/cjs/fields/TextEditor/types.native.js.map +1 -1
  111. package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
  112. package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
  113. package/dist/cjs/fields/shared/EditorPreviewPanes.native.js.map +1 -1
  114. package/dist/cjs/filterFields.native.js.map +1 -1
  115. package/dist/cjs/formEngine/createPrefill.native.js.map +1 -1
  116. package/dist/cjs/formEngine/dirtyTracking.native.js.map +1 -1
  117. package/dist/cjs/formEngine/doctypeMeta.native.js.map +1 -1
  118. package/dist/cjs/formEngine/fetchFrom.native.js.map +1 -1
  119. package/dist/cjs/formEngine/fieldPermissions.native.js.map +1 -1
  120. package/dist/cjs/formEngine/formActions.native.js.map +1 -1
  121. package/dist/cjs/formEngine/formLayout.native.js.map +1 -1
  122. package/dist/cjs/formEngine/index.native.js.map +1 -1
  123. package/dist/cjs/formEngine/useFormEngine.native.js.map +1 -1
  124. package/dist/cjs/frappeEventColor.native.js.map +1 -1
  125. package/dist/cjs/frappeStoryTarget.native.js.map +1 -1
  126. package/dist/cjs/frappeUsers.native.js.map +1 -1
  127. package/dist/cjs/index.cjs +4 -0
  128. package/dist/cjs/index.native.js +38 -34
  129. package/dist/cjs/index.native.js.map +1 -1
  130. package/dist/cjs/liveErrors.native.js.map +1 -1
  131. package/dist/cjs/resolveControl.native.js.map +1 -1
  132. package/dist/cjs/schemaForm/SchemaWizard.native.js.map +1 -1
  133. package/dist/cjs/schemaForm/applyFieldOrder.native.js.map +1 -1
  134. package/dist/cjs/schemaForm/index.cjs +10 -9
  135. package/dist/cjs/schemaForm/index.native.js +11 -10
  136. package/dist/cjs/schemaForm/index.native.js.map +1 -1
  137. package/dist/cjs/shared/devWarn.cjs +40 -0
  138. package/dist/cjs/shared/devWarn.native.js +45 -0
  139. package/dist/cjs/shared/devWarn.native.js.map +1 -0
  140. package/dist/cjs/shared/useKeyboardInset.native.js.map +1 -1
  141. package/dist/cjs/syncError.native.js.map +1 -1
  142. package/dist/cjs/useFrappeAggregate.cjs +15 -8
  143. package/dist/cjs/useFrappeAggregate.native.js +15 -9
  144. package/dist/cjs/useFrappeAggregate.native.js.map +1 -1
  145. package/dist/cjs/useFrappeFacets.cjs +10 -9
  146. package/dist/cjs/useFrappeFacets.native.js +10 -10
  147. package/dist/cjs/useFrappeFacets.native.js.map +1 -1
  148. package/dist/cjs/useFrappePagination.native.js.map +1 -1
  149. package/dist/cjs/useFrappeRows.cjs +113 -0
  150. package/dist/cjs/useFrappeRows.native.js +129 -0
  151. package/dist/cjs/useFrappeRows.native.js.map +1 -0
  152. package/dist/esm/ChartCard.native.js.map +1 -1
  153. package/dist/esm/FileBrowser/AttachLibrary.native.js.map +1 -1
  154. package/dist/esm/FileBrowser/folderTree.native.js.map +1 -1
  155. package/dist/esm/FileBrowser/index.native.js.map +1 -1
  156. package/dist/esm/FileBrowser/types.native.js.map +1 -1
  157. package/dist/esm/FileBrowser/viewPersistence.native.js.map +1 -1
  158. package/dist/esm/FormSidebar.native.js.map +1 -1
  159. package/dist/esm/FrappeAssignment.native.js.map +1 -1
  160. package/dist/esm/FrappeCalendar.mjs +20 -17
  161. package/dist/esm/FrappeCalendar.mjs.map +1 -1
  162. package/dist/esm/FrappeCalendar.native.js +22 -21
  163. package/dist/esm/FrappeCalendar.native.js.map +1 -1
  164. package/dist/esm/FrappeChartCard.native.js.map +1 -1
  165. package/dist/esm/FrappeConnectionIndicator.native.js.map +1 -1
  166. package/dist/esm/FrappeDashboard.native.js.map +1 -1
  167. package/dist/esm/FrappeForm.mjs +1 -1
  168. package/dist/esm/FrappeForm.mjs.map +1 -1
  169. package/dist/esm/FrappeForm.native.js +1 -1
  170. package/dist/esm/FrappeForm.native.js.map +1 -1
  171. package/dist/esm/FrappeGantt.mjs +27 -36
  172. package/dist/esm/FrappeGantt.mjs.map +1 -1
  173. package/dist/esm/FrappeGantt.native.js +29 -40
  174. package/dist/esm/FrappeGantt.native.js.map +1 -1
  175. package/dist/esm/FrappeImageView.mjs +5 -3
  176. package/dist/esm/FrappeImageView.mjs.map +1 -1
  177. package/dist/esm/FrappeImageView.native.js +9 -5
  178. package/dist/esm/FrappeImageView.native.js.map +1 -1
  179. package/dist/esm/FrappeKPICard.mjs +27 -2
  180. package/dist/esm/FrappeKPICard.mjs.map +1 -1
  181. package/dist/esm/FrappeKPICard.native.js +27 -2
  182. package/dist/esm/FrappeKPICard.native.js.map +1 -1
  183. package/dist/esm/FrappeKanban.mjs +16 -17
  184. package/dist/esm/FrappeKanban.mjs.map +1 -1
  185. package/dist/esm/FrappeKanban.native.js +18 -21
  186. package/dist/esm/FrappeKanban.native.js.map +1 -1
  187. package/dist/esm/FrappeLikedBy.mjs +1 -1
  188. package/dist/esm/FrappeLikedBy.mjs.map +1 -1
  189. package/dist/esm/FrappeLikedBy.native.js +1 -1
  190. package/dist/esm/FrappeLikedBy.native.js.map +1 -1
  191. package/dist/esm/FrappeList.mjs +2 -2
  192. package/dist/esm/FrappeList.mjs.map +1 -1
  193. package/dist/esm/FrappeList.native.js +2 -2
  194. package/dist/esm/FrappeList.native.js.map +1 -1
  195. package/dist/esm/FrappeListView.native.js.map +1 -1
  196. package/dist/esm/FrappeProvider.mjs.map +1 -1
  197. package/dist/esm/FrappeProvider.native.js.map +1 -1
  198. package/dist/esm/FrappeReconnectIndicator.native.js.map +1 -1
  199. package/dist/esm/FrappeReportBuilder.mjs +2 -2
  200. package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
  201. package/dist/esm/FrappeReportBuilder.native.js +2 -2
  202. package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
  203. package/dist/esm/FrappeReportView.native.js.map +1 -1
  204. package/dist/esm/FrappeTable.mjs +36 -39
  205. package/dist/esm/FrappeTable.mjs.map +1 -1
  206. package/dist/esm/FrappeTable.native.js +39 -40
  207. package/dist/esm/FrappeTable.native.js.map +1 -1
  208. package/dist/esm/FrappeTreeView.mjs +89 -55
  209. package/dist/esm/FrappeTreeView.mjs.map +1 -1
  210. package/dist/esm/FrappeTreeView.native.js +133 -55
  211. package/dist/esm/FrappeTreeView.native.js.map +1 -1
  212. package/dist/esm/SyncErrorNotice.native.js.map +1 -1
  213. package/dist/esm/commentActions.mjs +1 -1
  214. package/dist/esm/commentActions.mjs.map +1 -1
  215. package/dist/esm/commentActions.native.js +1 -1
  216. package/dist/esm/commentActions.native.js.map +1 -1
  217. package/dist/esm/components/attachments.native.js.map +1 -1
  218. package/dist/esm/components/tags.native.js.map +1 -1
  219. package/dist/esm/components/timeline.mjs +1 -1
  220. package/dist/esm/components/timeline.mjs.map +1 -1
  221. package/dist/esm/components/timeline.native.js +1 -1
  222. package/dist/esm/components/timeline.native.js.map +1 -1
  223. package/dist/esm/dependsOn.native.js.map +1 -1
  224. package/dist/esm/desk/DeskFormPage.native.js.map +1 -1
  225. package/dist/esm/desk/DeskFormSidebar.native.js.map +1 -1
  226. package/dist/esm/desk/DeskListPage.native.js.map +1 -1
  227. package/dist/esm/desk/DeskShell.mjs +2 -0
  228. package/dist/esm/desk/DeskShell.mjs.map +1 -1
  229. package/dist/esm/desk/DeskShell.native.js +2 -0
  230. package/dist/esm/desk/DeskShell.native.js.map +1 -1
  231. package/dist/esm/desk/DeskSidebar.native.js.map +1 -1
  232. package/dist/esm/desk/awesomebar.mjs +1 -1
  233. package/dist/esm/desk/awesomebar.mjs.map +1 -1
  234. package/dist/esm/desk/awesomebar.native.js +1 -1
  235. package/dist/esm/desk/awesomebar.native.js.map +1 -1
  236. package/dist/esm/fieldOverrides.native.js.map +1 -1
  237. package/dist/esm/fieldRegistry.native.js.map +1 -1
  238. package/dist/esm/fields/Barcode/index.native.js.map +1 -1
  239. package/dist/esm/fields/Check/index.native.js.map +1 -1
  240. package/dist/esm/fields/CodeEditor/index.native.js.map +1 -1
  241. package/dist/esm/fields/Color/index.native.js.map +1 -1
  242. package/dist/esm/fields/Data/index.native.js.map +1 -1
  243. package/dist/esm/fields/Date/sheetKeyboard.native.js.map +1 -1
  244. package/dist/esm/fields/Date/types.native.js.map +1 -1
  245. package/dist/esm/fields/Date/wire.native.js.map +1 -1
  246. package/dist/esm/fields/DateRange/index.native.js.map +1 -1
  247. package/dist/esm/fields/Duration/index.native.js.map +1 -1
  248. package/dist/esm/fields/DynamicLink.native.js.map +1 -1
  249. package/dist/esm/fields/FileUpload.native.js.map +1 -1
  250. package/dist/esm/fields/FileUploadField/index.native.js.map +1 -1
  251. package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
  252. package/dist/esm/fields/HTML/index.native.js.map +1 -1
  253. package/dist/esm/fields/Heading/index.native.js.map +1 -1
  254. package/dist/esm/fields/Icon/index.native.js.map +1 -1
  255. package/dist/esm/fields/Link.native.js.map +1 -1
  256. package/dist/esm/fields/LinkTree.native.js.map +1 -1
  257. package/dist/esm/fields/LongText/index.native.js.map +1 -1
  258. package/dist/esm/fields/MentionInput/index.mjs +4 -1
  259. package/dist/esm/fields/MentionInput/index.mjs.map +1 -1
  260. package/dist/esm/fields/MentionInput/index.native.js +4 -1
  261. package/dist/esm/fields/MentionInput/index.native.js.map +1 -1
  262. package/dist/esm/fields/Numeric/index.native.js.map +1 -1
  263. package/dist/esm/fields/OTPInput/index.native.js.map +1 -1
  264. package/dist/esm/fields/Password/index.native.js.map +1 -1
  265. package/dist/esm/fields/Phone/index.native.js.map +1 -1
  266. package/dist/esm/fields/Progress/index.native.js.map +1 -1
  267. package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
  268. package/dist/esm/fields/Rating/index.native.js.map +1 -1
  269. package/dist/esm/fields/ReadOnly/index.native.js.map +1 -1
  270. package/dist/esm/fields/Select/index.native.js.map +1 -1
  271. package/dist/esm/fields/Signature/index.native.js.map +1 -1
  272. package/dist/esm/fields/Slider/index.native.js.map +1 -1
  273. package/dist/esm/fields/SmallText/index.native.js.map +1 -1
  274. package/dist/esm/fields/Switch/index.native.js.map +1 -1
  275. package/dist/esm/fields/TableMultiSelect.native.js.map +1 -1
  276. package/dist/esm/fields/TextEditor/types.native.js.map +1 -1
  277. package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
  278. package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
  279. package/dist/esm/fields/shared/EditorPreviewPanes.native.js.map +1 -1
  280. package/dist/esm/filterFields.native.js.map +1 -1
  281. package/dist/esm/formEngine/createPrefill.native.js.map +1 -1
  282. package/dist/esm/formEngine/dirtyTracking.native.js.map +1 -1
  283. package/dist/esm/formEngine/doctypeMeta.native.js.map +1 -1
  284. package/dist/esm/formEngine/fetchFrom.native.js.map +1 -1
  285. package/dist/esm/formEngine/fieldPermissions.native.js.map +1 -1
  286. package/dist/esm/formEngine/formActions.native.js.map +1 -1
  287. package/dist/esm/formEngine/formLayout.native.js.map +1 -1
  288. package/dist/esm/formEngine/index.native.js.map +1 -1
  289. package/dist/esm/formEngine/useFormEngine.native.js.map +1 -1
  290. package/dist/esm/frappeEventColor.native.js.map +1 -1
  291. package/dist/esm/frappeStoryTarget.native.js.map +1 -1
  292. package/dist/esm/frappeUsers.native.js.map +1 -1
  293. package/dist/esm/index.mjs +2 -1
  294. package/dist/esm/index.mjs.map +1 -1
  295. package/dist/esm/index.native.js +2 -1
  296. package/dist/esm/index.native.js.map +1 -1
  297. package/dist/esm/liveErrors.native.js.map +1 -1
  298. package/dist/esm/resolveControl.native.js.map +1 -1
  299. package/dist/esm/schemaForm/SchemaWizard.native.js.map +1 -1
  300. package/dist/esm/schemaForm/applyFieldOrder.native.js.map +1 -1
  301. package/dist/esm/schemaForm/index.mjs +8 -7
  302. package/dist/esm/schemaForm/index.mjs.map +1 -1
  303. package/dist/esm/schemaForm/index.native.js +8 -7
  304. package/dist/esm/schemaForm/index.native.js.map +1 -1
  305. package/dist/esm/shared/devWarn.mjs +14 -0
  306. package/dist/esm/shared/devWarn.mjs.map +1 -0
  307. package/dist/esm/shared/devWarn.native.js +16 -0
  308. package/dist/esm/shared/devWarn.native.js.map +1 -0
  309. package/dist/esm/shared/useKeyboardInset.native.js.map +1 -1
  310. package/dist/esm/syncError.native.js.map +1 -1
  311. package/dist/esm/useFrappeAggregate.mjs +15 -8
  312. package/dist/esm/useFrappeAggregate.mjs.map +1 -1
  313. package/dist/esm/useFrappeAggregate.native.js +15 -9
  314. package/dist/esm/useFrappeAggregate.native.js.map +1 -1
  315. package/dist/esm/useFrappeFacets.mjs +10 -9
  316. package/dist/esm/useFrappeFacets.mjs.map +1 -1
  317. package/dist/esm/useFrappeFacets.native.js +10 -10
  318. package/dist/esm/useFrappeFacets.native.js.map +1 -1
  319. package/dist/esm/useFrappePagination.native.js.map +1 -1
  320. package/dist/esm/useFrappeRows.mjs +86 -0
  321. package/dist/esm/useFrappeRows.mjs.map +1 -0
  322. package/dist/esm/useFrappeRows.native.js +99 -0
  323. package/dist/esm/useFrappeRows.native.js.map +1 -0
  324. package/dist/jsx/FrappeCalendar.mjs +20 -17
  325. package/dist/jsx/FrappeCalendar.mjs.map +1 -1
  326. package/dist/jsx/FrappeCalendar.native.js +22 -21
  327. package/dist/jsx/FrappeCalendar.native.js.map +1 -1
  328. package/dist/jsx/FrappeForm.mjs +1 -1
  329. package/dist/jsx/FrappeForm.mjs.map +1 -1
  330. package/dist/jsx/FrappeForm.native.js +1 -1
  331. package/dist/jsx/FrappeForm.native.js.map +1 -1
  332. package/dist/jsx/FrappeGantt.mjs +27 -36
  333. package/dist/jsx/FrappeGantt.mjs.map +1 -1
  334. package/dist/jsx/FrappeGantt.native.js +27 -38
  335. package/dist/jsx/FrappeGantt.native.js.map +1 -1
  336. package/dist/jsx/FrappeImageView.mjs +5 -3
  337. package/dist/jsx/FrappeImageView.mjs.map +1 -1
  338. package/dist/jsx/FrappeImageView.native.js +8 -4
  339. package/dist/jsx/FrappeImageView.native.js.map +1 -1
  340. package/dist/jsx/FrappeKPICard.mjs +27 -2
  341. package/dist/jsx/FrappeKPICard.mjs.map +1 -1
  342. package/dist/jsx/FrappeKPICard.native.js +26 -1
  343. package/dist/jsx/FrappeKPICard.native.js.map +1 -1
  344. package/dist/jsx/FrappeKanban.mjs +16 -17
  345. package/dist/jsx/FrappeKanban.mjs.map +1 -1
  346. package/dist/jsx/FrappeKanban.native.js +18 -21
  347. package/dist/jsx/FrappeKanban.native.js.map +1 -1
  348. package/dist/jsx/FrappeLikedBy.mjs +1 -1
  349. package/dist/jsx/FrappeLikedBy.mjs.map +1 -1
  350. package/dist/jsx/FrappeLikedBy.native.js +1 -1
  351. package/dist/jsx/FrappeLikedBy.native.js.map +1 -1
  352. package/dist/jsx/FrappeList.mjs +2 -2
  353. package/dist/jsx/FrappeList.mjs.map +1 -1
  354. package/dist/jsx/FrappeList.native.js +2 -2
  355. package/dist/jsx/FrappeList.native.js.map +1 -1
  356. package/dist/jsx/FrappeProvider.mjs.map +1 -1
  357. package/dist/jsx/FrappeProvider.native.js.map +1 -1
  358. package/dist/jsx/FrappeReportBuilder.mjs +2 -2
  359. package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
  360. package/dist/jsx/FrappeReportBuilder.native.js +2 -2
  361. package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
  362. package/dist/jsx/FrappeTable.mjs +36 -39
  363. package/dist/jsx/FrappeTable.mjs.map +1 -1
  364. package/dist/jsx/FrappeTable.native.js +38 -39
  365. package/dist/jsx/FrappeTable.native.js.map +1 -1
  366. package/dist/jsx/FrappeTreeView.mjs +89 -55
  367. package/dist/jsx/FrappeTreeView.mjs.map +1 -1
  368. package/dist/jsx/FrappeTreeView.native.js +129 -50
  369. package/dist/jsx/FrappeTreeView.native.js.map +1 -1
  370. package/dist/jsx/commentActions.mjs +1 -1
  371. package/dist/jsx/commentActions.mjs.map +1 -1
  372. package/dist/jsx/commentActions.native.js +1 -1
  373. package/dist/jsx/commentActions.native.js.map +1 -1
  374. package/dist/jsx/components/timeline.mjs +1 -1
  375. package/dist/jsx/components/timeline.mjs.map +1 -1
  376. package/dist/jsx/components/timeline.native.js +1 -1
  377. package/dist/jsx/desk/DeskShell.mjs +2 -0
  378. package/dist/jsx/desk/DeskShell.mjs.map +1 -1
  379. package/dist/jsx/desk/DeskShell.native.js +2 -0
  380. package/dist/jsx/desk/DeskShell.native.js.map +1 -1
  381. package/dist/jsx/desk/awesomebar.mjs +1 -1
  382. package/dist/jsx/desk/awesomebar.mjs.map +1 -1
  383. package/dist/jsx/desk/awesomebar.native.js +1 -1
  384. package/dist/jsx/desk/awesomebar.native.js.map +1 -1
  385. package/dist/jsx/fields/MentionInput/index.mjs +4 -1
  386. package/dist/jsx/fields/MentionInput/index.mjs.map +1 -1
  387. package/dist/jsx/fields/MentionInput/index.native.js +4 -1
  388. package/dist/jsx/fields/MentionInput/index.native.js.map +1 -1
  389. package/dist/jsx/index.js +2 -1
  390. package/dist/jsx/index.js.map +1 -1
  391. package/dist/jsx/index.mjs +2 -1
  392. package/dist/jsx/index.mjs.map +1 -1
  393. package/dist/jsx/index.native.js +4 -0
  394. package/dist/jsx/index.native.js.map +1 -1
  395. package/dist/jsx/schemaForm/index.mjs +8 -7
  396. package/dist/jsx/schemaForm/index.mjs.map +1 -1
  397. package/dist/jsx/schemaForm/index.native.js +10 -9
  398. package/dist/jsx/schemaForm/index.native.js.map +1 -1
  399. package/dist/jsx/shared/devWarn.mjs +14 -0
  400. package/dist/jsx/shared/devWarn.mjs.map +1 -0
  401. package/dist/jsx/shared/devWarn.native.js +45 -0
  402. package/dist/jsx/shared/devWarn.native.js.map +1 -0
  403. package/dist/jsx/useFrappeAggregate.mjs +15 -8
  404. package/dist/jsx/useFrappeAggregate.mjs.map +1 -1
  405. package/dist/jsx/useFrappeAggregate.native.js +15 -9
  406. package/dist/jsx/useFrappeAggregate.native.js.map +1 -1
  407. package/dist/jsx/useFrappeFacets.mjs +10 -9
  408. package/dist/jsx/useFrappeFacets.mjs.map +1 -1
  409. package/dist/jsx/useFrappeFacets.native.js +10 -10
  410. package/dist/jsx/useFrappeFacets.native.js.map +1 -1
  411. package/dist/jsx/useFrappeRows.mjs +86 -0
  412. package/dist/jsx/useFrappeRows.mjs.map +1 -0
  413. package/dist/jsx/useFrappeRows.native.js +129 -0
  414. package/dist/jsx/useFrappeRows.native.js.map +1 -0
  415. package/package.json +17 -16
  416. package/src/FileBrowser/FileBrowser.spec.tsx +11 -1
  417. package/src/FrappeCalendar.tsx +25 -22
  418. package/src/FrappeForm.tsx +7 -3
  419. package/src/FrappeGantt.tsx +29 -39
  420. package/src/FrappeImageView.tsx +5 -3
  421. package/src/FrappeKPICard.spec.tsx +128 -0
  422. package/src/FrappeKPICard.tsx +44 -4
  423. package/src/FrappeKanban.tsx +24 -17
  424. package/src/FrappeLikedBy.tsx +1 -1
  425. package/src/FrappeList.tsx +3 -2
  426. package/src/FrappeProvider.fixturesOnly.spec.tsx +129 -0
  427. package/src/FrappeProvider.tsx +16 -6
  428. package/src/FrappeReportBuilder.tsx +4 -2
  429. package/src/FrappeTable.autoHeight.spec.tsx +142 -0
  430. package/src/FrappeTable.rowAccumulator.spec.tsx +110 -0
  431. package/src/FrappeTable.tsx +75 -48
  432. package/src/FrappeTreeView.stories.tsx +59 -0
  433. package/src/FrappeTreeView.tsx +166 -56
  434. package/src/commentActions.ts +1 -1
  435. package/src/components/timeline.tsx +1 -1
  436. package/src/desk/DeskShell.tsx +2 -0
  437. package/src/desk/awesomebar.tsx +5 -2
  438. package/src/fields/CodeEditor/CodeEditor.spec.tsx +10 -9
  439. package/src/fields/Date/typeParity.spec.tsx +19 -9
  440. package/src/fields/Duration/Duration.spec.tsx +73 -0
  441. package/src/fields/MentionInput/MentionInput.spec.tsx +110 -0
  442. package/src/fields/MentionInput/index.tsx +4 -1
  443. package/src/fields/Password/index.tsx +1 -1
  444. package/src/fields/Progress/Progress.spec.tsx +9 -0
  445. package/src/fields/RadioGroup/RadioGroup.spec.tsx +93 -0
  446. package/src/fields/Signature/Signature.spec.tsx +105 -0
  447. package/src/fields/Slider/Slider.spec.tsx +73 -0
  448. package/src/fields/Switch/Switch.spec.tsx +65 -0
  449. package/src/fields/TextEditor/TextEditor.spec.tsx +2 -3
  450. package/src/fields/TextEditor/TextEditor.stories.tsx +31 -1
  451. package/src/fields/ToggleGroup/ToggleGroup.spec.tsx +110 -0
  452. package/src/frappeViews.designLaw.spec.ts +11 -7
  453. package/src/index.ts +13 -0
  454. package/src/schemaForm/index.tsx +6 -7
  455. package/src/shared/devWarn.ts +42 -0
  456. package/src/useFrappeAggregate.ts +17 -11
  457. package/src/useFrappeFacets.ts +17 -10
  458. package/src/useFrappeRows.spec.tsx +176 -0
  459. package/src/useFrappeRows.ts +293 -0
  460. package/types/FrappeCalendar.d.ts.map +1 -1
  461. package/types/FrappeForm.d.ts +6 -2
  462. package/types/FrappeForm.d.ts.map +1 -1
  463. package/types/FrappeGantt.d.ts.map +1 -1
  464. package/types/FrappeImageView.d.ts.map +1 -1
  465. package/types/FrappeKPICard.d.ts +9 -1
  466. package/types/FrappeKPICard.d.ts.map +1 -1
  467. package/types/FrappeKanban.d.ts.map +1 -1
  468. package/types/FrappeList.d.ts.map +1 -1
  469. package/types/FrappeProvider.d.ts +16 -6
  470. package/types/FrappeProvider.d.ts.map +1 -1
  471. package/types/FrappeReportBuilder.d.ts.map +1 -1
  472. package/types/FrappeTable.d.ts +20 -1
  473. package/types/FrappeTable.d.ts.map +1 -1
  474. package/types/FrappeTreeView.d.ts +38 -1
  475. package/types/FrappeTreeView.d.ts.map +1 -1
  476. package/types/desk/DeskShell.d.ts.map +1 -1
  477. package/types/desk/awesomebar.d.ts.map +1 -1
  478. package/types/fields/MentionInput/index.d.ts.map +1 -1
  479. package/types/fields/Password/index.d.ts +1 -1
  480. package/types/fields/Password/index.d.ts.map +1 -1
  481. package/types/index.d.ts +1 -0
  482. package/types/index.d.ts.map +1 -1
  483. package/types/schemaForm/index.d.ts.map +1 -1
  484. package/types/shared/devWarn.d.ts +23 -0
  485. package/types/shared/devWarn.d.ts.map +1 -0
  486. package/types/useFrappeAggregate.d.ts +2 -1
  487. package/types/useFrappeAggregate.d.ts.map +1 -1
  488. package/types/useFrappeFacets.d.ts +2 -1
  489. package/types/useFrappeFacets.d.ts.map +1 -1
  490. package/types/useFrappeRows.d.ts +186 -0
  491. package/types/useFrappeRows.d.ts.map +1 -0
@@ -1,4 +1,3 @@
1
- import { useFrappeCollection, useLiveQuery } from "@multiplatform.one/frappe";
2
1
  import {
3
2
  Calendar,
4
3
  eventChipTextColor,
@@ -18,6 +17,7 @@ import {
18
17
  import { FrappeConnectionIndicator } from "./FrappeConnectionIndicator";
19
18
  import { useFrappeConfig } from "./FrappeProvider";
20
19
  import { describeSyncError } from "./syncError";
20
+ import { useFrappeRows } from "./useFrappeRows";
21
21
  import { resolveFrappeEventColor } from "./frappeEventColor";
22
22
 
23
23
  export type CalendarViewType = "month" | "week" | "day";
@@ -111,9 +111,6 @@ export function FrappeCalendar<TDoc extends { name: string; doctype: string }>({
111
111
  const { knobProps } = useResolvedKnobs();
112
112
  const { t } = useTranslation();
113
113
  const [actionError, setActionError] = useState<Error | null>(null);
114
- // Honest error state: the collection swallows failed subscription fetches
115
- // (calendar would just look empty) — onSyncError reports them here.
116
- const [syncError, setSyncError] = useState<unknown>(null);
117
114
  const height =
118
115
  heightProp ?? (knobProps.sizeToken === "$3" ? 480 : knobProps.sizeToken === "$5" ? 720 : 600);
119
116
 
@@ -121,8 +118,10 @@ export function FrappeCalendar<TDoc extends { name: string; doctype: string }>({
121
118
  const finalAuth = auth ?? config?.auth;
122
119
  const finalSyncOptions = syncOptions ?? config?.syncOptions;
123
120
 
124
- if (!finalBaseURL) {
125
- throw new Error("FrappeCalendar requires baseURL prop or FrappeProvider with baseURL");
121
+ if (!finalBaseURL && !config?.fixtures) {
122
+ throw new Error(
123
+ "FrappeCalendar requires baseURL prop or FrappeProvider with baseURL or fixtures",
124
+ );
126
125
  }
127
126
 
128
127
  // Ensure required fields are fetched
@@ -163,25 +162,29 @@ export function FrappeCalendar<TDoc extends { name: string; doctype: string }>({
163
162
  ],
164
163
  );
165
164
 
166
- const collection = useFrappeCollection<TDoc>(connectionConfig, doctype, {
165
+ // The shared doctype read (MPO-251). `mapError` keeps this calendar's own
166
+ // error copy (describeSyncError); real subscription failures still win over
167
+ // the generic isError flag inside the hook, because they carry an
168
+ // actionable message. `limit: null` keeps the bench's own page length.
169
+ const mapSyncError = useCallback(
170
+ (err: unknown) => new Error(describeSyncError(err, doctype, t)),
171
+ [doctype, t],
172
+ );
173
+ const {
174
+ rows: docs,
175
+ isQueryLoading: isLoading,
176
+ error: queryError,
177
+ collection,
178
+ } = useFrappeRows<TDoc>(connectionConfig, doctype, {
167
179
  fields: effectiveFields,
168
180
  filters,
169
- onSyncError: setSyncError,
181
+ limit: null,
182
+ mapError: mapSyncError,
183
+ messages: {
184
+ permission: t("You do not have permission to view {{doctype}}", { doctype }),
185
+ generic: t("Failed to load {{doctype}} events", { doctype }),
186
+ },
170
187
  });
171
- const {
172
- data: rawData,
173
- isLoading,
174
- isError,
175
- } = useLiveQuery((q) => (collection ? q.from({ _list: collection }) : null), [collection]);
176
- const docs = useMemo(() => (rawData as TDoc[] | undefined) ?? [], [rawData]);
177
- // Real subscription failures (permission-error, network, bad doctype) win
178
- // over the generic isError flag — they carry an actionable message.
179
- const queryError =
180
- syncError != null
181
- ? new Error(describeSyncError(syncError, doctype, t))
182
- : isError
183
- ? new Error(t("Failed to load {{doctype}} events", { doctype }))
184
- : null;
185
188
 
186
189
  // Scheme-aware palette for resolving NAMED doc colors (P-15): Frappe's
187
190
  // palette references ("blue", "Red") map to sanctioned solids per scheme;
@@ -171,11 +171,15 @@ export interface AssignmentTodoRow {
171
171
  reference_name?: string;
172
172
  }
173
173
 
174
- /** Resolves a Frappe file field value (e.g. `/files/x.png`) to a full URL. */
175
- export function resolveFileUrl(value: unknown, baseURL: string): string | undefined {
174
+ /**
175
+ * Resolves a Frappe file field value (e.g. `/files/x.png`) to a full URL.
176
+ * A site-relative path needs a server to hang off, so it resolves to nothing
177
+ * on a fixtures-only host (no baseURL); absolute URLs still pass through.
178
+ */
179
+ export function resolveFileUrl(value: unknown, baseURL: string | undefined): string | undefined {
176
180
  if (!value || typeof value !== "string") return undefined;
177
181
  if (value.startsWith("http://") || value.startsWith("https://")) return value;
178
- if (value.startsWith("/")) return `${baseURL.replace(/\/$/, "")}${value}`;
182
+ if (value.startsWith("/")) return baseURL ? `${baseURL.replace(/\/$/, "")}${value}` : undefined;
179
183
  return value;
180
184
  }
181
185
 
@@ -1,14 +1,13 @@
1
- import { useFrappeCollection, useLiveQuery } from "@multiplatform.one/frappe";
2
1
  import {
2
+ Alert,
3
3
  GanttView,
4
4
  type GanttItemMoveRange,
5
5
  type GanttTask,
6
6
  type GanttZoom,
7
7
  } from "@multiplatform.one/components";
8
8
  import React, { useCallback, useMemo, useState, type ReactNode } from "react";
9
- import { Text, YStack, type YStackProps } from "tamagui";
9
+ import { YStack, type YStackProps } from "tamagui";
10
10
  import { useTranslation } from "react-i18next";
11
- import { formCommonColors } from "@multiplatform.one/forms";
12
11
  import {
13
12
  formatCalendarDate,
14
13
  parseCalendarDate,
@@ -18,6 +17,7 @@ import {
18
17
  import { FrappeConnectionIndicator } from "./FrappeConnectionIndicator";
19
18
  import { useFrappeConfig } from "./FrappeProvider";
20
19
  import { describeSyncError } from "./syncError";
20
+ import { useFrappeRows } from "./useFrappeRows";
21
21
  import { resolveFrappeEventColor } from "./frappeEventColor";
22
22
 
23
23
  export interface FrappeGanttProps<TDoc extends { name: string; doctype: string }> extends Omit<
@@ -113,16 +113,13 @@ export function FrappeGantt<TDoc extends { name: string; doctype: string }>({
113
113
  const { knobProps } = useResolvedKnobs();
114
114
  const { t } = useTranslation();
115
115
  const [actionError, setActionError] = useState<Error | null>(null);
116
- // Honest error state: the collection swallows failed subscription fetches
117
- // (the chart would just look empty) — onSyncError reports them here.
118
- const [syncError, setSyncError] = useState<unknown>(null);
119
116
 
120
117
  const finalBaseURL = baseURL ?? config?.baseURL;
121
118
  const finalAuth = auth ?? config?.auth;
122
119
  const finalSyncOptions = syncOptions ?? config?.syncOptions;
123
120
 
124
- if (!finalBaseURL) {
125
- throw new Error("FrappeGantt requires baseURL prop or FrappeProvider with baseURL");
121
+ if (!finalBaseURL && !config?.fixtures) {
122
+ throw new Error("FrappeGantt requires baseURL prop or FrappeProvider with baseURL or fixtures");
126
123
  }
127
124
 
128
125
  // Ensure required fields are fetched
@@ -162,25 +159,29 @@ export function FrappeGantt<TDoc extends { name: string; doctype: string }>({
162
159
  ],
163
160
  );
164
161
 
165
- const collection = useFrappeCollection<TDoc>(connectionConfig, doctype, {
162
+ // The shared doctype read (MPO-251). `mapError` keeps this chart's own
163
+ // error copy (describeSyncError); real subscription failures still win over
164
+ // the generic isError flag inside the hook, because they carry an
165
+ // actionable message. `limit: null` keeps the bench's own page length.
166
+ const mapSyncError = useCallback(
167
+ (err: unknown) => new Error(describeSyncError(err, doctype, t)),
168
+ [doctype, t],
169
+ );
170
+ const {
171
+ rows: docs,
172
+ isQueryLoading: isLoading,
173
+ error: queryError,
174
+ collection,
175
+ } = useFrappeRows<TDoc>(connectionConfig, doctype, {
166
176
  fields: effectiveFields,
167
177
  filters,
168
- onSyncError: setSyncError,
178
+ limit: null,
179
+ mapError: mapSyncError,
180
+ messages: {
181
+ permission: t("You do not have permission to view {{doctype}}", { doctype }),
182
+ generic: t("Failed to load {{doctype}} tasks", { doctype }),
183
+ },
169
184
  });
170
- const {
171
- data: rawData,
172
- isLoading,
173
- isError,
174
- } = useLiveQuery((q) => (collection ? q.from({ _list: collection }) : null), [collection]);
175
- const docs = useMemo(() => (rawData as TDoc[] | undefined) ?? [], [rawData]);
176
- // Real subscription failures (permission-error, network, bad doctype) win
177
- // over the generic isError flag — they carry an actionable message.
178
- const queryError =
179
- syncError !== null && syncError !== undefined
180
- ? new Error(describeSyncError(syncError, doctype, t))
181
- : isError
182
- ? new Error(t("Failed to load {{doctype}} tasks", { doctype }))
183
- : null;
184
185
 
185
186
  // Scheme-aware palette for resolving NAMED doc colors (P-15 / LC-27):
186
187
  // Frappe's palette references ("blue", "Red") map to sanctioned solids per
@@ -255,23 +256,12 @@ export function FrappeGantt<TDoc extends { name: string; doctype: string }>({
255
256
  <YStack {...knobProps.gap}>
256
257
  <FrappeConnectionIndicator config={connectionConfig} />
257
258
  {actionError ? (
258
- <YStack
259
- {...knobProps.panelPadding}
260
- {...knobProps.borderRadius}
261
- backgroundColor="$red2"
262
- borderColor="$red7"
263
- borderWidth={1}
264
- >
265
- <Text color={formCommonColors.error} {...knobProps.label}>
266
- {t("Action failed")}
267
- </Text>
268
- <Text color={formCommonColors.muted} {...knobProps.label}>
269
- {actionError.message}
270
- </Text>
271
- </YStack>
259
+ <Alert intent="error" title={t("Action failed")}>
260
+ {actionError.message}
261
+ </Alert>
272
262
  ) : null}
273
263
  <GanttView
274
- {...knobProps.borderRadius}
264
+ {...knobProps.containerRadius}
275
265
  tasks={tasks}
276
266
  zoom={zoom}
277
267
  isLoading={isLoading}
@@ -4,7 +4,7 @@ import { ImageSquareIcon } from "@phosphor-icons/react";
4
4
  import React, { useMemo } from "react";
5
5
  import { useTranslation } from "react-i18next";
6
6
  import { Spinner, formCommonColors } from "@multiplatform.one/forms";
7
- import { Image, Text, YStack, type YStackProps } from "tamagui";
7
+ import { Image, Text, YStack, useTheme, type YStackProps } from "tamagui";
8
8
  import { isServer } from "@multiplatform.one/platform";
9
9
  import { useResolvedKnobs } from "@multiplatform.one/theme";
10
10
  import { useFrappeConfig } from "./FrappeProvider";
@@ -167,6 +167,7 @@ function FrappeImageViewClient<TDoc extends { name: string; doctype: string }>({
167
167
  syncOptions,
168
168
  ...stackProps
169
169
  }: FrappeImageViewProps<TDoc>) {
170
+ const theme = useTheme();
170
171
  const config = useFrappeConfig();
171
172
  const { knobProps } = useResolvedKnobs();
172
173
  const { t } = useTranslation();
@@ -273,7 +274,7 @@ function FrappeImageViewClient<TDoc extends { name: string; doctype: string }>({
273
274
  const iconSize = Number(knobProps.controlIcon?.width) || 16;
274
275
  return (
275
276
  <YStack {...knobProps.panelPadding} alignItems="center" {...knobProps.gap} {...stackProps}>
276
- <ImageSquareIcon size={iconSize * 2} color={formCommonColors.muted} />
277
+ <ImageSquareIcon size={iconSize * 2} color={theme.color11?.val ?? "currentColor"} />
277
278
  <Text color={formCommonColors.muted} {...knobProps.body}>
278
279
  {t("No image field configured for {{doctype}}", { doctype })}
279
280
  </Text>
@@ -332,6 +333,7 @@ function FrappeImageCard({
332
333
  onItemClick?: (doc: Partial<unknown>) => void;
333
334
  knobProps: ReturnType<typeof useResolvedKnobs>["knobProps"];
334
335
  }) {
336
+ const theme = useTheme();
335
337
  const imageUrl = getImageUrl(doc[imageField], baseURL);
336
338
  const title = doc[titleField] || doc.name;
337
339
  const subtitle = subtitleField ? doc[subtitleField] : undefined;
@@ -371,7 +373,7 @@ function FrappeImageCard({
371
373
  alignItems="center"
372
374
  justifyContent="center"
373
375
  >
374
- <ImageSquareIcon size={iconSize * 2} color={formCommonColors.muted} />
376
+ <ImageSquareIcon size={iconSize * 2} color={theme.color11?.val ?? "currentColor"} />
375
377
  </YStack>
376
378
  )}
377
379
  <Card.Footer>
@@ -0,0 +1,128 @@
1
+ /**
2
+ * MPO-237: a `dataSource` with no `baseURL` in scope is a misconfiguration,
3
+ * not static mode. The card asked for a computed figure and there is nowhere
4
+ * to compute it, so `config.value` must never be painted as the KPI: a wrong
5
+ * number presented confidently is worse than a visible gap, because nothing
6
+ * downstream can tell it was never computed.
7
+ *
8
+ * Static mode (no `dataSource` at all) is the documented, intended path and
9
+ * is asserted here too, so the fix cannot over-correct into it.
10
+ */
11
+
12
+ import { InMemoryFixtureProvider } from "@multiplatform.one/frappe";
13
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
14
+ import { cleanup, screen, waitFor } from "@testing-library/react";
15
+ import i18next from "i18next";
16
+ import { initReactI18next } from "react-i18next";
17
+ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
18
+ import { FrappeKPICard } from "./FrappeKPICard";
19
+ import { FrappeProvider } from "./FrappeProvider";
20
+ import { __resetFrappeDevWarnSeen } from "./shared/devWarn";
21
+
22
+ beforeAll(async () => {
23
+ await i18next.use(initReactI18next).init({
24
+ lng: "en",
25
+ interpolation: { escapeValue: false },
26
+ resources: { en: { translation: {} } },
27
+ });
28
+ });
29
+
30
+ beforeEach(() => {
31
+ __resetFrappeDevWarnSeen();
32
+ });
33
+
34
+ afterEach(() => {
35
+ cleanup();
36
+ vi.restoreAllMocks();
37
+ });
38
+
39
+ const config = { id: "open-todos", title: "Open ToDos", value: 999, format: "number" as const };
40
+
41
+ describe("FrappeKPICard with a dataSource and no baseURL (MPO-237)", () => {
42
+ it("does not render config.value as though it were the computed figure", () => {
43
+ vi.spyOn(console, "warn").mockImplementation(() => {});
44
+ renderWithProviders(<FrappeKPICard config={config} dataSource={{ doctype: "ToDo" }} />);
45
+
46
+ expect(screen.queryByText("999")).toBeNull();
47
+ expect(screen.getByText("Open ToDos is not configured")).toBeInTheDocument();
48
+ expect(screen.getByText(/no Frappe baseURL is in scope/)).toBeInTheDocument();
49
+ });
50
+
51
+ it("logs a development warning naming the missing baseURL", () => {
52
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
53
+ renderWithProviders(<FrappeKPICard config={config} dataSource={{ doctype: "ToDo" }} />);
54
+
55
+ const line = warn.mock.calls.map((call) => String(call[0])).join("\n");
56
+ expect(line).toContain("[frappe-ui] kpi-datasource-without-baseurl");
57
+ expect(line).toContain("baseURL");
58
+ expect(line).toContain("component=FrappeKPICard");
59
+ expect(line).toContain("id=open-todos");
60
+ expect(line).toContain("doctype=ToDo");
61
+ });
62
+
63
+ it("warns once per card, not once per render", () => {
64
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
65
+ const { rerender } = renderWithProviders(
66
+ <FrappeKPICard config={config} dataSource={{ doctype: "ToDo" }} />,
67
+ );
68
+ rerender(<FrappeKPICard config={{ ...config }} dataSource={{ doctype: "ToDo" }} />);
69
+ rerender(<FrappeKPICard config={{ ...config }} dataSource={{ doctype: "ToDo" }} />);
70
+
71
+ const lines = warn.mock.calls.filter((call) =>
72
+ String(call[0]).includes("kpi-datasource-without-baseurl"),
73
+ );
74
+ expect(lines).toHaveLength(1);
75
+ });
76
+
77
+ it("stays silent in production", () => {
78
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
79
+ const previous = process.env.NODE_ENV;
80
+ process.env.NODE_ENV = "production";
81
+ try {
82
+ renderWithProviders(<FrappeKPICard config={config} dataSource={{ doctype: "ToDo" }} />);
83
+ } finally {
84
+ process.env.NODE_ENV = previous;
85
+ }
86
+
87
+ expect(
88
+ warn.mock.calls.filter((call) => String(call[0]).includes("kpi-datasource-without-baseurl")),
89
+ ).toHaveLength(0);
90
+ });
91
+ });
92
+
93
+ describe("FrappeKPICard configured paths stay untouched (MPO-237)", () => {
94
+ it("renders config.value with no warning when no dataSource is set", () => {
95
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
96
+ renderWithProviders(<FrappeKPICard config={config} />);
97
+
98
+ expect(screen.getByText("999")).toBeInTheDocument();
99
+ expect(
100
+ warn.mock.calls.filter((call) => String(call[0]).includes("kpi-datasource-without-baseurl")),
101
+ ).toHaveLength(0);
102
+ });
103
+
104
+ it("computes the figure, and does not warn, when the provider supplies a baseURL", async () => {
105
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
106
+ renderWithProviders(
107
+ <FrappeProvider
108
+ baseURL="http://test"
109
+ fixtures={
110
+ new InMemoryFixtureProvider({
111
+ ToDo: [
112
+ { name: "TD-1", doctype: "ToDo", status: "Open" },
113
+ { name: "TD-2", doctype: "ToDo", status: "Open" },
114
+ ],
115
+ })
116
+ }
117
+ >
118
+ <FrappeKPICard config={config} dataSource={{ doctype: "ToDo" }} />
119
+ </FrappeProvider>,
120
+ );
121
+
122
+ await waitFor(() => expect(screen.getByText("2")).toBeInTheDocument());
123
+ expect(screen.queryByText("999")).toBeNull();
124
+ expect(
125
+ warn.mock.calls.filter((call) => String(call[0]).includes("kpi-datasource-without-baseurl")),
126
+ ).toHaveLength(0);
127
+ });
128
+ });
@@ -11,6 +11,12 @@
11
11
  * (permission failures called out explicitly), a subtle stale affordance
12
12
  * (the card dims while a post-event refetch is in flight), and "—" when the
13
13
  * aggregate has no result (e.g. avg over an empty set).
14
+ *
15
+ * A `dataSource` with no `baseURL` in scope (neither the prop nor a
16
+ * FrappeProvider) is a misconfiguration, not static mode: the caller asked
17
+ * for a computed figure and there is nowhere to compute it. The card renders
18
+ * the not-configured surface and warns in dev. It never falls back to
19
+ * `config.value`, which would present an uncomputed number as the KPI.
14
20
  */
15
21
  import {
16
22
  AsyncBoundary,
@@ -19,10 +25,11 @@ import {
19
25
  Skeleton,
20
26
  type KPICardConfig,
21
27
  } from "@multiplatform.one/components";
22
- import React, { useMemo } from "react";
28
+ import React, { useEffect, useMemo } from "react";
23
29
  import { useTranslation } from "react-i18next";
24
30
  import { type YStackProps } from "tamagui";
25
31
  import { useFrappeConfig } from "./FrappeProvider";
32
+ import { frappeDevWarn } from "./shared/devWarn";
26
33
  import {
27
34
  useFrappeAggregate,
28
35
  type FrappeAggregateConnection,
@@ -44,11 +51,13 @@ export interface FrappeKPIDataSource {
44
51
  export interface FrappeKPICardProps extends Omit<YStackProps, "children"> {
45
52
  /**
46
53
  * KPI chrome (title, format, trend, icon…). In live mode `value` is
47
- * overridden by the aggregate; in static mode it renders as-is.
54
+ * overridden by the aggregate; in static mode it renders as-is. With a
55
+ * `dataSource` and no `baseURL`, `value` is not rendered at all.
48
56
  */
49
57
  config: KPICardConfig;
50
58
  /** When set, the card's value goes live against this doctype. */
51
59
  dataSource?: FrappeKPIDataSource;
60
+ /** Falls back to the FrappeProvider's. Required whenever `dataSource` is set. */
52
61
  baseURL?: string;
53
62
  auth?: { useToken?: boolean; token?: string; type?: string };
54
63
  syncOptions?: unknown;
@@ -92,6 +101,18 @@ export function FrappeKPICard({
92
101
  }
93
102
  : undefined;
94
103
 
104
+ const missingBaseURL = !!dataSource && !finalBaseURL;
105
+ const doctype = dataSource?.doctype;
106
+ useEffect(() => {
107
+ if (!missingBaseURL) return;
108
+ frappeDevWarn("kpi-datasource-without-baseurl", {
109
+ component: "FrappeKPICard",
110
+ id: config.id,
111
+ doctype,
112
+ suggest: "set baseURL on FrappeKPICard or on the FrappeProvider above it",
113
+ });
114
+ }, [missingBaseURL, config.id, doctype]);
115
+
95
116
  const { value, isLoading, isStale, error, isPermissionError } = useFrappeAggregate(connection, {
96
117
  doctype: dataSource?.doctype ?? "",
97
118
  aggregate: dataSource?.aggregate,
@@ -106,11 +127,30 @@ export function FrappeKPICard({
106
127
  return { ...config, value: value ?? "—" };
107
128
  }, [config, dataSource, value]);
108
129
 
109
- // Static passthrough — same component object semantics as before.
110
- if (!dataSource || !connection) {
130
+ // Static passthrough. The caller supplied the figure and asked for no
131
+ // computation.
132
+ if (!dataSource) {
111
133
  return <KPICard config={config} {...stackProps} />;
112
134
  }
113
135
 
136
+ // A requested figure with nowhere to compute it. `config.value` is the
137
+ // caller's placeholder, so painting it here would publish a number nothing
138
+ // downstream could tell was never computed.
139
+ if (!connection) {
140
+ return (
141
+ <AsyncBoundary
142
+ compact
143
+ error
144
+ errorTitle={t("{{title}} is not configured", { title: config.title })}
145
+ errorDescription={t(
146
+ "This card reads {{doctype}} live, but no Frappe baseURL is in scope, so the figure was never computed.",
147
+ { doctype: dataSource.doctype },
148
+ )}
149
+ {...stackProps}
150
+ />
151
+ );
152
+ }
153
+
114
154
  if (isLoading) {
115
155
  return <KPICardSkeleton {...stackProps} />;
116
156
  }
@@ -1,4 +1,3 @@
1
- import { useFrappeCollection, useLiveQuery } from "@multiplatform.one/frappe";
2
1
  import {
3
2
  Kanban,
4
3
  componentColors,
@@ -15,6 +14,7 @@ import { useResolvedKnobs } from "@multiplatform.one/theme";
15
14
  import { FrappeConnectionIndicator } from "./FrappeConnectionIndicator";
16
15
  import { useFrappeConfig } from "./FrappeProvider";
17
16
  import { describeSyncError } from "./syncError";
17
+ import { useFrappeRows } from "./useFrappeRows";
18
18
 
19
19
  /**
20
20
  * Configuration for a single Kanban column.
@@ -91,16 +91,15 @@ export function FrappeKanban<TDoc extends { name: string; doctype: string }>({
91
91
  const { knobProps } = useResolvedKnobs();
92
92
  const { t } = useTranslation();
93
93
  const [actionError, setActionError] = useState<Error | null>(null);
94
- // Honest error state: the collection swallows failed subscription fetches
95
- // (board would just look empty) — onSyncError reports them here.
96
- const [syncError, setSyncError] = useState<unknown>(null);
97
94
 
98
95
  const finalBaseURL = baseURL ?? config?.baseURL;
99
96
  const finalAuth = auth ?? config?.auth;
100
97
  const finalSyncOptions = syncOptions ?? config?.syncOptions;
101
98
 
102
- if (!finalBaseURL) {
103
- throw new Error("FrappeKanban requires baseURL prop or FrappeProvider with baseURL");
99
+ if (!finalBaseURL && !config?.fixtures) {
100
+ throw new Error(
101
+ "FrappeKanban requires baseURL prop or FrappeProvider with baseURL or fixtures",
102
+ );
104
103
  }
105
104
 
106
105
  // Ensure required fields are fetched
@@ -139,17 +138,25 @@ export function FrappeKanban<TDoc extends { name: string; doctype: string }>({
139
138
  ],
140
139
  );
141
140
 
142
- const collection = useFrappeCollection<TDoc>(connectionConfig, doctype, {
141
+ // The shared doctype read (MPO-251). `mapError` keeps this board's own
142
+ // error copy (describeSyncError) rather than the hook's default shape, and
143
+ // `limit: null` keeps the bench's own page length.
144
+ const mapSyncError = useCallback(
145
+ (err: unknown) => new Error(describeSyncError(err, doctype, t)),
146
+ [doctype, t],
147
+ );
148
+ const {
149
+ rows: cards,
150
+ isQueryLoading: isLoading,
151
+ isError,
152
+ syncError,
153
+ collection,
154
+ } = useFrappeRows<TDoc>(connectionConfig, doctype, {
143
155
  fields: effectiveFields,
144
156
  filters,
145
- onSyncError: setSyncError,
157
+ limit: null,
158
+ mapError: mapSyncError,
146
159
  });
147
- const {
148
- data: rawData,
149
- isLoading,
150
- isError,
151
- } = useLiveQuery((q) => (collection ? q.from({ _list: collection }) : null), [collection]);
152
- const cards = useMemo(() => (rawData as TDoc[] | undefined) ?? [], [rawData]);
153
160
 
154
161
  // Group cards by status into generic KanbanColumn format.
155
162
  // Cards with an unknown status land in an "Unassigned" column so they
@@ -286,9 +293,9 @@ export function FrappeKanban<TDoc extends { name: string; doctype: string }>({
286
293
  // Real subscription failure (permission-error, network, bad doctype)
287
294
  // reported by the collection via onSyncError — honest error UI instead of
288
295
  // an empty board. The `error` render prop stays the consumer override.
289
- const boardError = syncError != null ? describeSyncError(syncError, doctype, t) : null;
290
- if (boardError && error) {
291
- return <>{error(new Error(boardError))}</>;
296
+ const boardError = syncError ? syncError.message : null;
297
+ if (syncError && error) {
298
+ return <>{error(syncError)}</>;
292
299
  }
293
300
 
294
301
  // Opt-in query error panel via `error` prop. Empty/fixture-less collections
@@ -168,7 +168,7 @@ function FrappeLikedByBody({
168
168
 
169
169
  const http = useMemo(
170
170
  () =>
171
- config && !config.fixtures
171
+ config?.baseURL && !config.fixtures
172
172
  ? new HttpClient(
173
173
  config.baseURL,
174
174
  config.auth as Parameters<typeof HttpClient.prototype.updateAuth>[0],
@@ -51,8 +51,9 @@ export function FrappeList<TDoc extends { name: string; doctype: string }>({
51
51
  const finalAuth = auth ?? config?.auth;
52
52
  const finalSyncOptions = syncOptions ?? config?.syncOptions;
53
53
 
54
- if (!finalBaseURL) {
55
- throw new Error("FrappeList requires baseURL prop or FrappeProvider with baseURL");
54
+ // Fixtures answer every read locally, so they stand in for a baseURL.
55
+ if (!finalBaseURL && !config?.fixtures) {
56
+ throw new Error("FrappeList requires baseURL prop or FrappeProvider with baseURL or fixtures");
56
57
  }
57
58
 
58
59
  const connectionConfig = useMemo(