@multiplatform.one/frappe-ui 7.28.1 → 7.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/dist/cjs/FrappeCalendar.cjs +3 -2
  2. package/dist/cjs/FrappeCalendar.native.js +3 -2
  3. package/dist/cjs/FrappeCalendar.native.js.map +1 -1
  4. package/dist/cjs/FrappeForm.cjs +148 -464
  5. package/dist/cjs/FrappeForm.native.js +152 -633
  6. package/dist/cjs/FrappeForm.native.js.map +1 -1
  7. package/dist/cjs/FrappeListView.cjs +5 -27
  8. package/dist/cjs/FrappeListView.native.js +11 -32
  9. package/dist/cjs/FrappeListView.native.js.map +1 -1
  10. package/dist/cjs/FrappeReportBuilder.cjs +523 -309
  11. package/dist/cjs/FrappeReportBuilder.native.js +658 -353
  12. package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
  13. package/dist/cjs/FrappeReportView.cjs +35 -234
  14. package/dist/cjs/FrappeReportView.native.js +39 -283
  15. package/dist/cjs/FrappeReportView.native.js.map +1 -1
  16. package/dist/cjs/dependsOn.cjs +2 -2
  17. package/dist/cjs/dependsOn.native.js +1 -2
  18. package/dist/cjs/dependsOn.native.js.map +1 -1
  19. package/dist/cjs/fields/shared/EditorPreviewPanes.cjs +2 -2
  20. package/dist/cjs/fields/shared/EditorPreviewPanes.native.js +2 -2
  21. package/dist/cjs/fields/shared/EditorPreviewPanes.native.js.map +1 -1
  22. package/dist/cjs/formEngine/docPermissions.cjs +20 -0
  23. package/dist/cjs/formEngine/docPermissions.native.js +69 -0
  24. package/dist/cjs/formEngine/docPermissions.native.js.map +1 -1
  25. package/dist/cjs/formEngine/index.cjs +2 -0
  26. package/dist/cjs/formEngine/index.native.js +2 -0
  27. package/dist/cjs/formEngine/index.native.js.map +1 -1
  28. package/dist/cjs/index.cjs +67 -7
  29. package/dist/cjs/index.native.js +67 -7
  30. package/dist/cjs/index.native.js.map +1 -1
  31. package/dist/cjs/liveErrors.cjs +8 -2
  32. package/dist/cjs/liveErrors.native.js +8 -2
  33. package/dist/cjs/liveErrors.native.js.map +1 -1
  34. package/dist/cjs/measures.cjs +31 -0
  35. package/dist/cjs/measures.native.js +34 -0
  36. package/dist/cjs/measures.native.js.map +1 -0
  37. package/dist/cjs/recordShape.cjs +103 -0
  38. package/dist/cjs/recordShape.native.js +115 -0
  39. package/dist/cjs/recordShape.native.js.map +1 -0
  40. package/dist/cjs/report/ReportGrid.cjs +453 -0
  41. package/dist/cjs/report/ReportGrid.native.js +510 -0
  42. package/dist/cjs/report/ReportGrid.native.js.map +1 -0
  43. package/dist/cjs/report/ReportViewToolbar.cjs +253 -0
  44. package/dist/cjs/report/ReportViewToolbar.native.js +303 -0
  45. package/dist/cjs/report/ReportViewToolbar.native.js.map +1 -0
  46. package/dist/cjs/report/queryReport.cjs +444 -0
  47. package/dist/cjs/report/queryReport.native.js +588 -0
  48. package/dist/cjs/report/queryReport.native.js.map +1 -0
  49. package/dist/cjs/report/queryReportColumns.cjs +131 -0
  50. package/dist/cjs/report/queryReportColumns.native.js +159 -0
  51. package/dist/cjs/report/queryReportColumns.native.js.map +1 -0
  52. package/dist/cjs/report/reportViewModel.cjs +386 -0
  53. package/dist/cjs/report/reportViewModel.native.js +657 -0
  54. package/dist/cjs/report/reportViewModel.native.js.map +1 -0
  55. package/dist/cjs/report/savedReports.cjs +137 -0
  56. package/dist/cjs/report/savedReports.native.js +169 -0
  57. package/dist/cjs/report/savedReports.native.js.map +1 -0
  58. package/dist/cjs/report/useFrappeReport.cjs +184 -0
  59. package/dist/cjs/report/useFrappeReport.native.js +214 -0
  60. package/dist/cjs/report/useFrappeReport.native.js.map +1 -0
  61. package/dist/cjs/report/useQueryReport.cjs +201 -0
  62. package/dist/cjs/report/useQueryReport.native.js +233 -0
  63. package/dist/cjs/report/useQueryReport.native.js.map +1 -0
  64. package/dist/cjs/useFrappeForm.cjs +558 -0
  65. package/dist/cjs/useFrappeForm.native.js +714 -0
  66. package/dist/cjs/useFrappeForm.native.js.map +1 -0
  67. package/dist/esm/FrappeCalendar.mjs +4 -3
  68. package/dist/esm/FrappeCalendar.mjs.map +1 -1
  69. package/dist/esm/FrappeCalendar.native.js +4 -3
  70. package/dist/esm/FrappeCalendar.native.js.map +1 -1
  71. package/dist/esm/FrappeForm.mjs +151 -467
  72. package/dist/esm/FrappeForm.mjs.map +1 -1
  73. package/dist/esm/FrappeForm.native.js +155 -636
  74. package/dist/esm/FrappeForm.native.js.map +1 -1
  75. package/dist/esm/FrappeListView.mjs +6 -28
  76. package/dist/esm/FrappeListView.mjs.map +1 -1
  77. package/dist/esm/FrappeListView.native.js +12 -33
  78. package/dist/esm/FrappeListView.native.js.map +1 -1
  79. package/dist/esm/FrappeReportBuilder.mjs +526 -312
  80. package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
  81. package/dist/esm/FrappeReportBuilder.native.js +661 -356
  82. package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
  83. package/dist/esm/FrappeReportView.mjs +30 -229
  84. package/dist/esm/FrappeReportView.mjs.map +1 -1
  85. package/dist/esm/FrappeReportView.native.js +34 -278
  86. package/dist/esm/FrappeReportView.native.js.map +1 -1
  87. package/dist/esm/dependsOn.mjs +2 -2
  88. package/dist/esm/dependsOn.mjs.map +1 -1
  89. package/dist/esm/dependsOn.native.js +1 -2
  90. package/dist/esm/dependsOn.native.js.map +1 -1
  91. package/dist/esm/fields/shared/EditorPreviewPanes.mjs +2 -2
  92. package/dist/esm/fields/shared/EditorPreviewPanes.mjs.map +1 -1
  93. package/dist/esm/fields/shared/EditorPreviewPanes.native.js +2 -2
  94. package/dist/esm/fields/shared/EditorPreviewPanes.native.js.map +1 -1
  95. package/dist/esm/formEngine/docPermissions.mjs +19 -1
  96. package/dist/esm/formEngine/docPermissions.mjs.map +1 -1
  97. package/dist/esm/formEngine/docPermissions.native.js +68 -1
  98. package/dist/esm/formEngine/docPermissions.native.js.map +1 -1
  99. package/dist/esm/formEngine/index.mjs +2 -2
  100. package/dist/esm/formEngine/index.mjs.map +1 -1
  101. package/dist/esm/formEngine/index.native.js +2 -2
  102. package/dist/esm/formEngine/index.native.js.map +1 -1
  103. package/dist/esm/index.mjs +16 -4
  104. package/dist/esm/index.mjs.map +1 -1
  105. package/dist/esm/index.native.js +16 -4
  106. package/dist/esm/index.native.js.map +1 -1
  107. package/dist/esm/liveErrors.mjs +8 -3
  108. package/dist/esm/liveErrors.mjs.map +1 -1
  109. package/dist/esm/liveErrors.native.js +8 -3
  110. package/dist/esm/liveErrors.native.js.map +1 -1
  111. package/dist/esm/measures.mjs +5 -0
  112. package/dist/esm/measures.mjs.map +1 -0
  113. package/dist/esm/measures.native.js +5 -0
  114. package/dist/esm/measures.native.js.map +1 -0
  115. package/dist/esm/recordShape.mjs +74 -0
  116. package/dist/esm/recordShape.mjs.map +1 -0
  117. package/dist/esm/recordShape.native.js +83 -0
  118. package/dist/esm/recordShape.native.js.map +1 -0
  119. package/dist/esm/report/ReportGrid.mjs +428 -0
  120. package/dist/esm/report/ReportGrid.mjs.map +1 -0
  121. package/dist/esm/report/ReportGrid.native.js +471 -0
  122. package/dist/esm/report/ReportGrid.native.js.map +1 -0
  123. package/dist/esm/report/ReportViewToolbar.mjs +228 -0
  124. package/dist/esm/report/ReportViewToolbar.mjs.map +1 -0
  125. package/dist/esm/report/ReportViewToolbar.native.js +264 -0
  126. package/dist/esm/report/ReportViewToolbar.native.js.map +1 -0
  127. package/dist/esm/report/queryReport.mjs +404 -0
  128. package/dist/esm/report/queryReport.mjs.map +1 -0
  129. package/dist/esm/report/queryReport.native.js +545 -0
  130. package/dist/esm/report/queryReport.native.js.map +1 -0
  131. package/dist/esm/report/queryReportColumns.mjs +105 -0
  132. package/dist/esm/report/queryReportColumns.mjs.map +1 -0
  133. package/dist/esm/report/queryReportColumns.native.js +119 -0
  134. package/dist/esm/report/queryReportColumns.native.js.map +1 -0
  135. package/dist/esm/report/reportViewModel.mjs +343 -0
  136. package/dist/esm/report/reportViewModel.mjs.map +1 -0
  137. package/dist/esm/report/reportViewModel.native.js +611 -0
  138. package/dist/esm/report/reportViewModel.native.js.map +1 -0
  139. package/dist/esm/report/savedReports.mjs +112 -0
  140. package/dist/esm/report/savedReports.mjs.map +1 -0
  141. package/dist/esm/report/savedReports.native.js +141 -0
  142. package/dist/esm/report/savedReports.native.js.map +1 -0
  143. package/dist/esm/report/useFrappeReport.mjs +158 -0
  144. package/dist/esm/report/useFrappeReport.mjs.map +1 -0
  145. package/dist/esm/report/useFrappeReport.native.js +185 -0
  146. package/dist/esm/report/useFrappeReport.native.js.map +1 -0
  147. package/dist/esm/report/useQueryReport.mjs +176 -0
  148. package/dist/esm/report/useQueryReport.mjs.map +1 -0
  149. package/dist/esm/report/useQueryReport.native.js +205 -0
  150. package/dist/esm/report/useQueryReport.native.js.map +1 -0
  151. package/dist/esm/useFrappeForm.mjs +533 -0
  152. package/dist/esm/useFrappeForm.mjs.map +1 -0
  153. package/dist/esm/useFrappeForm.native.js +686 -0
  154. package/dist/esm/useFrappeForm.native.js.map +1 -0
  155. package/dist/jsx/FrappeCalendar.mjs +4 -3
  156. package/dist/jsx/FrappeCalendar.mjs.map +1 -1
  157. package/dist/jsx/FrappeCalendar.native.js +3 -2
  158. package/dist/jsx/FrappeCalendar.native.js.map +1 -1
  159. package/dist/jsx/FrappeForm.mjs +151 -467
  160. package/dist/jsx/FrappeForm.mjs.map +1 -1
  161. package/dist/jsx/FrappeForm.native.js +152 -633
  162. package/dist/jsx/FrappeForm.native.js.map +1 -1
  163. package/dist/jsx/FrappeListView.mjs +6 -28
  164. package/dist/jsx/FrappeListView.mjs.map +1 -1
  165. package/dist/jsx/FrappeListView.native.js +11 -32
  166. package/dist/jsx/FrappeListView.native.js.map +1 -1
  167. package/dist/jsx/FrappeReportBuilder.mjs +526 -312
  168. package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
  169. package/dist/jsx/FrappeReportBuilder.native.js +658 -353
  170. package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
  171. package/dist/jsx/FrappeReportView.mjs +30 -229
  172. package/dist/jsx/FrappeReportView.mjs.map +1 -1
  173. package/dist/jsx/FrappeReportView.native.js +39 -283
  174. package/dist/jsx/FrappeReportView.native.js.map +1 -1
  175. package/dist/jsx/dependsOn.mjs +2 -2
  176. package/dist/jsx/dependsOn.mjs.map +1 -1
  177. package/dist/jsx/dependsOn.native.js +1 -2
  178. package/dist/jsx/dependsOn.native.js.map +1 -1
  179. package/dist/jsx/fields/shared/EditorPreviewPanes.mjs +2 -2
  180. package/dist/jsx/fields/shared/EditorPreviewPanes.mjs.map +1 -1
  181. package/dist/jsx/fields/shared/EditorPreviewPanes.native.js +2 -2
  182. package/dist/jsx/fields/shared/EditorPreviewPanes.native.js.map +1 -1
  183. package/dist/jsx/formEngine/docPermissions.mjs +19 -1
  184. package/dist/jsx/formEngine/docPermissions.mjs.map +1 -1
  185. package/dist/jsx/formEngine/docPermissions.native.js +69 -0
  186. package/dist/jsx/formEngine/docPermissions.native.js.map +1 -1
  187. package/dist/jsx/formEngine/index.mjs +2 -2
  188. package/dist/jsx/formEngine/index.mjs.map +1 -1
  189. package/dist/jsx/formEngine/index.native.js +2 -0
  190. package/dist/jsx/formEngine/index.native.js.map +1 -1
  191. package/dist/jsx/index.js +16 -4
  192. package/dist/jsx/index.js.map +1 -1
  193. package/dist/jsx/index.mjs +16 -4
  194. package/dist/jsx/index.mjs.map +1 -1
  195. package/dist/jsx/index.native.js +67 -7
  196. package/dist/jsx/index.native.js.map +1 -1
  197. package/dist/jsx/liveErrors.mjs +8 -3
  198. package/dist/jsx/liveErrors.mjs.map +1 -1
  199. package/dist/jsx/liveErrors.native.js +8 -2
  200. package/dist/jsx/liveErrors.native.js.map +1 -1
  201. package/dist/jsx/measures.mjs +5 -0
  202. package/dist/jsx/measures.mjs.map +1 -0
  203. package/dist/jsx/measures.native.js +34 -0
  204. package/dist/jsx/measures.native.js.map +1 -0
  205. package/dist/jsx/recordShape.mjs +74 -0
  206. package/dist/jsx/recordShape.mjs.map +1 -0
  207. package/dist/jsx/recordShape.native.js +115 -0
  208. package/dist/jsx/recordShape.native.js.map +1 -0
  209. package/dist/jsx/report/ReportGrid.mjs +428 -0
  210. package/dist/jsx/report/ReportGrid.mjs.map +1 -0
  211. package/dist/jsx/report/ReportGrid.native.js +510 -0
  212. package/dist/jsx/report/ReportGrid.native.js.map +1 -0
  213. package/dist/jsx/report/ReportViewToolbar.mjs +228 -0
  214. package/dist/jsx/report/ReportViewToolbar.mjs.map +1 -0
  215. package/dist/jsx/report/ReportViewToolbar.native.js +303 -0
  216. package/dist/jsx/report/ReportViewToolbar.native.js.map +1 -0
  217. package/dist/jsx/report/queryReport.mjs +404 -0
  218. package/dist/jsx/report/queryReport.mjs.map +1 -0
  219. package/dist/jsx/report/queryReport.native.js +588 -0
  220. package/dist/jsx/report/queryReport.native.js.map +1 -0
  221. package/dist/jsx/report/queryReportColumns.mjs +105 -0
  222. package/dist/jsx/report/queryReportColumns.mjs.map +1 -0
  223. package/dist/jsx/report/queryReportColumns.native.js +159 -0
  224. package/dist/jsx/report/queryReportColumns.native.js.map +1 -0
  225. package/dist/jsx/report/reportViewModel.mjs +343 -0
  226. package/dist/jsx/report/reportViewModel.mjs.map +1 -0
  227. package/dist/jsx/report/reportViewModel.native.js +657 -0
  228. package/dist/jsx/report/reportViewModel.native.js.map +1 -0
  229. package/dist/jsx/report/savedReports.mjs +112 -0
  230. package/dist/jsx/report/savedReports.mjs.map +1 -0
  231. package/dist/jsx/report/savedReports.native.js +169 -0
  232. package/dist/jsx/report/savedReports.native.js.map +1 -0
  233. package/dist/jsx/report/useFrappeReport.mjs +158 -0
  234. package/dist/jsx/report/useFrappeReport.mjs.map +1 -0
  235. package/dist/jsx/report/useFrappeReport.native.js +214 -0
  236. package/dist/jsx/report/useFrappeReport.native.js.map +1 -0
  237. package/dist/jsx/report/useQueryReport.mjs +176 -0
  238. package/dist/jsx/report/useQueryReport.mjs.map +1 -0
  239. package/dist/jsx/report/useQueryReport.native.js +233 -0
  240. package/dist/jsx/report/useQueryReport.native.js.map +1 -0
  241. package/dist/jsx/useFrappeForm.mjs +533 -0
  242. package/dist/jsx/useFrappeForm.mjs.map +1 -0
  243. package/dist/jsx/useFrappeForm.native.js +714 -0
  244. package/dist/jsx/useFrappeForm.native.js.map +1 -0
  245. package/package.json +16 -16
  246. package/src/FrappeCalendar.tsx +8 -2
  247. package/src/FrappeForm.stories.tsx +52 -0
  248. package/src/FrappeForm.tsx +176 -565
  249. package/src/FrappeListView.cards.stories.tsx +3 -6
  250. package/src/FrappeListView.tsx +15 -55
  251. package/src/FrappeReportBuilder.stories.tsx +34 -7
  252. package/src/FrappeReportBuilder.tsx +790 -380
  253. package/src/FrappeReportView.spec.tsx +10 -6
  254. package/src/FrappeReportView.stories.tsx +20 -1
  255. package/src/FrappeReportView.tsx +89 -415
  256. package/src/dependsOn.ts +4 -1
  257. package/src/fields/shared/EditorPreviewPanes.tsx +2 -2
  258. package/src/formEngine/docPermissions.spec.ts +36 -1
  259. package/src/formEngine/docPermissions.ts +34 -0
  260. package/src/formEngine/index.ts +8 -1
  261. package/src/frappeRemainder.designLaw.spec.ts +9 -1
  262. package/src/frappeRoutes.spec.tsx +9 -0
  263. package/src/frappeViews.designLaw.spec.ts +15 -2
  264. package/src/guestFallback.spec.tsx +236 -0
  265. package/src/index.ts +117 -16
  266. package/src/liveErrors.spec.ts +10 -0
  267. package/src/liveErrors.ts +16 -3
  268. package/src/measures.ts +11 -0
  269. package/src/recordShape.spec.ts +100 -0
  270. package/src/recordShape.ts +133 -0
  271. package/src/report/ReportGrid.spec.tsx +175 -0
  272. package/src/report/ReportGrid.tsx +530 -0
  273. package/src/report/ReportViewToolbar.spec.tsx +90 -0
  274. package/src/report/ReportViewToolbar.tsx +314 -0
  275. package/src/report/queryReport.spec.ts +483 -0
  276. package/src/report/queryReport.ts +722 -0
  277. package/src/report/queryReportColumns.tsx +123 -0
  278. package/src/report/reportViewModel.spec.ts +393 -0
  279. package/src/report/reportViewModel.ts +617 -0
  280. package/src/report/savedReports.ts +172 -0
  281. package/src/report/seam.spec.ts +216 -0
  282. package/src/report/useFrappeReport.ts +241 -0
  283. package/src/report/useQueryReport.spec.tsx +131 -0
  284. package/src/report/useQueryReport.ts +309 -0
  285. package/src/useFrappeForm.spec.tsx +209 -0
  286. package/src/useFrappeForm.ts +778 -0
  287. package/types/FrappeCalendar.d.ts.map +1 -1
  288. package/types/FrappeForm.d.ts +21 -1
  289. package/types/FrappeForm.d.ts.map +1 -1
  290. package/types/FrappeListView.d.ts.map +1 -1
  291. package/types/FrappeReportBuilder.d.ts +66 -16
  292. package/types/FrappeReportBuilder.d.ts.map +1 -1
  293. package/types/FrappeReportView.d.ts +9 -127
  294. package/types/FrappeReportView.d.ts.map +1 -1
  295. package/types/dependsOn.d.ts.map +1 -1
  296. package/types/formEngine/docPermissions.d.ts +14 -0
  297. package/types/formEngine/docPermissions.d.ts.map +1 -1
  298. package/types/formEngine/index.d.ts +1 -1
  299. package/types/formEngine/index.d.ts.map +1 -1
  300. package/types/index.d.ts +15 -3
  301. package/types/index.d.ts.map +1 -1
  302. package/types/liveErrors.d.ts +9 -1
  303. package/types/liveErrors.d.ts.map +1 -1
  304. package/types/measures.d.ts +3 -0
  305. package/types/measures.d.ts.map +1 -0
  306. package/types/recordShape.d.ts +45 -0
  307. package/types/recordShape.d.ts.map +1 -0
  308. package/types/report/ReportGrid.d.ts +20 -0
  309. package/types/report/ReportGrid.d.ts.map +1 -0
  310. package/types/report/ReportViewToolbar.d.ts +31 -0
  311. package/types/report/ReportViewToolbar.d.ts.map +1 -0
  312. package/types/report/queryReport.d.ts +181 -0
  313. package/types/report/queryReport.d.ts.map +1 -0
  314. package/types/report/queryReportColumns.d.ts +12 -0
  315. package/types/report/queryReportColumns.d.ts.map +1 -0
  316. package/types/report/reportViewModel.d.ts +146 -0
  317. package/types/report/reportViewModel.d.ts.map +1 -0
  318. package/types/report/savedReports.d.ts +27 -0
  319. package/types/report/savedReports.d.ts.map +1 -0
  320. package/types/report/useFrappeReport.d.ts +60 -0
  321. package/types/report/useFrappeReport.d.ts.map +1 -0
  322. package/types/report/useQueryReport.d.ts +37 -0
  323. package/types/report/useQueryReport.d.ts.map +1 -0
  324. package/types/useFrappeForm.d.ts +142 -0
  325. package/types/useFrappeForm.d.ts.map +1 -0
