@multiplatform.one/frappe-ui 7.22.0 → 7.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +14 -0
  2. package/dist/cjs/FrappeForm.cjs +96 -52
  3. package/dist/cjs/FrappeForm.native.js +103 -52
  4. package/dist/cjs/FrappeForm.native.js.map +1 -1
  5. package/dist/cjs/FrappeFormActions.cjs +161 -0
  6. package/dist/cjs/FrappeFormActions.native.js +167 -0
  7. package/dist/cjs/FrappeFormActions.native.js.map +1 -0
  8. package/dist/cjs/FrappeTable.cjs +42 -13
  9. package/dist/cjs/FrappeTable.native.js +55 -19
  10. package/dist/cjs/FrappeTable.native.js.map +1 -1
  11. package/dist/cjs/desk/DeskFormPage.cjs +2 -1
  12. package/dist/cjs/desk/DeskFormPage.native.js +3 -0
  13. package/dist/cjs/desk/DeskFormPage.native.js.map +1 -1
  14. package/dist/cjs/filterFields.cjs +277 -34
  15. package/dist/cjs/filterFields.native.js +311 -36
  16. package/dist/cjs/filterFields.native.js.map +1 -1
  17. package/dist/cjs/formEngine/docLifecycle.cjs +98 -0
  18. package/dist/cjs/formEngine/docLifecycle.native.js +131 -0
  19. package/dist/cjs/formEngine/docLifecycle.native.js.map +1 -0
  20. package/dist/cjs/formEngine/useAmendedAs.cjs +48 -0
  21. package/dist/cjs/formEngine/useAmendedAs.native.js +60 -0
  22. package/dist/cjs/formEngine/useAmendedAs.native.js.map +1 -0
  23. package/dist/cjs/formEngine/useDocRights.cjs +2 -2
  24. package/dist/cjs/formEngine/useDocRights.native.js +2 -2
  25. package/dist/cjs/formEngine/useDocRights.native.js.map +1 -1
  26. package/dist/cjs/formEngine/useFormEngine.cjs +1 -0
  27. package/dist/cjs/formEngine/useFormEngine.native.js +1 -0
  28. package/dist/cjs/formEngine/useFormEngine.native.js.map +1 -1
  29. package/dist/cjs/index.cjs +30 -1
  30. package/dist/cjs/index.native.js +30 -1
  31. package/dist/cjs/index.native.js.map +1 -1
  32. package/dist/cjs/list/listColumns.cjs +397 -0
  33. package/dist/cjs/list/listColumns.native.js +447 -0
  34. package/dist/cjs/list/listColumns.native.js.map +1 -0
  35. package/dist/cjs/list/listFields.cjs +50 -0
  36. package/dist/cjs/list/listFields.native.js +79 -0
  37. package/dist/cjs/list/listFields.native.js.map +1 -0
  38. package/dist/cjs/list/listFormat.cjs +68 -0
  39. package/dist/cjs/list/listFormat.native.js +74 -0
  40. package/dist/cjs/list/listFormat.native.js.map +1 -0
  41. package/dist/cjs/list/statusColors.cjs +102 -0
  42. package/dist/cjs/list/statusColors.native.js +109 -0
  43. package/dist/cjs/list/statusColors.native.js.map +1 -0
  44. package/dist/cjs/useFrappeAggregate.cjs +1 -1
  45. package/dist/cjs/useFrappeAggregate.native.js +1 -1
  46. package/dist/cjs/useFrappeAggregate.native.js.map +1 -1
  47. package/dist/esm/FrappeForm.mjs +97 -53
  48. package/dist/esm/FrappeForm.mjs.map +1 -1
  49. package/dist/esm/FrappeForm.native.js +104 -53
  50. package/dist/esm/FrappeForm.native.js.map +1 -1
  51. package/dist/esm/FrappeFormActions.mjs +136 -0
  52. package/dist/esm/FrappeFormActions.mjs.map +1 -0
  53. package/dist/esm/FrappeFormActions.native.js +139 -0
  54. package/dist/esm/FrappeFormActions.native.js.map +1 -0
  55. package/dist/esm/FrappeTable.mjs +42 -13
  56. package/dist/esm/FrappeTable.mjs.map +1 -1
  57. package/dist/esm/FrappeTable.native.js +55 -19
  58. package/dist/esm/FrappeTable.native.js.map +1 -1
  59. package/dist/esm/desk/DeskFormPage.mjs +2 -1
  60. package/dist/esm/desk/DeskFormPage.mjs.map +1 -1
  61. package/dist/esm/desk/DeskFormPage.native.js +3 -0
  62. package/dist/esm/desk/DeskFormPage.native.js.map +1 -1
  63. package/dist/esm/filterFields.mjs +263 -34
  64. package/dist/esm/filterFields.mjs.map +1 -1
  65. package/dist/esm/filterFields.native.js +297 -36
  66. package/dist/esm/filterFields.native.js.map +1 -1
  67. package/dist/esm/formEngine/docLifecycle.mjs +68 -0
  68. package/dist/esm/formEngine/docLifecycle.mjs.map +1 -0
  69. package/dist/esm/formEngine/docLifecycle.native.js +98 -0
  70. package/dist/esm/formEngine/docLifecycle.native.js.map +1 -0
  71. package/dist/esm/formEngine/useAmendedAs.mjs +23 -0
  72. package/dist/esm/formEngine/useAmendedAs.mjs.map +1 -0
  73. package/dist/esm/formEngine/useAmendedAs.native.js +32 -0
  74. package/dist/esm/formEngine/useAmendedAs.native.js.map +1 -0
  75. package/dist/esm/formEngine/useDocRights.mjs +2 -2
  76. package/dist/esm/formEngine/useDocRights.mjs.map +1 -1
  77. package/dist/esm/formEngine/useDocRights.native.js +2 -2
  78. package/dist/esm/formEngine/useDocRights.native.js.map +1 -1
  79. package/dist/esm/formEngine/useFormEngine.mjs +1 -1
  80. package/dist/esm/formEngine/useFormEngine.mjs.map +1 -1
  81. package/dist/esm/formEngine/useFormEngine.native.js +1 -1
  82. package/dist/esm/formEngine/useFormEngine.native.js.map +1 -1
  83. package/dist/esm/index.mjs +6 -2
  84. package/dist/esm/index.mjs.map +1 -1
  85. package/dist/esm/index.native.js +6 -2
  86. package/dist/esm/index.native.js.map +1 -1
  87. package/dist/esm/list/listColumns.mjs +372 -0
  88. package/dist/esm/list/listColumns.mjs.map +1 -0
  89. package/dist/esm/list/listColumns.native.js +419 -0
  90. package/dist/esm/list/listColumns.native.js.map +1 -0
  91. package/dist/esm/list/listFields.mjs +23 -0
  92. package/dist/esm/list/listFields.mjs.map +1 -0
  93. package/dist/esm/list/listFields.native.js +49 -0
  94. package/dist/esm/list/listFields.native.js.map +1 -0
  95. package/dist/esm/list/listFormat.mjs +38 -0
  96. package/dist/esm/list/listFormat.mjs.map +1 -0
  97. package/dist/esm/list/listFormat.native.js +41 -0
  98. package/dist/esm/list/listFormat.native.js.map +1 -0
  99. package/dist/esm/list/statusColors.mjs +77 -0
  100. package/dist/esm/list/statusColors.mjs.map +1 -0
  101. package/dist/esm/list/statusColors.native.js +81 -0
  102. package/dist/esm/list/statusColors.native.js.map +1 -0
  103. package/dist/esm/useFrappeAggregate.mjs +1 -1
  104. package/dist/esm/useFrappeAggregate.mjs.map +1 -1
  105. package/dist/esm/useFrappeAggregate.native.js +1 -1
  106. package/dist/esm/useFrappeAggregate.native.js.map +1 -1
  107. package/dist/jsx/FrappeForm.mjs +97 -53
  108. package/dist/jsx/FrappeForm.mjs.map +1 -1
  109. package/dist/jsx/FrappeForm.native.js +103 -52
  110. package/dist/jsx/FrappeForm.native.js.map +1 -1
  111. package/dist/jsx/FrappeFormActions.mjs +136 -0
  112. package/dist/jsx/FrappeFormActions.mjs.map +1 -0
  113. package/dist/jsx/FrappeFormActions.native.js +167 -0
  114. package/dist/jsx/FrappeFormActions.native.js.map +1 -0
  115. package/dist/jsx/FrappeTable.mjs +42 -13
  116. package/dist/jsx/FrappeTable.mjs.map +1 -1
  117. package/dist/jsx/FrappeTable.native.js +55 -19
  118. package/dist/jsx/FrappeTable.native.js.map +1 -1
  119. package/dist/jsx/desk/DeskFormPage.mjs +2 -1
  120. package/dist/jsx/desk/DeskFormPage.mjs.map +1 -1
  121. package/dist/jsx/desk/DeskFormPage.native.js +3 -0
  122. package/dist/jsx/desk/DeskFormPage.native.js.map +1 -1
  123. package/dist/jsx/filterFields.mjs +263 -34
  124. package/dist/jsx/filterFields.mjs.map +1 -1
  125. package/dist/jsx/filterFields.native.js +311 -36
  126. package/dist/jsx/filterFields.native.js.map +1 -1
  127. package/dist/jsx/formEngine/docLifecycle.mjs +68 -0
  128. package/dist/jsx/formEngine/docLifecycle.mjs.map +1 -0
  129. package/dist/jsx/formEngine/docLifecycle.native.js +131 -0
  130. package/dist/jsx/formEngine/docLifecycle.native.js.map +1 -0
  131. package/dist/jsx/formEngine/useAmendedAs.mjs +23 -0
  132. package/dist/jsx/formEngine/useAmendedAs.mjs.map +1 -0
  133. package/dist/jsx/formEngine/useAmendedAs.native.js +60 -0
  134. package/dist/jsx/formEngine/useAmendedAs.native.js.map +1 -0
  135. package/dist/jsx/formEngine/useDocRights.mjs +2 -2
  136. package/dist/jsx/formEngine/useDocRights.mjs.map +1 -1
  137. package/dist/jsx/formEngine/useDocRights.native.js +2 -2
  138. package/dist/jsx/formEngine/useDocRights.native.js.map +1 -1
  139. package/dist/jsx/formEngine/useFormEngine.mjs +1 -1
  140. package/dist/jsx/formEngine/useFormEngine.mjs.map +1 -1
  141. package/dist/jsx/formEngine/useFormEngine.native.js +1 -0
  142. package/dist/jsx/formEngine/useFormEngine.native.js.map +1 -1
  143. package/dist/jsx/index.js +6 -2
  144. package/dist/jsx/index.js.map +1 -1
  145. package/dist/jsx/index.mjs +6 -2
  146. package/dist/jsx/index.mjs.map +1 -1
  147. package/dist/jsx/index.native.js +30 -1
  148. package/dist/jsx/index.native.js.map +1 -1
  149. package/dist/jsx/list/listColumns.mjs +372 -0
  150. package/dist/jsx/list/listColumns.mjs.map +1 -0
  151. package/dist/jsx/list/listColumns.native.js +447 -0
  152. package/dist/jsx/list/listColumns.native.js.map +1 -0
  153. package/dist/jsx/list/listFields.mjs +23 -0
  154. package/dist/jsx/list/listFields.mjs.map +1 -0
  155. package/dist/jsx/list/listFields.native.js +79 -0
  156. package/dist/jsx/list/listFields.native.js.map +1 -0
  157. package/dist/jsx/list/listFormat.mjs +38 -0
  158. package/dist/jsx/list/listFormat.mjs.map +1 -0
  159. package/dist/jsx/list/listFormat.native.js +74 -0
  160. package/dist/jsx/list/listFormat.native.js.map +1 -0
  161. package/dist/jsx/list/statusColors.mjs +77 -0
  162. package/dist/jsx/list/statusColors.mjs.map +1 -0
  163. package/dist/jsx/list/statusColors.native.js +109 -0
  164. package/dist/jsx/list/statusColors.native.js.map +1 -0
  165. package/dist/jsx/useFrappeAggregate.mjs +1 -1
  166. package/dist/jsx/useFrappeAggregate.mjs.map +1 -1
  167. package/dist/jsx/useFrappeAggregate.native.js +1 -1
  168. package/dist/jsx/useFrappeAggregate.native.js.map +1 -1
  169. package/package.json +22 -16
  170. package/src/FrappeForm.deskSemantics.spec.tsx +1 -1
  171. package/src/FrappeForm.lifecycle.spec.tsx +463 -0
  172. package/src/FrappeForm.lifecycle.stories.tsx +84 -0
  173. package/src/FrappeForm.tsx +124 -84
  174. package/src/FrappeFormActions.tsx +216 -0
  175. package/src/FrappeTable.defaultColumns.spec.tsx +229 -0
  176. package/src/FrappeTable.tsx +79 -32
  177. package/src/desk/DeskFormPage.tsx +1 -0
  178. package/src/filterFields.spec.ts +57 -2
  179. package/src/filterFields.ts +365 -38
  180. package/src/formEngine/docLifecycle.spec.ts +195 -0
  181. package/src/formEngine/docLifecycle.ts +148 -0
  182. package/src/formEngine/useAmendedAs.ts +38 -0
  183. package/src/formEngine/useDocRights.ts +11 -3
  184. package/src/formEngine/useFormEngine.ts +1 -1
  185. package/src/frappeViews.designLaw.spec.ts +6 -0
  186. package/src/index.ts +47 -1
  187. package/src/list/listColumns.tsx +488 -0
  188. package/src/list/listFields.spec.ts +147 -0
  189. package/src/list/listFields.ts +68 -0
  190. package/src/list/listFormat.spec.ts +151 -0
  191. package/src/list/listFormat.ts +80 -0
  192. package/src/list/seam.spec.ts +116 -0
  193. package/src/list/statusColors.spec.ts +100 -0
  194. package/src/list/statusColors.ts +94 -0
  195. package/src/useFrappeAggregate.ts +1 -1
  196. package/types/FrappeForm.d.ts +13 -3
  197. package/types/FrappeForm.d.ts.map +1 -1
  198. package/types/FrappeFormActions.d.ts +36 -0
  199. package/types/FrappeFormActions.d.ts.map +1 -0
  200. package/types/FrappeTable.d.ts +11 -5
  201. package/types/FrappeTable.d.ts.map +1 -1
  202. package/types/desk/DeskFormPage.d.ts.map +1 -1
  203. package/types/filterFields.d.ts +81 -10
  204. package/types/filterFields.d.ts.map +1 -1
  205. package/types/formEngine/docLifecycle.d.ts +59 -0
  206. package/types/formEngine/docLifecycle.d.ts.map +1 -0
  207. package/types/formEngine/useAmendedAs.d.ts +9 -0
  208. package/types/formEngine/useAmendedAs.d.ts.map +1 -0
  209. package/types/formEngine/useDocRights.d.ts +1 -1
  210. package/types/formEngine/useDocRights.d.ts.map +1 -1
  211. package/types/formEngine/useFormEngine.d.ts +1 -0
  212. package/types/formEngine/useFormEngine.d.ts.map +1 -1
  213. package/types/index.d.ts +5 -1
  214. package/types/index.d.ts.map +1 -1
  215. package/types/list/listColumns.d.ts +41 -0
  216. package/types/list/listColumns.d.ts.map +1 -0
  217. package/types/list/listFields.d.ts +16 -0
  218. package/types/list/listFields.d.ts.map +1 -0
  219. package/types/list/listFormat.d.ts +30 -0
  220. package/types/list/listFormat.d.ts.map +1 -0
  221. package/types/list/statusColors.d.ts +14 -0
  222. package/types/list/statusColors.d.ts.map +1 -0
