@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.
- package/dist/cjs/FrappeCalendar.cjs +3 -2
- package/dist/cjs/FrappeCalendar.native.js +3 -2
- package/dist/cjs/FrappeCalendar.native.js.map +1 -1
- package/dist/cjs/FrappeForm.cjs +148 -464
- package/dist/cjs/FrappeForm.native.js +152 -633
- package/dist/cjs/FrappeForm.native.js.map +1 -1
- package/dist/cjs/FrappeListView.cjs +5 -27
- package/dist/cjs/FrappeListView.native.js +11 -32
- package/dist/cjs/FrappeListView.native.js.map +1 -1
- package/dist/cjs/FrappeReportBuilder.cjs +523 -309
- package/dist/cjs/FrappeReportBuilder.native.js +658 -353
- package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
- package/dist/cjs/FrappeReportView.cjs +35 -234
- package/dist/cjs/FrappeReportView.native.js +39 -283
- package/dist/cjs/FrappeReportView.native.js.map +1 -1
- package/dist/cjs/dependsOn.cjs +2 -2
- package/dist/cjs/dependsOn.native.js +1 -2
- package/dist/cjs/dependsOn.native.js.map +1 -1
- package/dist/cjs/fields/shared/EditorPreviewPanes.cjs +2 -2
- package/dist/cjs/fields/shared/EditorPreviewPanes.native.js +2 -2
- package/dist/cjs/fields/shared/EditorPreviewPanes.native.js.map +1 -1
- package/dist/cjs/formEngine/docPermissions.cjs +20 -0
- package/dist/cjs/formEngine/docPermissions.native.js +69 -0
- package/dist/cjs/formEngine/docPermissions.native.js.map +1 -1
- package/dist/cjs/formEngine/index.cjs +2 -0
- package/dist/cjs/formEngine/index.native.js +2 -0
- package/dist/cjs/formEngine/index.native.js.map +1 -1
- package/dist/cjs/index.cjs +67 -7
- package/dist/cjs/index.native.js +67 -7
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/liveErrors.cjs +8 -2
- package/dist/cjs/liveErrors.native.js +8 -2
- package/dist/cjs/liveErrors.native.js.map +1 -1
- package/dist/cjs/measures.cjs +31 -0
- package/dist/cjs/measures.native.js +34 -0
- package/dist/cjs/measures.native.js.map +1 -0
- package/dist/cjs/recordShape.cjs +103 -0
- package/dist/cjs/recordShape.native.js +115 -0
- package/dist/cjs/recordShape.native.js.map +1 -0
- package/dist/cjs/report/ReportGrid.cjs +453 -0
- package/dist/cjs/report/ReportGrid.native.js +510 -0
- package/dist/cjs/report/ReportGrid.native.js.map +1 -0
- package/dist/cjs/report/ReportViewToolbar.cjs +253 -0
- package/dist/cjs/report/ReportViewToolbar.native.js +303 -0
- package/dist/cjs/report/ReportViewToolbar.native.js.map +1 -0
- package/dist/cjs/report/queryReport.cjs +444 -0
- package/dist/cjs/report/queryReport.native.js +588 -0
- package/dist/cjs/report/queryReport.native.js.map +1 -0
- package/dist/cjs/report/queryReportColumns.cjs +131 -0
- package/dist/cjs/report/queryReportColumns.native.js +159 -0
- package/dist/cjs/report/queryReportColumns.native.js.map +1 -0
- package/dist/cjs/report/reportViewModel.cjs +386 -0
- package/dist/cjs/report/reportViewModel.native.js +657 -0
- package/dist/cjs/report/reportViewModel.native.js.map +1 -0
- package/dist/cjs/report/savedReports.cjs +137 -0
- package/dist/cjs/report/savedReports.native.js +169 -0
- package/dist/cjs/report/savedReports.native.js.map +1 -0
- package/dist/cjs/report/useFrappeReport.cjs +184 -0
- package/dist/cjs/report/useFrappeReport.native.js +214 -0
- package/dist/cjs/report/useFrappeReport.native.js.map +1 -0
- package/dist/cjs/report/useQueryReport.cjs +201 -0
- package/dist/cjs/report/useQueryReport.native.js +233 -0
- package/dist/cjs/report/useQueryReport.native.js.map +1 -0
- package/dist/cjs/useFrappeForm.cjs +558 -0
- package/dist/cjs/useFrappeForm.native.js +714 -0
- package/dist/cjs/useFrappeForm.native.js.map +1 -0
- package/dist/esm/FrappeCalendar.mjs +4 -3
- package/dist/esm/FrappeCalendar.mjs.map +1 -1
- package/dist/esm/FrappeCalendar.native.js +4 -3
- package/dist/esm/FrappeCalendar.native.js.map +1 -1
- package/dist/esm/FrappeForm.mjs +151 -467
- package/dist/esm/FrappeForm.mjs.map +1 -1
- package/dist/esm/FrappeForm.native.js +155 -636
- package/dist/esm/FrappeForm.native.js.map +1 -1
- package/dist/esm/FrappeListView.mjs +6 -28
- package/dist/esm/FrappeListView.mjs.map +1 -1
- package/dist/esm/FrappeListView.native.js +12 -33
- package/dist/esm/FrappeListView.native.js.map +1 -1
- package/dist/esm/FrappeReportBuilder.mjs +526 -312
- package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
- package/dist/esm/FrappeReportBuilder.native.js +661 -356
- package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
- package/dist/esm/FrappeReportView.mjs +30 -229
- package/dist/esm/FrappeReportView.mjs.map +1 -1
- package/dist/esm/FrappeReportView.native.js +34 -278
- package/dist/esm/FrappeReportView.native.js.map +1 -1
- package/dist/esm/dependsOn.mjs +2 -2
- package/dist/esm/dependsOn.mjs.map +1 -1
- package/dist/esm/dependsOn.native.js +1 -2
- package/dist/esm/dependsOn.native.js.map +1 -1
- package/dist/esm/fields/shared/EditorPreviewPanes.mjs +2 -2
- package/dist/esm/fields/shared/EditorPreviewPanes.mjs.map +1 -1
- package/dist/esm/fields/shared/EditorPreviewPanes.native.js +2 -2
- package/dist/esm/fields/shared/EditorPreviewPanes.native.js.map +1 -1
- package/dist/esm/formEngine/docPermissions.mjs +19 -1
- package/dist/esm/formEngine/docPermissions.mjs.map +1 -1
- package/dist/esm/formEngine/docPermissions.native.js +68 -1
- package/dist/esm/formEngine/docPermissions.native.js.map +1 -1
- package/dist/esm/formEngine/index.mjs +2 -2
- package/dist/esm/formEngine/index.mjs.map +1 -1
- package/dist/esm/formEngine/index.native.js +2 -2
- package/dist/esm/formEngine/index.native.js.map +1 -1
- package/dist/esm/index.mjs +16 -4
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +16 -4
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/liveErrors.mjs +8 -3
- package/dist/esm/liveErrors.mjs.map +1 -1
- package/dist/esm/liveErrors.native.js +8 -3
- package/dist/esm/liveErrors.native.js.map +1 -1
- package/dist/esm/measures.mjs +5 -0
- package/dist/esm/measures.mjs.map +1 -0
- package/dist/esm/measures.native.js +5 -0
- package/dist/esm/measures.native.js.map +1 -0
- package/dist/esm/recordShape.mjs +74 -0
- package/dist/esm/recordShape.mjs.map +1 -0
- package/dist/esm/recordShape.native.js +83 -0
- package/dist/esm/recordShape.native.js.map +1 -0
- package/dist/esm/report/ReportGrid.mjs +428 -0
- package/dist/esm/report/ReportGrid.mjs.map +1 -0
- package/dist/esm/report/ReportGrid.native.js +471 -0
- package/dist/esm/report/ReportGrid.native.js.map +1 -0
- package/dist/esm/report/ReportViewToolbar.mjs +228 -0
- package/dist/esm/report/ReportViewToolbar.mjs.map +1 -0
- package/dist/esm/report/ReportViewToolbar.native.js +264 -0
- package/dist/esm/report/ReportViewToolbar.native.js.map +1 -0
- package/dist/esm/report/queryReport.mjs +404 -0
- package/dist/esm/report/queryReport.mjs.map +1 -0
- package/dist/esm/report/queryReport.native.js +545 -0
- package/dist/esm/report/queryReport.native.js.map +1 -0
- package/dist/esm/report/queryReportColumns.mjs +105 -0
- package/dist/esm/report/queryReportColumns.mjs.map +1 -0
- package/dist/esm/report/queryReportColumns.native.js +119 -0
- package/dist/esm/report/queryReportColumns.native.js.map +1 -0
- package/dist/esm/report/reportViewModel.mjs +343 -0
- package/dist/esm/report/reportViewModel.mjs.map +1 -0
- package/dist/esm/report/reportViewModel.native.js +611 -0
- package/dist/esm/report/reportViewModel.native.js.map +1 -0
- package/dist/esm/report/savedReports.mjs +112 -0
- package/dist/esm/report/savedReports.mjs.map +1 -0
- package/dist/esm/report/savedReports.native.js +141 -0
- package/dist/esm/report/savedReports.native.js.map +1 -0
- package/dist/esm/report/useFrappeReport.mjs +158 -0
- package/dist/esm/report/useFrappeReport.mjs.map +1 -0
- package/dist/esm/report/useFrappeReport.native.js +185 -0
- package/dist/esm/report/useFrappeReport.native.js.map +1 -0
- package/dist/esm/report/useQueryReport.mjs +176 -0
- package/dist/esm/report/useQueryReport.mjs.map +1 -0
- package/dist/esm/report/useQueryReport.native.js +205 -0
- package/dist/esm/report/useQueryReport.native.js.map +1 -0
- package/dist/esm/useFrappeForm.mjs +533 -0
- package/dist/esm/useFrappeForm.mjs.map +1 -0
- package/dist/esm/useFrappeForm.native.js +686 -0
- package/dist/esm/useFrappeForm.native.js.map +1 -0
- package/dist/jsx/FrappeCalendar.mjs +4 -3
- package/dist/jsx/FrappeCalendar.mjs.map +1 -1
- package/dist/jsx/FrappeCalendar.native.js +3 -2
- package/dist/jsx/FrappeCalendar.native.js.map +1 -1
- package/dist/jsx/FrappeForm.mjs +151 -467
- package/dist/jsx/FrappeForm.mjs.map +1 -1
- package/dist/jsx/FrappeForm.native.js +152 -633
- package/dist/jsx/FrappeForm.native.js.map +1 -1
- package/dist/jsx/FrappeListView.mjs +6 -28
- package/dist/jsx/FrappeListView.mjs.map +1 -1
- package/dist/jsx/FrappeListView.native.js +11 -32
- package/dist/jsx/FrappeListView.native.js.map +1 -1
- package/dist/jsx/FrappeReportBuilder.mjs +526 -312
- package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
- package/dist/jsx/FrappeReportBuilder.native.js +658 -353
- package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
- package/dist/jsx/FrappeReportView.mjs +30 -229
- package/dist/jsx/FrappeReportView.mjs.map +1 -1
- package/dist/jsx/FrappeReportView.native.js +39 -283
- package/dist/jsx/FrappeReportView.native.js.map +1 -1
- package/dist/jsx/dependsOn.mjs +2 -2
- package/dist/jsx/dependsOn.mjs.map +1 -1
- package/dist/jsx/dependsOn.native.js +1 -2
- package/dist/jsx/dependsOn.native.js.map +1 -1
- package/dist/jsx/fields/shared/EditorPreviewPanes.mjs +2 -2
- package/dist/jsx/fields/shared/EditorPreviewPanes.mjs.map +1 -1
- package/dist/jsx/fields/shared/EditorPreviewPanes.native.js +2 -2
- package/dist/jsx/fields/shared/EditorPreviewPanes.native.js.map +1 -1
- package/dist/jsx/formEngine/docPermissions.mjs +19 -1
- package/dist/jsx/formEngine/docPermissions.mjs.map +1 -1
- package/dist/jsx/formEngine/docPermissions.native.js +69 -0
- package/dist/jsx/formEngine/docPermissions.native.js.map +1 -1
- package/dist/jsx/formEngine/index.mjs +2 -2
- package/dist/jsx/formEngine/index.mjs.map +1 -1
- package/dist/jsx/formEngine/index.native.js +2 -0
- package/dist/jsx/formEngine/index.native.js.map +1 -1
- package/dist/jsx/index.js +16 -4
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +16 -4
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +67 -7
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/liveErrors.mjs +8 -3
- package/dist/jsx/liveErrors.mjs.map +1 -1
- package/dist/jsx/liveErrors.native.js +8 -2
- package/dist/jsx/liveErrors.native.js.map +1 -1
- package/dist/jsx/measures.mjs +5 -0
- package/dist/jsx/measures.mjs.map +1 -0
- package/dist/jsx/measures.native.js +34 -0
- package/dist/jsx/measures.native.js.map +1 -0
- package/dist/jsx/recordShape.mjs +74 -0
- package/dist/jsx/recordShape.mjs.map +1 -0
- package/dist/jsx/recordShape.native.js +115 -0
- package/dist/jsx/recordShape.native.js.map +1 -0
- package/dist/jsx/report/ReportGrid.mjs +428 -0
- package/dist/jsx/report/ReportGrid.mjs.map +1 -0
- package/dist/jsx/report/ReportGrid.native.js +510 -0
- package/dist/jsx/report/ReportGrid.native.js.map +1 -0
- package/dist/jsx/report/ReportViewToolbar.mjs +228 -0
- package/dist/jsx/report/ReportViewToolbar.mjs.map +1 -0
- package/dist/jsx/report/ReportViewToolbar.native.js +303 -0
- package/dist/jsx/report/ReportViewToolbar.native.js.map +1 -0
- package/dist/jsx/report/queryReport.mjs +404 -0
- package/dist/jsx/report/queryReport.mjs.map +1 -0
- package/dist/jsx/report/queryReport.native.js +588 -0
- package/dist/jsx/report/queryReport.native.js.map +1 -0
- package/dist/jsx/report/queryReportColumns.mjs +105 -0
- package/dist/jsx/report/queryReportColumns.mjs.map +1 -0
- package/dist/jsx/report/queryReportColumns.native.js +159 -0
- package/dist/jsx/report/queryReportColumns.native.js.map +1 -0
- package/dist/jsx/report/reportViewModel.mjs +343 -0
- package/dist/jsx/report/reportViewModel.mjs.map +1 -0
- package/dist/jsx/report/reportViewModel.native.js +657 -0
- package/dist/jsx/report/reportViewModel.native.js.map +1 -0
- package/dist/jsx/report/savedReports.mjs +112 -0
- package/dist/jsx/report/savedReports.mjs.map +1 -0
- package/dist/jsx/report/savedReports.native.js +169 -0
- package/dist/jsx/report/savedReports.native.js.map +1 -0
- package/dist/jsx/report/useFrappeReport.mjs +158 -0
- package/dist/jsx/report/useFrappeReport.mjs.map +1 -0
- package/dist/jsx/report/useFrappeReport.native.js +214 -0
- package/dist/jsx/report/useFrappeReport.native.js.map +1 -0
- package/dist/jsx/report/useQueryReport.mjs +176 -0
- package/dist/jsx/report/useQueryReport.mjs.map +1 -0
- package/dist/jsx/report/useQueryReport.native.js +233 -0
- package/dist/jsx/report/useQueryReport.native.js.map +1 -0
- package/dist/jsx/useFrappeForm.mjs +533 -0
- package/dist/jsx/useFrappeForm.mjs.map +1 -0
- package/dist/jsx/useFrappeForm.native.js +714 -0
- package/dist/jsx/useFrappeForm.native.js.map +1 -0
- package/package.json +16 -16
- package/src/FrappeCalendar.tsx +8 -2
- package/src/FrappeForm.stories.tsx +52 -0
- package/src/FrappeForm.tsx +176 -565
- package/src/FrappeListView.cards.stories.tsx +3 -6
- package/src/FrappeListView.tsx +15 -55
- package/src/FrappeReportBuilder.stories.tsx +34 -7
- package/src/FrappeReportBuilder.tsx +790 -380
- package/src/FrappeReportView.spec.tsx +10 -6
- package/src/FrappeReportView.stories.tsx +20 -1
- package/src/FrappeReportView.tsx +89 -415
- package/src/dependsOn.ts +4 -1
- package/src/fields/shared/EditorPreviewPanes.tsx +2 -2
- package/src/formEngine/docPermissions.spec.ts +36 -1
- package/src/formEngine/docPermissions.ts +34 -0
- package/src/formEngine/index.ts +8 -1
- package/src/frappeRemainder.designLaw.spec.ts +9 -1
- package/src/frappeRoutes.spec.tsx +9 -0
- package/src/frappeViews.designLaw.spec.ts +15 -2
- package/src/guestFallback.spec.tsx +236 -0
- package/src/index.ts +117 -16
- package/src/liveErrors.spec.ts +10 -0
- package/src/liveErrors.ts +16 -3
- package/src/measures.ts +11 -0
- package/src/recordShape.spec.ts +100 -0
- package/src/recordShape.ts +133 -0
- package/src/report/ReportGrid.spec.tsx +175 -0
- package/src/report/ReportGrid.tsx +530 -0
- package/src/report/ReportViewToolbar.spec.tsx +90 -0
- package/src/report/ReportViewToolbar.tsx +314 -0
- package/src/report/queryReport.spec.ts +483 -0
- package/src/report/queryReport.ts +722 -0
- package/src/report/queryReportColumns.tsx +123 -0
- package/src/report/reportViewModel.spec.ts +393 -0
- package/src/report/reportViewModel.ts +617 -0
- package/src/report/savedReports.ts +172 -0
- package/src/report/seam.spec.ts +216 -0
- package/src/report/useFrappeReport.ts +241 -0
- package/src/report/useQueryReport.spec.tsx +131 -0
- package/src/report/useQueryReport.ts +309 -0
- package/src/useFrappeForm.spec.tsx +209 -0
- package/src/useFrappeForm.ts +778 -0
- package/types/FrappeCalendar.d.ts.map +1 -1
- package/types/FrappeForm.d.ts +21 -1
- package/types/FrappeForm.d.ts.map +1 -1
- package/types/FrappeListView.d.ts.map +1 -1
- package/types/FrappeReportBuilder.d.ts +66 -16
- package/types/FrappeReportBuilder.d.ts.map +1 -1
- package/types/FrappeReportView.d.ts +9 -127
- package/types/FrappeReportView.d.ts.map +1 -1
- package/types/dependsOn.d.ts.map +1 -1
- package/types/formEngine/docPermissions.d.ts +14 -0
- package/types/formEngine/docPermissions.d.ts.map +1 -1
- package/types/formEngine/index.d.ts +1 -1
- package/types/formEngine/index.d.ts.map +1 -1
- package/types/index.d.ts +15 -3
- package/types/index.d.ts.map +1 -1
- package/types/liveErrors.d.ts +9 -1
- package/types/liveErrors.d.ts.map +1 -1
- package/types/measures.d.ts +3 -0
- package/types/measures.d.ts.map +1 -0
- package/types/recordShape.d.ts +45 -0
- package/types/recordShape.d.ts.map +1 -0
- package/types/report/ReportGrid.d.ts +20 -0
- package/types/report/ReportGrid.d.ts.map +1 -0
- package/types/report/ReportViewToolbar.d.ts +31 -0
- package/types/report/ReportViewToolbar.d.ts.map +1 -0
- package/types/report/queryReport.d.ts +181 -0
- package/types/report/queryReport.d.ts.map +1 -0
- package/types/report/queryReportColumns.d.ts +12 -0
- package/types/report/queryReportColumns.d.ts.map +1 -0
- package/types/report/reportViewModel.d.ts +146 -0
- package/types/report/reportViewModel.d.ts.map +1 -0
- package/types/report/savedReports.d.ts +27 -0
- package/types/report/savedReports.d.ts.map +1 -0
- package/types/report/useFrappeReport.d.ts +60 -0
- package/types/report/useFrappeReport.d.ts.map +1 -0
- package/types/report/useQueryReport.d.ts +37 -0
- package/types/report/useQueryReport.d.ts.map +1 -0
- package/types/useFrappeForm.d.ts +142 -0
- package/types/useFrappeForm.d.ts.map +1 -0
package/dist/jsx/FrappeForm.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
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 {
|
|
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 {
|
|
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
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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
|
-
|
|
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 (!
|
|
344
|
+
if (!name || wasDeleted || !rights.delete) return;
|
|
648
345
|
if (!(await dialog.confirm(t("Delete this {{doctype}}?", {
|
|
649
346
|
doctype
|
|
650
347
|
})))) return;
|
|
651
|
-
|
|
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
|
|
666
|
-
if (!
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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 (!
|
|
698
|
-
const draft =
|
|
699
|
-
|
|
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__ */
|
|
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
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
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
|
-
|
|
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 &&
|
|
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 &&
|
|
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],
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}] :
|
|
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,
|