@@ -1,10 +1,9 @@
1
- import { getFrappeSyncModule, useFrappeCollection, useFrappeDoctypeEvents, useLiveQuery, useDocTypeMeta } from "@multiplatform.one/frappe";
1
+ import { useFrappeCollection, useLiveQuery } from "@multiplatform.one/frappe";
2
2
  import { AsyncBoundary, AsyncSkeleton, DetailPageLayout, EmptyState, PageHeader, Tabs } from "@multiplatform.one/components";
3
3
  import { SchemaForm } from "./schemaForm/index.mjs";
4
4
  import { FormSidebar } from "./FormSidebar.mjs";
5
- import { eq } from "@tanstack/db";
6
5
  import { ArrowLeftIcon } from "@phosphor-icons/react";
7
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
+ import { useEffect, useMemo, useState } from "react";
8
7
  import { useTranslation } from "react-i18next";
9
8
  import { Button, Input, formCommonColors } from "@multiplatform.one/forms";
10
9
  import { Text, XStack, YStack } from "tamagui";
@@ -13,56 +12,23 @@ import { useFrappeConfig } from "./FrappeProvider.mjs";
13
12
  import { FrappeReconnectIndicator } from "./FrappeReconnectIndicator.mjs";
14
13
  import { Timeline } from "./components/timeline.mjs";