package/README.md CHANGED
@@ -24,6 +24,10 @@ pnpm add @multiplatform.one/frappe-ui
24
24
  - **Field registry** — maps Frappe fieldtypes to form field components
25
25
  (`registerField`, `getFieldComponent`, `initDefaultRegistry`,
26
26
  `fieldtypeToTableField`, `childFieldsToColumns`)
27
+ - **Desk list** — `buildListColumns` (the desk list's column derivation, which
28
+ `FrappeTable` defaults to), `listFieldsToFetch`, and the one filterable-field
29
+ vocabulary both list stacks read (`filterableFieldsFromMeta`,
30
+ `docFieldsToFilterFields`, desk's operators and tuple conversion)
27
31
 
28
32
  ## What it must not do
29
33
 
@@ -64,6 +68,16 @@ export function ToDoTable() {
64
68
  `FrappeTable` accepts the rest of the `DataTable` props from
65
69
  `@multiplatform.one/table` (filtering, sorting, pagination, `serverSide`, …).
66
70
 
71
+ `columns` is optional. Without it the table renders the doctype the way desk's
72
+ list does: `buildListColumns` derives the title, the `in_list_view` fields the
73
+ session's roles may read and the activity cluster from the doctype's meta, and
74
+ a bench read asks for `listFieldsToFetch`. The table waits for meta and the
75
+ roles before that read, so it happens once.
76
+
77
+ ```tsx
78
+ <FrappeTable<ToDo> doctype="ToDo" serverSide />
79
+ ```
80
+
67
81
  ### Infinite scroll (server-side)
68
82
 
69
83
  With `serverSide` and `paginationMode="infinite"`, `FrappeTable` supplies the
@@ -47,7 +47,12 @@ var import_fetchFrom = require("./formEngine/fetchFrom.cjs");
47
47
  var import_fieldPermissions = require("./formEngine/fieldPermissions.cjs");
48
48
  var import_formActions = require("./formEngine/formActions.cjs");
49
49
  var import_formLayout = require("./formEngine/formLayout.cjs");
50
+ var import_createPrefill = require("./formEngine/createPrefill.cjs");
51
+ var import_docLifecycle = require("./formEngine/docLifecycle.cjs");
52
+ var import_useAmendedAs = require("./formEngine/useAmendedAs.cjs");
50
53
  var import_useDocRights = require("./formEngine/useDocRights.cjs");
54
+ var import_useFormEngine = require("./formEngine/useFormEngine.cjs");
55
+ var import_FrappeFormActions = require("./FrappeFormActions.cjs");
51
56
  var import_frappeConnections = require("./frappeConnections.cjs");
52
57
  var import_FrappeFormConnections = require("./FrappeFormConnections.cjs");
53
58
  var import_FrappeRowsReader = require("./FrappeRowsReader.cjs");
@@ -174,6 +179,7 @@ function FrappeFormRoute({
174
179
  onCancel,
175
180
  onDelete,
176
181
  onEdit,
182
+ onAmend,
177
183
  ...props
178
184
  }) {
179
185
  const router = (0, import_frappeNavigation.useFrappeRouter)();
@@ -216,6 +222,9 @@ function FrappeFormRoute({
216
222
  })),
217
223
  onDelete: onDelete ?? (() => go({
218
224
  mode: "list"
225
+ })),
226
+ onAmend: onAmend ?? (() => go({
227
+ mode: "create"
219
228
  }))
220
229
  });