15
14
  import { dialog } from "@multiplatform.one/desktop";
16
- import { applyFetchedValues, buildFetchMap } from "./formEngine/fetchFrom.mjs";
17
- import { buildPermLevelMatrix } from "./formEngine/fieldPermissions.mjs";
18
- import { createHttpClient, validateLink } from "./formEngine/formActions.mjs";
19
- import { buildFormTabs, isPromptNamed } from "./formEngine/formLayout.mjs";
20
- import { setCreatePrefill, takeCreatePrefill } from "./formEngine/createPrefill.mjs";
21
- import { buildAmendValues, canDeleteDoc, canEditDoc, isAmendable, resolveDocAction, resolveDocIndicator } from "./formEngine/docLifecycle.mjs";
22
- import { useAmendedAs } from "./formEngine/useAmendedAs.mjs";
23
- import { useDocRights } from "./formEngine/useDocRights.mjs";
24
- import { extractServerError } from "./formEngine/useFormEngine.mjs";
15
+ import { canDeleteDoc, canEditDoc, isAmendable, resolveDocAction, resolveDocIndicator } from "./formEngine/docLifecycle.mjs";
25
16
  import { FrappeFormActions } from "./FrappeFormActions.mjs";
26
17
  import { connectionFields, connectionsFromMeta } from "./frappeConnections.mjs";
27
18
  import { FrappeFormConnections } from "./FrappeFormConnections.mjs";
28
19
  import { FrappeNotConfigured } from "./FrappeNotConfigured.mjs";
29
20
  import { FrappeRowsReader } from "./FrappeRowsReader.mjs";
21
+ import { isPermissionError } from "./liveErrors.mjs";
22
+ import { fieldsFromRecord } from "./recordShape.mjs";
30
23
  import { frappeRouteHref, frappeRouteTarget, useFrappeRouter } from "./frappeNavigation.mjs";
24
+ import { useFrappeForm } from "./useFrappeForm.mjs";
31
25
  import { jsx, jsxs } from "react/jsx-runtime";
32
26
  import { createElement } from "react";
33
- function isEmptyFieldValue(value) {
34
- if (value === void 0 || value === null) return true;
35
- if (typeof value === "string" && value.trim() === "") return true;
36
- if (Array.isArray(value) && value.length === 0) return true;
37
- return false;
38
- }
39
- function sectionsToLayoutFields(sections) {
40
- const fields = [];
41
- sections.forEach((section, sectionIndex) => {
42
- fields.push({
43
- fieldname: `__section_${sectionIndex}`,
44
- fieldtype: section.tab ? "Tab Break" : "Section Break",
45
- label: section.label,
46
- collapsible: section.collapsible ? 1 : 0
47
- });
48
- section.columns.forEach((column, columnIndex) => {
49
- if (columnIndex > 0) {
50
- fields.push({
51
- fieldname: `__column_${sectionIndex}_${columnIndex}`,
52
- fieldtype: "Column Break"
53
- });
54
- }
55
- fields.push(...column.fields);
56
- });
57
- });
58
- return fields;
59
- }
60
- const documentBookkeeping = /* @__PURE__ */new Set(["name", "owner", "creation", "modified", "modified_by", "docstatus", "idx", "doctype", "_user_tags", "_comments", "_assign", "_liked_by", "_seen"]);
61
- function fieldsFromDocument(doc) {
62
- return Object.entries(doc).filter(([key, value]) => !key.startsWith("$") && !documentBookkeeping.has(key) && (value === null || typeof value === "string" || typeof value === "number")).map(([key, value]) => ({
63
- name: key,
64
- label: key.replace(/_/g, " ").replace(/(^|\s)\S/g, c => c.toUpperCase()),
65
- type: typeof value === "number" ? Number.isInteger(value) ? "Int" : "Float" : "Data",
27
+ function schemaFieldsFromRecord(doc) {
28
+ return fieldsFromRecord(doc).map(field => ({
29
+ name: field.fieldname,
30
+ label: field.label,
31
+ type: field.fieldtype,
66
32
  readOnly: true
67
33
  }));
68
34
  }
@@ -206,20 +172,48 @@ function FrappeFormRoute({
206
172
  }))
207
173
  });
208
174
  }