221
230
  }
@@ -236,6 +245,7 @@ function FrappeFormBody({
236
245
  onSave,
237
246
  onCancel,
238
247
  onDelete,
248
+ onAmend,
239
249
  onEdit,
240
250
  showToolbar = true,
241
251
  showSidebar = true,
@@ -273,6 +283,7 @@ function FrappeFormBody({
273
283
  localDocRef.current = localDoc;
274
284
  const baselineRef = (0, import_react2.useRef)(baselineDoc);
275
285
  baselineRef.current = baselineDoc;
286
+ const createDraftRef = (0, import_react2.useRef)(void 0);
276
287
  const finalBaseURL = baseURL ?? config?.baseURL;
277
288
  const finalAuth = auth ?? config?.auth;
278
289
  const finalSyncOptions = syncOptions ?? config?.syncOptions;
@@ -294,12 +305,14 @@ function FrappeFormBody({
294
305
  isLoading: isMetaLoading,
295
306
  error: metaError
296
307
  } = (0, import_frappe.useDocTypeMeta)(frappeConfig, doctype);
308
+ const isSubmittable = !!meta?.is_submittable;
297
309
  const isCreate = !name;
298
310
  const docstatus = Number(baselineDoc?.docstatus ?? 0);
299
311
  const layoutFields = (0, import_react2.useMemo)(() => {
300
312
  if (!meta) return void 0;
301
313
  return sections ? sectionsToLayoutFields(sections) : meta.fields;
302
314
  }, [meta, sections]);
315
+ const amendedAs = (0, import_useAmendedAs.useAmendedAs)(isSubmittable && docstatus === 2 ? frappeConfig : void 0, doctype, name);
303
316
  const readsWithoutMeta = !children && !meta && Boolean(metaError) && Boolean(name);
304
317
  const recordTabs = (0, import_react2.useMemo)(() => {
305
318
  if (!readsWithoutMeta || !baselineDoc) return void 0;
@@ -413,6 +426,7 @@ function FrappeFormBody({
413
426
  const assignmentRows = (0, import_react2.useMemo)(() => assignmentCollection ? rawAssignmentRows ?? [] : void 0, [assignmentCollection, rawAssignmentRows]);
414
427
  (0, import_react2.useEffect)(() => {
415
428
  everLoadedRef.current = false;
429
+ if (name) createDraftRef.current = void 0;
416
430
  setWasDeleted(false);
417
431
  setConflictFields([]);
418
432
  setDocError(null);
@@ -486,7 +500,14 @@ function FrappeFormBody({
486
500
  } else if (name && everLoadedRef.current && !liveDoc?.isLoading) {
487
501
  setWasDeleted(true);
488
502
  } else if (!name) {
503
+ if (createDraftRef.current?.doctype !== doctype) {
504
+ createDraftRef.current = {
505
+ doctype,
506
+ values: (0, import_createPrefill.takeCreatePrefill)(doctype)
507
+ };
508
+ }
489
509
  setLocalDoc({
510
+ ...createDraftRef.current.values,
490
511
  doctype
491
512
  });
492
513
  }
@@ -663,6 +684,43 @@ function FrappeFormBody({
663
684
  setIsSubmitting(false);
664
685
  }
665
686
  };
687
+ const transitionDocstatus = async (to, question, failure) => {
688
+ if (!docCollection || !name || wasDeleted || !rights[to === 1 ? "submit" : "cancel"]) return;
689
+ if (to === 1) {
690
+ const requiredErrors = validateRequiredFields();
691
+ if (Object.keys(requiredErrors).length > 0) {
692
+ setErrors(requiredErrors);
693
+ return;
694
+ }
695
+ }
696
+ if (!(await import_desktop.dialog.confirm(question))) return;
697
+ setIsSubmitting(true);
698
+ setErrors({});
699
+ try {
700
+ const tx = docCollection.update(name, draft => {
701
+ draft.docstatus = to;
702
+ });
703
+ await tx.isPersisted.promise;
704
+ } catch (err) {
705
+ setErrors({
706
+ general: (0, import_useFormEngine.extractServerError)(err, failure)
707
+ });
708
+ } finally {
709
+ setIsSubmitting(false);
710
+ }
711
+ };
712
+ const handleSubmitDocument = () => transitionDocstatus(1, t("Permanently Submit {{name}}?", {
713
+ name
714
+ }), t("Failed to submit"));
715
+ const handleCancelDocument = () => transitionDocstatus(2, t("Permanently Cancel {{name}}?", {
716
+ name
717
+ }), t("Failed to cancel"));
718
+ const handleAmend = () => {
719
+ if (!meta || !name || !onAmend || !rights.amend) return;
720
+ const draft = (0, import_docLifecycle.buildAmendValues)(meta.fields, localDoc, name);
721
+ (0, import_createPrefill.setCreatePrefill)(doctype, draft);
722
+ onAmend(draft);
723
+ };
666
724
  const titleField = meta?.title_field;
667
725
  const titleValue = titleField ? localDoc[titleField] : void 0;
668
726
  const pageTitle = isCreate ? t("New {{doctype}}", {
@@ -784,12 +842,30 @@ function FrappeFormBody({
784
842
  const effectiveDisabled = disabled || wasDeleted;
785
843
  const readOnlyView = Boolean(onEdit) || readsWithoutMeta;
786
844
  const fieldsDisabled = effectiveDisabled || readOnlyView || !canWriteRecord;
787
- const showDelete = Boolean(name && onDelete && !wasDeleted && rights.delete);
788
- const showEdit = Boolean(onEdit && rights.write);
789
- const showSave = !onEdit && canWriteRecord;
790
- const showCancel = !onEdit && Boolean(onCancel);
791
- const showNotSaved = hasUnsavedChanges && !onEdit;
792
- const toolbarShown = showToolbar && !readsWithoutMeta && (showDelete || showEdit || showSave || showCancel || showNotSaved);
845
+ const indicator = (0, import_docLifecycle.resolveDocIndicator)({
846
+ docstatus,
847
+ isCreate,
848
+ isDirty: hasUnsavedChanges && !onEdit,
849
+ isSubmittable
850
+ });
851
+ const docAction = (0, import_docLifecycle.resolveDocAction)({
852
+ docstatus,
853
+ isCreate,
854
+ isDirty: hasUnsavedChanges,
855
+ isSubmittable,
856
+ perms: {
857
+ write: canWriteRecord,
858
+ submit: rights.submit,
859
+ cancel: rights.cancel,
860
+ amend: rights.amend
861
+ },
862
+ isReadOnlyView: Boolean(onEdit)
863
+ });
864
+ const showDelete = Boolean(name && onDelete && !wasDeleted && rights.delete && (0, import_docLifecycle.canDeleteDoc)(docstatus));
865
+ const showEdit = Boolean(onEdit && rights.write && (0, import_docLifecycle.canEditDoc)(docstatus, meta));
866
+ const showAmend = Boolean(onAmend && (0, import_docLifecycle.isAmendable)(meta));
867
+ const showClose = !onEdit && Boolean(onCancel);
868
+ const toolbarShown = showToolbar && !readsWithoutMeta && Boolean(indicator || docAction || showDelete || showEdit || showClose);
793
869
  const shownTabs = formTabs ?? recordTabs;
794
870
  const imageFieldName = meta?.image_field;
795
871
  const sidebarImageUrl = imageFieldName ? resolveFileUrl(localDoc[imageFieldName], finalBaseURL) : void 0;
@@ -925,52 +1001,20 @@ function FrappeFormBody({
925
1001
  color: import_forms.formCommonColors.error,
926
1002
  ...knobProps.body,
927
1003
  children: errors.general
928
- }), toolbarShown && /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.XStack, {
929
- ...knobProps.gap,
930
- justifyContent: "flex-end",
931
- alignItems: "center",
932
- paddingTop: "$3",
933
- children: [showNotSaved && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
934
- color: "$orange11",
935
- marginInlineEnd: "auto",
936
- ...knobProps.body,
937
- children: t("Not Saved")
938
- }), showDelete && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
939
- outlined: true,
940
- error: true,
941
- icon: import_react.TrashIcon,
942
- loading: isSubmitting,
943
- disabled: effectiveDisabled,
944
- disabledReason: t("Form is read-only"),
945
- disabledReasonPlacement: "inline",
946
- onPress: handleDelete,
947
- children: t("Delete")
948
- }), showEdit && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
949
- accent: true,
950
- icon: import_react.PencilSimpleIcon,
951
- disabled: effectiveDisabled,
952
- disabledReason: t("Form is read-only"),
953
- disabledReasonPlacement: "inline",
954
- onPress: onEdit,
955
- children: t("Edit")
956
- }), showCancel && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
957
- outlined: true,
958
- icon: import_react.XIcon,
959
- disabled,
960
- disabledReason: t("Form is read-only"),
961
- disabledReasonPlacement: "inline",
962
- onPress: handleCancel,
963
- children: t("Cancel")
964
- }), showSave && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
965
- accent: true,
966
- icon: import_react.FloppyDiskIcon,
967
- loading: isSubmitting,
968
- disabled: effectiveDisabled,
969
- disabledReason: t("Form is read-only"),
970
- disabledReasonPlacement: "inline",
971
- onPress: handleSave,
972
- children: isSubmitting ? t("Saving...") : t("Save")
973
- })]
1004
+ }), toolbarShown && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_FrappeFormActions.FrappeFormActions, {
1005
+ indicator,
1006
+ action: docAction,
1007
+ busy: isSubmitting,
1008
+ disabled: effectiveDisabled,
1009
+ onSave: handleSave,
1010
+ onSubmit: handleSubmitDocument,
1011
+ onCancelDocument: handleCancelDocument,
1012
+ onAmend: showAmend ? handleAmend : void 0,
1013
+ amendedAs,
1014
+ onDelete: showDelete ? handleDelete : void 0,
1015
+ onEdit: showEdit ? onEdit : void 0,
1016
+ onClose: showClose ? handleCancel : void 0,
1017
+ closeDisabled: disabled
974
1018
  })]
975
1019
  })]