209
- function FrappeFormBody({
210
- baseURL,
211
- auth,
212
- syncOptions,
175
+ function FrappeFormBody(props) {
176
+ if (props.form) return /* @__PURE__ */jsx(FrappeFormView, {
177
+ ...props,
178
+ form: props.form
179
+ });
180
+ return /* @__PURE__ */jsx(FrappeFormOwned, {
181
+ ...props
182
+ });
183
+ }
184
+ function FrappeFormOwned(props) {
185
+ const config = useFrappeConfig();
186
+ if (!(props.baseURL ?? config?.baseURL)) {
187
+ throw new Error("FrappeForm requires baseURL prop or FrappeProvider with baseURL");
188
+ }
189
+ const form = useFrappeForm({
190
+ doctype: props.doctype,
191
+ name: props.name,
192
+ baseURL: props.baseURL,
193
+ auth: props.auth,
194
+ syncOptions: props.syncOptions,
195
+ fields: props.fields,
196
+ sections: props.sections,
197
+ hideFields: props.hideFields,
198
+ hideSections: props.hideSections,
199
+ roles: props.roles,
200
+ readShares: props.showSidebar ?? true,
201
+ disabled: props.disabled,
202
+ initialDoc: props.initialDoc
203
+ });
204
+ return /* @__PURE__ */jsx(FrappeFormView, {
205
+ ...props,
206
+ form
207
+ });
208
+ }
209
+ function FrappeFormView({
210
+ form,
213
211
  doctype,
214
212
  name,
215
- fields,
216
213
  children,
217
214
  overrides,
218
- hideFields,
219
215
  readOnlyFields,
220
- sections,
221
216
  order,
222
- roles,
223
217
  onSave,
224
218
  onCancel,
225
219
  onDelete,
@@ -228,73 +222,55 @@ function FrappeFormBody({
228
222
  showToolbar = true,
229
223
  showSidebar = true,
230
224
  showTimeline = true,
225
+ showBanners = true,
231
226
  disabled = false,
232
227
  sectionHeadingLevel,
233
228
  connections,
234
229
  showConnections = true,
235
- initialDoc,
230
+ extraTabs,
231
+ renderFieldWrapper,
232
+ renderTab,
236
233
  loadingFallback,
237
234
  notFoundFallback,
238
235
  errorFallback,
239
236
  routePage
240
237
  }) {
241
- const config = useFrappeConfig();
242
238
  const {
243
239
  knobProps
244
240
  } = useResolvedKnobs();
245
241
  const {
246
242
  t
247
243
  } = useTranslation();
248
- const seedDoc = name && initialDoc && initialDoc.name === name ? initialDoc : void 0;
249
- const seedRef = useRef(seedDoc);
250
- seedRef.current = seedDoc;
251
- const [localDoc, setLocalDoc] = useState(() => seedDoc ?? {});
252
- const [baselineDoc, setBaselineDoc] = useState(() => seedDoc);
253
- const [docError, setDocError] = useState(null);
254
- const [conflictFields, setConflictFields] = useState([]);
255
- const [wasDeleted, setWasDeleted] = useState(false);
256
- const [isSubmitting, setIsSubmitting] = useState(false);
257
- const [errors, setErrors] = useState({});
258
- const [activeTab, setActiveTab] = useState(void 0);
259
- const everLoadedRef = useRef(false);
260
- const localDocRef = useRef(localDoc);
261
- localDocRef.current = localDoc;
262
- const baselineRef = useRef(baselineDoc);
263
- baselineRef.current = baselineDoc;
264
- const createDraftRef = useRef(void 0);
265
- const finalBaseURL = baseURL ?? config?.baseURL;
266
- const finalAuth = auth ?? config?.auth;
267
- const finalSyncOptions = syncOptions ?? config?.syncOptions;
268
- if (!finalBaseURL) {
269
- throw new Error("FrappeForm requires baseURL prop or FrappeProvider with baseURL");
270
- }
271
- const frappeConfig = useMemo(() => ({
272
- baseURL: finalBaseURL,
273
- socketPort: config?.socketPort,
274
- siteName: config?.siteName,
275
- socketBaseURL: config?.socketBaseURL,
276
- auth: finalAuth,
277
- syncOptions: finalSyncOptions,
278
- realtime: config?.realtime,
279
- fixtures: config?.fixtures
280
- }), [finalBaseURL, config?.socketPort, config?.siteName, config?.socketBaseURL, config?.realtime, finalAuth, finalSyncOptions, config?.fixtures]);
281
244
  const {
282
245
  meta,
283
- isLoading: isMetaLoading,
284
- error: metaError
285
- } = useDocTypeMeta(frappeConfig, doctype);
286
- const isSubmittable = !!meta?.is_submittable;
246
+ metaLoading: isMetaLoading,
247
+ metaError,
248
+ doc: localDoc,
249
+ baseline: baselineDoc,
250
+ setValue,
251
+ errors,
252
+ isSubmitting,
253
+ docstatus,
254
+ isSubmittable,
255
+ rights,
256
+ rightsPending,
257
+ canWrite: canWriteRecord,
258
+ wasDeleted,
259
+ conflictFields,
260
+ docError,
261
+ tabs: formTabs,
262
+ promptNamed,
263
+ activeTab,
264
+ setActiveTab,
265
+ amendedAs
266
+ } = form;
267
+ const frappeConfig = form.connection;
287
268
  const isCreate = !name;
288
- const docstatus = Number(baselineDoc?.docstatus ?? 0);
289
- const layoutFields = useMemo(() => {
290
- if (!meta) return void 0;
291
- return sections ? sectionsToLayoutFields(sections) : meta.fields;
292
- }, [meta, sections]);
293
- const amendedAs = useAmendedAs(isSubmittable && docstatus === 2 ? frappeConfig : void 0, doctype, name);
269
+ const hasUnsavedChanges = form.isDirty;
294
270
  const readsWithoutMeta = !children && !meta && Boolean(metaError) && Boolean(name);
295
271
  const recordTabs = useMemo(() => {
296
272
  if (!readsWithoutMeta || !baselineDoc) return void 0;
297
- const recordFields = fieldsFromDocument(baselineDoc);
273
+ const recordFields = schemaFieldsFromRecord(baselineDoc);
298
274
  return [{
299
275
  key: "__details",
300
276
  label: t("Details"),
@@ -308,87 +284,7 @@ function FrappeFormBody({
308
284
  }
309
285
  }];
310
286
  }, [readsWithoutMeta, baselineDoc, t]);
311
- const promptNamed = isCreate && !!meta && isPromptNamed(meta.autoname, meta.naming_rule);
312
- const fetchMap = useMemo(() => meta ? buildFetchMap(meta.fields) : void 0, [meta]);
313
- const handleDocSyncError = useCallback(err => {
314
- setDocError(err instanceof Error ? err : new Error(String(err)));
315
- }, []);
316
- const docCollection = useFrappeCollection(name ? frappeConfig : void 0, doctype, {
317
- fields,
318
- filters: name ? {
319
- name
320
- } : void 0,
321
- onSyncError: handleDocSyncError
322
- });
323
- const {
324
- data: rawDocData,
325
- isLoading: isDocLoading
326
- } = useLiveQuery(q => {
327
- if (!docCollection || !name) return null;
328
- return q.from({
329
- _doc: docCollection
330
- }).where(({
331
- _doc
332
- }) => {
333
- if (!_doc) return eq(name, name);
334
- return eq(_doc.name, name);
335
- });
336
- }, [docCollection, name]);
337
- const liveDoc = useMemo(() => {
338
- const arr = Array.isArray(rawDocData) ? rawDocData : rawDocData ? [rawDocData] : [];
339
- const doc = arr[0] ?? null;
340
- return doc ? {
341
- data: doc,
342
- isLoading: isDocLoading
343
- } : name ? {
344
- data: null,
345
- isLoading: isDocLoading
346
- } : null;
347
- }, [rawDocData, isDocLoading, name]);
348
- const collection = useFrappeCollection(name ? void 0 : frappeConfig, doctype, {
349
- fields
350
- });
351
- const shareCollection = useFrappeCollection(showSidebar && name ? frappeConfig : void 0, "DocShare", {
352
- fields: ["name", "user", "everyone", "read", "write", "share"],
353
- filters: {
354
- share_doctype: doctype,
355
- share_name: name
356
- }
357
- });
358
- const {
359
- data: rawShareRows
360
- } = useLiveQuery(q => shareCollection ? q.from({
361
- _share: shareCollection
362
- }) : null, [shareCollection]);
363
- const shareRows = useMemo(() => shareCollection ? rawShareRows ?? [] : void 0, [shareCollection, rawShareRows]);
364
- const owner = baselineDoc?.owner;
365
- const {
366
- rights,
367
- pending: rightsPending,
368
- roles: effectiveRoles
369
- } = useDocRights(frappeConfig, meta, {
370
- roles,
371
- owner: typeof owner === "string" ? owner : void 0,
372
- shares: shareRows
373
- });
374
- const permLevels = useMemo(() => buildPermLevelMatrix(meta?.permissions, effectiveRoles), [meta?.permissions, effectiveRoles]);
375
- const formTabs = useMemo(() => {
376
- if (!layoutFields) return void 0;
377
- return buildFormTabs({
378
- fields: layoutFields,
379
- doc: {
380
- ...localDoc,
381
- __islocal: isCreate ? 1 : 0
382
- },
383
- docstatus,
384
- formReadOnly: !!meta?.read_only,
385
- isCreate,
386
- detailsLabel: t("Details"),
387
- permLevels,
388
- hiddenFields: hideFields
389
- });
390
- }, [layoutFields, localDoc, docstatus, meta?.read_only, isCreate, permLevels, hideFields, t]);
391
- const assignmentCollection = useFrappeCollection(showSidebar && name && !frappeConfig.fixtures ? frappeConfig : void 0, "ToDo", {
287
+ const assignmentCollection = useFrappeCollection(showSidebar && name && frappeConfig && !frappeConfig.fixtures ? frappeConfig : void 0, "ToDo", {
392
288
  fields: ["name", "allocated_to", "status", "reference_type", "reference_name"],
393
289
  filters: {
394
290
  reference_type: doctype,
@@ -402,16 +298,6 @@ function FrappeFormBody({
402
298
  _assignment: assignmentCollection
403
299
  }) : null, [assignmentCollection]);
404
300
  const assignmentRows = useMemo(() => assignmentCollection ? rawAssignmentRows ?? [] : void 0, [assignmentCollection, rawAssignmentRows]);
405
- useEffect(() => {
406
- everLoadedRef.current = false;
407
- if (name) createDraftRef.current = void 0;
408
- setWasDeleted(false);
409
- setConflictFields([]);
410
- setDocError(null);
411
- const seed = seedRef.current;
412
- setBaselineDoc(seed);
413
- if (seed) setLocalDoc(seed);
414
- }, [doctype, name]);
415
301
  const effectiveConnections = useMemo(() => connections ?? connectionsFromMeta(meta), [connections, meta]);
416
302
  const readsConnections = Boolean(children) && Boolean(name) && effectiveConnections.length > 0;
417
303
  const [connectionSnapshots, setConnectionSnapshots] = useState({});
@@ -442,262 +328,48 @@ function FrappeFormBody({
442
328
  const connectionsLoading = connectionReads.some(({
443
329
  link
444
330
  }) => connectionSnapshots[link.doctype]?.isLoading ?? true);
445
- useFrappeDoctypeEvents(name ? frappeConfig : void 0, doctype, event => {
446
- if (event.type !== "doc_deleted") return;
447
- const payload = event.payload;
448
- if (payload?.name === name) setWasDeleted(true);
449
- }, Boolean(name));
450
- useEffect(() => {
451
- const incoming = liveDoc?.data;
452
- if (incoming) {
453
- everLoadedRef.current = true;
454
- setWasDeleted(false);
455
- const baseline = baselineRef.current;
456
- if (!baseline) {
457
- setBaselineDoc(incoming);
458
- setLocalDoc(incoming);
459
- return;
460
- }
461
- const local = localDocRef.current;
462
- const merged = {
463
- ...incoming
464
- };
465
- const newConflicts = [];
466
- for (const key of Object.keys(local)) {
467
- const localValue = local[key];
468
- if (localValue === baseline[key]) continue;
469
- merged[key] = localValue;
470
- const remote = incoming[key];
471
- if (remote !== baseline[key] && remote !== localValue) newConflicts.push(key);
472
- }
473
- setBaselineDoc(incoming);
474
- setLocalDoc(merged);
475
- if (newConflicts.length > 0) {
476
- setConflictFields(prev => Array.from(/* @__PURE__ */new Set([...prev, ...newConflicts])));
477
- }
478
- } else if (name && everLoadedRef.current && !liveDoc?.isLoading) {
479
- setWasDeleted(true);
480
- } else if (!name) {
481
- if (createDraftRef.current?.doctype !== doctype) {
482
- createDraftRef.current = {
483
- doctype,
484
- values: takeCreatePrefill(doctype)
485
- };
486
- }
487
- setLocalDoc({
488
- ...createDraftRef.current.values,
489
- doctype
490
- });
491
- }
492
- }, [liveDoc?.data, liveDoc?.isLoading, doctype, name]);
493
- const dirtyFieldNames = useMemo(() => {
494
- const local = localDoc;
495
- const base = baselineDoc;
496
- const dirty = [];
497
- for (const key of Object.keys(local)) {
498
- if (key === "doctype" || key === "name") continue;
499
- if (base ? local[key] !== base[key] : local[key] !== void 0) dirty.push(key);
500
- }
501
- return dirty;
502
- }, [localDoc, baselineDoc]);
503
- const hasUnsavedChanges = dirtyFieldNames.length > 0;
504
- const runFetchFrom = (field, value) => {
505
- const rules = fetchMap?.get(field);
506
- if (!rules || rules.length === 0) return;
507
- const applyPatch = fetched2 => {
508
- setLocalDoc(prev => {
509
- const current = prev;
510
- if (current[field] !== value) return prev;
511
- const patch = applyFetchedValues(rules, fetched2, current);
512
- return Object.keys(patch).length > 0 ? {
513
- ...current,
514
- ...patch
515
- } : prev;
516
- });
517
- };
518
- if (isEmptyFieldValue(value)) {
519
- applyPatch({});
520
- return;
521
- }
522
- const linkField = meta?.fields.find(metaField => metaField.fieldname === field);
523
- const targetDoctype = typeof linkField?.options === "string" ? linkField.options : "";
524
- if (!targetDoctype) return;
525
- const sources = Array.from(new Set(rules.map(rule => rule.source)));
526
- const fixtures = frappeConfig.fixtures;
527
- const fetched = fixtures ? fixtures.getDoc(targetDoctype, String(value), sources).then(doc => doc ?? {}) : validateLink(createHttpClient(frappeConfig), targetDoctype, String(value), sources);
528
- fetched.then(applyPatch).catch(() => void 0);
529
- };
530
- const setValue = (field, value) => {
531
- setLocalDoc(prev => ({
532
- ...prev,
533
- [field]: value
534
- }));
535
- setErrors(prev => {
536
- const newErrors = {
537
- ...prev
538
- };
539
- delete newErrors[String(field)];
540
- return newErrors;
541
- });
542
- runFetchFrom(String(field), value);
543
- };
544
- const validateRequiredFields = () => {
545
- if (!formTabs) return {};
546
- const validationErrors = {};
547
- const doc = localDoc;
548
- if (promptNamed && isEmptyFieldValue(doc.name)) {
549
- validationErrors.name = t("Name is required");
550
- }
551
- for (const tab of formTabs) {
552
- for (const field of tab.schema.fields) {
553
- if (!field.required) continue;
554
- if (isEmptyFieldValue(doc[field.name])) {
555
- validationErrors[field.name] = t("{{label}} is required", {
556
- label: field.label || field.name
557
- });
558
- }
559
- }
560
- }
561
- return validationErrors;
562
- };
563
- const canWriteRecord = isCreate ? rights.create : rights.write;
564
331
  const handleSave = async () => {
565
- if (wasDeleted || !canWriteRecord) return;
566
- const requiredErrors = validateRequiredFields();
567
- if (Object.keys(requiredErrors).length > 0) {
568
- setErrors(requiredErrors);
569
- return;
570
- }
571
- setIsSubmitting(true);
572
- setErrors({});
573
- try {
574
- if (name) {
575
- if (!docCollection) return;
576
- const changedFields = dirtyFieldNames;
577
- if (changedFields.length > 0) {
578
- const local = localDoc;
579
- const tx = docCollection.update(name, draft => {
580
- for (const key of changedFields) {
581
- draft[key] = local[key];
582
- }
583
- });
584
- await tx.isPersisted.promise;
585
- }
586
- setConflictFields([]);
587
- } else {
588
- if (!collection) return;
589
- const temporaryName = localDoc.name ?? `new-${Date.now()}`;
590
- const docToInsert = {
591
- ...localDoc,
592
- doctype,
593
- name: temporaryName
594
- };
595
- const tx = collection.insert(docToInsert);
596
- await tx.isPersisted.promise;
597
- const createdName = getFrappeSyncModule(frappeConfig)?.createdName(temporaryName) ?? temporaryName;
598
- onSave?.({
599
- ...localDoc,
600
- name: createdName
601
- });
602
- return;
603
- }
604
- onSave?.(localDoc);
605
- } catch (err) {
606
- const e = err;
607
- if (e?.exc_type === "ValidationError") {
608
- const validationErrors = {};
609
- if (e._server_messages) {
610
- try {
611
- const messages = JSON.parse(e._server_messages);
612
- for (const msg of messages) {
613
- const parsed = typeof msg === "string" ? JSON.parse(msg) : msg;
614
- if (parsed.message) {
615
- validationErrors.general = parsed.message;
616
- }
617
- }
618
- } catch {
619
- validationErrors.general = t("Validation error occurred");
620
- }
621
- }
622
- setErrors(validationErrors);
623
- } else {
624
- setErrors({
625
- general: e?.message || t("Failed to save document")
626
- });
627
- }
628
- } finally {
629
- setIsSubmitting(false);
630
- }
332
+ const saved = await form.save();
333
+ if (!saved) return;
334
+ onSave?.(isCreate ? {
335
+ ...localDoc,
336
+ name: saved
337
+ } : localDoc);
631
338
  };
632
339
  const handleCancel = () => {
633
- if (baselineDoc) {
634
- setLocalDoc(baselineDoc);
635
- } else if (liveDoc?.data) {
636
- setLocalDoc(liveDoc.data);
637
- } else {
638
- setLocalDoc({
639
- doctype
640
- });
641
- }
642
- setConflictFields([]);
643
- setErrors({});
340
+ form.reset();
644
341
  onCancel?.();
645
342
  };
646
343
  const handleDelete = async () => {
647
- if (!docCollection || !name || wasDeleted || !rights.delete) return;
344
+ if (!name || wasDeleted || !rights.delete) return;
648
345
  if (!(await dialog.confirm(t("Delete this {{doctype}}?", {
649
346
  doctype
650
347
  })))) return;
651
- setIsSubmitting(true);
652
- setErrors({});
653
- try {
654
- const tx = docCollection.delete(name);
655
- await tx.isPersisted.promise;
656
- onDelete?.(localDoc);
657
- } catch (err) {
658
- setErrors({
659
- general: err?.message || t("Failed to delete")
660
- });
661
- } finally {
662
- setIsSubmitting(false);
663
- }
348
+ if (await form.remove()) onDelete?.(localDoc);
664
349
  };
665
- const transitionDocstatus = async (to, question, failure) => {
666
- if (!docCollection || !name || wasDeleted || !rights[to === 1 ? "submit" : "cancel"]) return;
667
- if (to === 1) {
668
- const requiredErrors = validateRequiredFields();
669
- if (Object.keys(requiredErrors).length > 0) {
670
- setErrors(requiredErrors);
671
- return;
672
- }
673
- }
674
- if (!(await dialog.confirm(question))) return;
675
- setIsSubmitting(true);
676
- setErrors({});
677
- try {
678
- const tx = docCollection.update(name, draft => {
679
- draft.docstatus = to;
680
- });
681
- await tx.isPersisted.promise;
682
- } catch (err) {
683
- setErrors({
684
- general: extractServerError(err, failure)
685
- });
686
- } finally {
687
- setIsSubmitting(false);
350
+ const handleSubmitDocument = async () => {
351
+ if (!name || wasDeleted || !rights.submit) return;
352
+ const requiredErrors = form.validate();
353
+ if (Object.keys(requiredErrors).length > 0) {
354
+ form.setErrors(requiredErrors);
355
+ return;
688
356
  }
357
+ if (!(await dialog.confirm(t("Permanently Submit {{name}}?", {
358
+ name
359
+ })))) return;
360
+ await form.submitDocument();
361
+ };
362
+ const handleCancelDocument = async () => {
363
+ if (!name || wasDeleted || !rights.cancel) return;
364
+ if (!(await dialog.confirm(t("Permanently Cancel {{name}}?", {
365
+ name
366
+ })))) return;
367
+ await form.cancelDocument();
689
368
  };
690
- const handleSubmitDocument = () => transitionDocstatus(1, t("Permanently Submit {{name}}?", {
691
- name
692
- }), t("Failed to submit"));
693
- const handleCancelDocument = () => transitionDocstatus(2, t("Permanently Cancel {{name}}?", {
694
- name
695
- }), t("Failed to cancel"));
696
369
  const handleAmend = () => {
697
- if (!meta || !name || !onAmend || !rights.amend) return;
698
- const draft = buildAmendValues(meta.fields, localDoc, name);
699
- setCreatePrefill(doctype, draft);
700
- onAmend(draft);
370
+ if (!onAmend) return;
371
+ const draft = form.amend();
372
+ if (draft) onAmend(draft);
701
373
  };
702
374
  const titleField = meta?.title_field;
703
375
  const titleValue = titleField ? localDoc[titleField] : void 0;
@@ -757,27 +429,34 @@ function FrappeFormBody({
757
429
  }));
758
430
  }
759
431
  if (!children && metaError && !meta && !name) {
760
- return frame(/* @__PURE__ */jsxs(YStack, {
432
+ return frame(isPermissionError(metaError) ? /* @__PURE__ */jsx(EmptyState, {
433
+ testID: "frappe-form-not-permitted",
434
+ title: t("You can't create {{doctype}}", {
435
+ doctype
436
+ }),
437
+ description: metaError.message
438
+ }) : /* @__PURE__ */jsx(AsyncBoundary, {
761
439
  ...knobProps.panelPadding,
762
- alignItems: "center",
763
- ...knobProps.gap,
764
- children: [/* @__PURE__ */jsx(Text, {
765
- color: formCommonColors.error,
766
- ...knobProps.body,
767
- children: t("Failed to load {{doctype}} form", {
768
- doctype
769
- })
770
- }), /* @__PURE__ */jsx(Text, {
771
- color: formCommonColors.muted,
772
- ...knobProps.body,
773
- children: metaError.message
774
- })]
440
+ error: metaError,
441
+ errorTitle: t("Failed to load {{doctype}} form", {
442
+ doctype
443
+ })
775
444
  }));
776
445
  }
777
- const isDocReading = Boolean(name) && (!docCollection || Boolean(liveDoc?.isLoading));
778
- const showsSeed = Boolean(seedDoc) && isDocReading;
779
- if (name && docError && !liveDoc?.data && !wasDeleted) {
446
+ if (name && form.status === "error" && docError) {
780
447
  if (errorFallback !== void 0) return frame(errorFallback);
448
+ if (isPermissionError(docError)) {
449
+ return frame(/* @__PURE__ */jsx(EmptyState, {
450
+ testID: "frappe-form-not-permitted",
451
+ title: t("You can't open {{doctype}} {{name}}", {
452
+ doctype,
453
+ name
454
+ }),
455
+ description: t("Your session can't read this {{doctype}}.", {
456
+ doctype
457
+ })
458
+ }));
459
+ }
781
460
  return frame(/* @__PURE__ */jsx(AsyncBoundary, {
782
461
  ...knobProps.panelPadding,
783
462
  error: docError,
@@ -786,7 +465,7 @@ function FrappeFormBody({
786
465
  })
787
466
  }));
788
467
  }
789
- if (name && isDocReading && !showsSeed && !wasDeleted) {
468
+ if (name && form.status === "loading") {
790
469
  if (loadingFallback !== void 0) return frame(loadingFallback);
791
470
  return frame(/* @__PURE__ */jsx(YStack, {
792
471
  ...knobProps.panelPadding,
@@ -796,7 +475,7 @@ function FrappeFormBody({
796
475
  })
797
476
  }));
798
477
  }
799
- if (name && !isDocReading && !liveDoc?.data && !wasDeleted) {
478
+ if (name && form.status === "notFound") {
800
479
  if (notFoundFallback !== void 0) return frame(notFoundFallback);
801
480
  return frame(/* @__PURE__ */jsxs(YStack, {
802
481
  ...knobProps.panelPadding,
@@ -846,15 +525,13 @@ function FrappeFormBody({
846
525
  const toolbarShown = showToolbar && !readsWithoutMeta && Boolean(indicator || docAction || showDelete || showEdit || showClose);
847
526
  const shownTabs = formTabs ?? recordTabs;
848
527
  const imageFieldName = meta?.image_field;
849
- const sidebarImageUrl = imageFieldName ? resolveFileUrl(localDoc[imageFieldName], finalBaseURL) : void 0;
528
+ const sidebarImageUrl = imageFieldName ? resolveFileUrl(localDoc[imageFieldName], frappeConfig?.baseURL) : void 0;
850
529
  const conflictLabels = conflictFields.map(fieldname => {
851
530
  const field = meta?.fields.find(metaField => metaField.fieldname === fieldname);
852
531
  return field?.label || fieldname;
853
532
  }).join(", ");
854
- const formTabItems = (shownTabs ?? []).map(tab => ({
855
- value: tab.key,
856
- label: tab.label,
857
- content: /* @__PURE__ */jsx(SchemaForm, {
533
+ const formTabItems = (shownTabs ?? []).map(tab => {
534
+ const content = /* @__PURE__ */jsx(SchemaForm, {
858
535
  schema: tab.schema,
859
536
  doc: localDoc,
860
537
  setValue: (field, value) => setValue(field, value),
@@ -863,10 +540,16 @@ function FrappeFormBody({
863
540
  overrides,
864
541
  readOnlyFields,
865
542
  sectionHeadingLevel,
866
- order
867
- })
868
- }));
869
- const tabItems = showConnections && name && effectiveConnections.length > 0 ? [...formTabItems, {
543
+ order,
544
+ renderFieldWrapper
545
+ });
546
+ return {
547
+ value: tab.key,
548
+ label: tab.label,
549
+ content: renderTab ? renderTab(tab, content) : content
550
+ };
551
+ });
552
+ const tabItems = [...formTabItems, ...(showConnections && name && frappeConfig && effectiveConnections.length > 0 ? [{
870
553
  value: "__connections",
871
554
  label: t("Connections"),
872
555
  content: /* @__PURE__ */jsx(FrappeFormConnections, {
@@ -874,12 +557,12 @@ function FrappeFormBody({
874
557
  connections: effectiveConnections,
875
558
  name
876
559
  })
877
- }] : formTabItems;
560
+ }] : []), ...(extraTabs ?? [])];
878
561
  const effectiveTab = activeTab && tabItems.some(tab => tab.value === activeTab) ? activeTab : tabItems[0]?.value;
879
562
  const localName = localDoc.name;
880
563
  const sidebar = showSidebar && name && meta ? /* @__PURE__ */jsx(FormSidebar, {
881
564
  imageUrl: sidebarImageUrl,
882
- sections: buildSidebarSections(localDoc, shareRows, t, assignmentRows),
565
+ sections: buildSidebarSections(localDoc, form.shareRows, t, assignmentRows),
883
566
  metadata: mapDocToSidebarMetadata(localDoc, t)
884
567
  }) : void 0;
885
568
  return frame(
@@ -893,14 +576,14 @@ function FrappeFormBody({
893
576
  flexGrow: 1,
894
577
  flexShrink: 1,
895
578
  flexBasis: "auto",
896
- children: [/* @__PURE__ */jsx(FrappeReconnectIndicator, {
579
+ children: [frappeConfig ? /* @__PURE__ */jsx(FrappeReconnectIndicator, {
897
580
  config: frappeConfig
898
- }), connectionReads.map(read => /* @__PURE__ */jsx(FrappeRowsReader, {
581
+ }) : null, frappeConfig ? connectionReads.map(read => /* @__PURE__ */jsx(FrappeRowsReader, {
899
582
  connection: frappeConfig,
900
583
  doctype: read.link.doctype,
901
584
  options: read.options,
902
585
  onChange: read.onChange
903
- }, read.link.doctype)), wasDeleted && /* @__PURE__ */jsx(XStack, {
586
+ }, read.link.doctype)) : null, showBanners && wasDeleted && /* @__PURE__ */jsx(XStack, {
904
587
  role: "alert",
905
588
  alignItems: "center",
906
589
  ...knobProps.containerRadius,
@@ -916,7 +599,7 @@ function FrappeFormBody({
916
599
  doctype
917
600
  })
918
601
  })
919
- }), !wasDeleted && conflictFields.length > 0 && /* @__PURE__ */jsx(XStack, {
602
+ }), showBanners && !wasDeleted && conflictFields.length > 0 && /* @__PURE__ */jsx(XStack, {
920
603
  role: "status",
921
604
  alignItems: "center",
922
605
  ...knobProps.containerRadius,
@@ -978,6 +661,7 @@ function FrappeFormBody({
978
661
  }), errors.general && /* @__PURE__ */jsx(Text, {
979
662
  color: formCommonColors.error,
980
663
  ...knobProps.body,
664
+ role: "alert",
981
665
  children: errors.general
982
666
  }), toolbarShown && /* @__PURE__ */jsx(FrappeFormActions, {
983
667
  indicator,