976
1020
  }), showTimeline && name && !readsWithoutMeta && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_timeline.Timeline, {
@@ -51,7 +51,12 @@ var import_fetchFrom = require("./formEngine/fetchFrom.native.js");
51
51
  var import_fieldPermissions = require("./formEngine/fieldPermissions.native.js");
52
52
  var import_formActions = require("./formEngine/formActions.native.js");
53
53
  var import_formLayout = require("./formEngine/formLayout.native.js");
54
+ var import_createPrefill = require("./formEngine/createPrefill.native.js");
55
+ var import_docLifecycle = require("./formEngine/docLifecycle.native.js");
56
+ var import_useAmendedAs = require("./formEngine/useAmendedAs.native.js");
54
57
  var import_useDocRights = require("./formEngine/useDocRights.native.js");
58
+ var import_useFormEngine = require("./formEngine/useFormEngine.native.js");
59
+ var import_FrappeFormActions = require("./FrappeFormActions.native.js");
55
60
  var import_frappeConnections = require("./frappeConnections.native.js");
56
61
  var import_FrappeFormConnections = require("./FrappeFormConnections.native.js");
57
62
  var import_FrappeRowsReader = require("./FrappeRowsReader.native.js");
@@ -203,6 +208,7 @@ function FrappeFormRoute(param) {
203
208
  onCancel,
204
209
  onDelete,
205
210
  onEdit,
211
+ onAmend,
206
212
  ...props
207
213
  } = param;
208
214
  var router = (0, import_frappeNavigation.useFrappeRouter)();
@@ -257,6 +263,11 @@ function FrappeFormRoute(param) {
257
263
  return go({
258
264
  mode: "list"
259
265
  });
266
+ },
267
+ onAmend: onAmend !== null && onAmend !== void 0 ? onAmend : function () {
268
+ return go({
269
+ mode: "create"
270
+ });
260
271
  }
261
272
  });
262
273
  }
@@ -278,6 +289,7 @@ function FrappeFormBody(param) {
278
289
  onSave,
279
290
  onCancel,
280
291
  onDelete,
292
+ onAmend,
281
293
  onEdit,
282
294
  showToolbar = true,
283
295
  showSidebar = true,
@@ -321,6 +333,7 @@ function FrappeFormBody(param) {
321
333
  localDocRef.current = localDoc;
322
334
  var baselineRef = (0, import_react3.useRef)(baselineDoc);
323
335
  baselineRef.current = baselineDoc;
336
+ var createDraftRef = (0, import_react3.useRef)(void 0);
324
337
  var finalBaseURL = baseURL !== null && baseURL !== void 0 ? baseURL : config === null || config === void 0 ? void 0 : config.baseURL;
325
338
  var finalAuth = auth !== null && auth !== void 0 ? auth : config === null || config === void 0 ? void 0 : config.auth;
326
339
  var finalSyncOptions = syncOptions !== null && syncOptions !== void 0 ? syncOptions : config === null || config === void 0 ? void 0 : config.syncOptions;
@@ -344,12 +357,14 @@ function FrappeFormBody(param) {
344
357
  isLoading: isMetaLoading,
345
358
  error: metaError
346
359
  } = (0, import_frappe.useDocTypeMeta)(frappeConfig, doctype);
360
+ var isSubmittable = !!(meta === null || meta === void 0 ? void 0 : meta.is_submittable);
347
361
  var isCreate = !name;
348
362
  var docstatus = Number((_ref = baselineDoc === null || baselineDoc === void 0 ? void 0 : baselineDoc.docstatus) !== null && _ref !== void 0 ? _ref : 0);
349
363
  var layoutFields = (0, import_react3.useMemo)(function () {
350
364
  if (!meta) return void 0;
351
365
  return sections ? sectionsToLayoutFields(sections) : meta.fields;
352
366
  }, [meta, sections]);
367
+ var amendedAs = (0, import_useAmendedAs.useAmendedAs)(isSubmittable && docstatus === 2 ? frappeConfig : void 0, doctype, name);
353
368
  var readsWithoutMeta = !children && !meta && Boolean(metaError) && Boolean(name);
354
369
  var recordTabs = (0, import_react3.useMemo)(function () {
355
370
  if (!readsWithoutMeta || !baselineDoc) return void 0;
@@ -477,6 +492,7 @@ function FrappeFormBody(param) {
477
492
  }, [assignmentCollection, rawAssignmentRows]);
478
493
  (0, import_react3.useEffect)(function () {
479
494
  everLoadedRef.current = false;
495
+ if (name) createDraftRef.current = void 0;
480
496
  setWasDeleted(false);
481
497
  setConflictFields([]);
482
498
  setDocError(null);
@@ -608,7 +624,15 @@ function FrappeFormBody(param) {
608
624
  } else if (name && everLoadedRef.current && !(liveDoc === null || liveDoc === void 0 ? void 0 : liveDoc.isLoading)) {
609
625
  setWasDeleted(true);
610
626
  } else if (!name) {
627
+ var _createDraftRef_current;
628
+ if (((_createDraftRef_current = createDraftRef.current) === null || _createDraftRef_current === void 0 ? void 0 : _createDraftRef_current.doctype) !== doctype) {
629
+ createDraftRef.current = {
630
+ doctype,
631
+ values: (0, import_createPrefill.takeCreatePrefill)(doctype)
632
+ };
633
+ }
611
634
  setLocalDoc({
635
+ ...createDraftRef.current.values,
612
636
  doctype
613
637
  });
614
638
  }
@@ -892,6 +916,47 @@ function FrappeFormBody(param) {
892
916
  setIsSubmitting(false);
893
917
  }
894
918
  };
919
+ var transitionDocstatus = async function transitionDocstatus2(to, question, failure) {
920
+ if (!docCollection || !name || wasDeleted || !rights[to === 1 ? "submit" : "cancel"]) return;
921
+ if (to === 1) {
922
+ var requiredErrors = validateRequiredFields();
923
+ if (Object.keys(requiredErrors).length > 0) {
924
+ setErrors(requiredErrors);
925
+ return;
926
+ }
927
+ }
928
+ if (!(await import_desktop.dialog.confirm(question))) return;
929
+ setIsSubmitting(true);
930
+ setErrors({});
931
+ try {
932
+ var tx = docCollection.update(name, function (draft) {
933
+ draft.docstatus = to;
934
+ });
935
+ await tx.isPersisted.promise;
936
+ } catch (err) {
937
+ setErrors({
938
+ general: (0, import_useFormEngine.extractServerError)(err, failure)
939
+ });
940
+ } finally {
941
+ setIsSubmitting(false);
942
+ }
943
+ };
944
+ var handleSubmitDocument = function handleSubmitDocument2() {
945
+ return transitionDocstatus(1, t("Permanently Submit {{name}}?", {
946
+ name
947
+ }), t("Failed to submit"));
948
+ };
949
+ var handleCancelDocument = function handleCancelDocument2() {
950
+ return transitionDocstatus(2, t("Permanently Cancel {{name}}?", {
951
+ name
952
+ }), t("Failed to cancel"));
953
+ };
954
+ var handleAmend = function handleAmend2() {
955
+ if (!meta || !name || !onAmend || !rights.amend) return;
956
+ var draft = (0, import_docLifecycle.buildAmendValues)(meta.fields, localDoc, name);
957
+ (0, import_createPrefill.setCreatePrefill)(doctype, draft);
958
+ onAmend(draft);
959
+ };
895
960
  var titleField = meta === null || meta === void 0 ? void 0 : meta.title_field;
896
961
  var titleValue = titleField ? localDoc[titleField] : void 0;
897
962
  var pageTitle = isCreate ? t("New {{doctype}}", {
@@ -1015,12 +1080,30 @@ function FrappeFormBody(param) {
1015
1080
  var effectiveDisabled = disabled || wasDeleted;
1016
1081
  var readOnlyView = Boolean(onEdit) || readsWithoutMeta;
1017
1082
  var fieldsDisabled = effectiveDisabled || readOnlyView || !canWriteRecord;
1018
- var showDelete = Boolean(name && onDelete && !wasDeleted && rights.delete);
1019
- var showEdit = Boolean(onEdit && rights.write);
1020
- var showSave = !onEdit && canWriteRecord;
1021
- var showCancel = !onEdit && Boolean(onCancel);
1022
- var showNotSaved = hasUnsavedChanges && !onEdit;
1023
- var toolbarShown = showToolbar && !readsWithoutMeta && (showDelete || showEdit || showSave || showCancel || showNotSaved);
1083
+ var indicator = (0, import_docLifecycle.resolveDocIndicator)({
1084
+ docstatus,
1085
+ isCreate,
1086
+ isDirty: hasUnsavedChanges && !onEdit,
1087
+ isSubmittable
1088
+ });
1089
+ var docAction = (0, import_docLifecycle.resolveDocAction)({
1090
+ docstatus,
1091
+ isCreate,
1092
+ isDirty: hasUnsavedChanges,
1093
+ isSubmittable,
1094
+ perms: {
1095
+ write: canWriteRecord,
1096
+ submit: rights.submit,
1097
+ cancel: rights.cancel,
1098
+ amend: rights.amend
1099
+ },
1100
+ isReadOnlyView: Boolean(onEdit)
1101
+ });
1102
+ var showDelete = Boolean(name && onDelete && !wasDeleted && rights.delete && (0, import_docLifecycle.canDeleteDoc)(docstatus));
1103
+ var showEdit = Boolean(onEdit && rights.write && (0, import_docLifecycle.canEditDoc)(docstatus, meta));
1104
+ var showAmend = Boolean(onAmend && (0, import_docLifecycle.isAmendable)(meta));
1105
+ var showClose = !onEdit && Boolean(onCancel);
1106
+ var toolbarShown = showToolbar && !readsWithoutMeta && Boolean(indicator || docAction || showDelete || showEdit || showClose);
1024
1107
  var shownTabs = formTabs !== null && formTabs !== void 0 ? formTabs : recordTabs;
1025
1108
  var imageFieldName = meta === null || meta === void 0 ? void 0 : meta.image_field;
1026
1109
  var sidebarImageUrl = imageFieldName ? resolveFileUrl(localDoc[imageFieldName], finalBaseURL) : void 0;
@@ -1176,52 +1259,20 @@ function FrappeFormBody(param) {
1176
1259
  color: import_forms.formCommonColors.error,
1177
1260
  ...knobProps.body,
1178
1261
  children: errors.general
1179
- }), toolbarShown && /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_tamagui.XStack, {
1180
- ...knobProps.gap,
1181
- justifyContent: "flex-end",
1182
- alignItems: "center",
1183
- paddingTop: "$3",
1184
- children: [showNotSaved && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_tamagui.Text, {
1185
- color: "$orange11",
1186
- marginInlineEnd: "auto",
1187
- ...knobProps.body,
1188
- children: t("Not Saved")
1189
- }), showDelete && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
1190
- outlined: true,
1191
- error: true,
1192
- icon: import_react2.TrashIcon,
1193
- loading: isSubmitting,
1194
- disabled: effectiveDisabled,
1195
- disabledReason: t("Form is read-only"),
1196
- disabledReasonPlacement: "inline",
1197
- onPress: handleDelete,
1198
- children: t("Delete")
1199
- }), showEdit && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
1200
- accent: true,
1201
- icon: import_react2.PencilSimpleIcon,
1202
- disabled: effectiveDisabled,
1203
- disabledReason: t("Form is read-only"),
1204
- disabledReasonPlacement: "inline",
1205
- onPress: onEdit,
1206
- children: t("Edit")
1207
- }), showCancel && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
1208
- outlined: true,
1209
- icon: import_react2.XIcon,
1210
- disabled,
1211
- disabledReason: t("Form is read-only"),
1212
- disabledReasonPlacement: "inline",
1213
- onPress: handleCancel,
1214
- children: t("Cancel")
1215
- }), showSave && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_forms.Button, {
1216
- accent: true,
1217
- icon: import_react2.FloppyDiskIcon,
1218
- loading: isSubmitting,
1219
- disabled: effectiveDisabled,
1220
- disabledReason: t("Form is read-only"),
1221
- disabledReasonPlacement: "inline",
1222
- onPress: handleSave,
1223
- children: isSubmitting ? t("Saving...") : t("Save")
1224
- })]
1262
+ }), toolbarShown && /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_FrappeFormActions.FrappeFormActions, {
1263
+ indicator,
1264
+ action: docAction,
1265
+ busy: isSubmitting,
1266
+ disabled: effectiveDisabled,
1267
+ onSave: handleSave,
1268
+ onSubmit: handleSubmitDocument,
1269
+ onCancelDocument: handleCancelDocument,
1270
+ onAmend: showAmend ? handleAmend : void 0,
1271
+ amendedAs,
1272
+ onDelete: showDelete ? handleDelete : void 0,
1273
+ onEdit: showEdit ? onEdit : void 0,
1274
+ onClose: showClose ? handleCancel : void 0,
1275
+ closeDisabled: disabled
1225
1276
  })]
1226
1277
  })]
1227
1278
  }), /* Timeline (edit mode only) — live Comment/Version via useFrappeCollection */