ding-react-admin 1.0.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -10
- package/dist/index.js +1071 -803
- package/dist/src/crud/InlineFormSet.d.ts +2 -17
- package/dist/src/crud/InlineFormSet.d.ts.map +1 -1
- package/dist/src/crud/ResourceForm.d.ts +4 -9
- package/dist/src/crud/ResourceForm.d.ts.map +1 -1
- package/dist/src/crud/ResourceList.d.ts.map +1 -1
- package/dist/src/crud/columns/ImageColumn.d.ts +14 -0
- package/dist/src/crud/columns/ImageColumn.d.ts.map +1 -0
- package/dist/src/crud/context/InlineFieldsRegistry.d.ts +15 -0
- package/dist/src/crud/context/InlineFieldsRegistry.d.ts.map +1 -0
- package/dist/src/crud/context/PayloadFieldsContext.d.ts +18 -0
- package/dist/src/crud/context/PayloadFieldsContext.d.ts.map +1 -0
- package/dist/src/crud/fields/FileField.d.ts +12 -0
- package/dist/src/crud/fields/FileField.d.ts.map +1 -0
- package/dist/src/crud/fields/ImageField.d.ts +14 -0
- package/dist/src/crud/fields/ImageField.d.ts.map +1 -0
- package/dist/src/crud/fields/uploadFieldUtils.d.ts +5 -0
- package/dist/src/crud/fields/uploadFieldUtils.d.ts.map +1 -0
- package/dist/src/crud/fields/useUploadPreviewUrl.d.ts +4 -0
- package/dist/src/crud/fields/useUploadPreviewUrl.d.ts.map +1 -0
- package/dist/src/crud/index.d.ts +22 -9
- package/dist/src/crud/index.d.ts.map +1 -1
- package/dist/src/crud/types.d.ts +9 -7
- package/dist/src/crud/types.d.ts.map +1 -1
- package/dist/src/crud/utils/buildFormPayload.d.ts +6 -0
- package/dist/src/crud/utils/buildFormPayload.d.ts.map +1 -0
- package/dist/src/crud/utils/buildInlineRowsPayload.d.ts +6 -0
- package/dist/src/crud/utils/buildInlineRowsPayload.d.ts.map +1 -0
- package/dist/src/crud/utils/buildResourceFormSubmitBody.d.ts +5 -0
- package/dist/src/crud/utils/buildResourceFormSubmitBody.d.ts.map +1 -0
- package/dist/src/crud/utils/formErrors.d.ts +1 -1
- package/dist/src/crud/utils/formErrors.d.ts.map +1 -1
- package/dist/src/crud/utils/getFormValue.d.ts +3 -0
- package/dist/src/crud/utils/getFormValue.d.ts.map +1 -0
- package/dist/src/crud/utils/hasUploadValues.d.ts +3 -0
- package/dist/src/crud/utils/hasUploadValues.d.ts.map +1 -0
- package/dist/src/crud/utils/nestedFieldPath.d.ts +3 -0
- package/dist/src/crud/utils/nestedFieldPath.d.ts.map +1 -0
- package/dist/src/crud/utils/prepareFormSubmitBody.d.ts +7 -0
- package/dist/src/crud/utils/prepareFormSubmitBody.d.ts.map +1 -0
- package/dist/src/crud/utils/setFormValue.d.ts +3 -0
- package/dist/src/crud/utils/setFormValue.d.ts.map +1 -0
- package/dist/src/crud/utils/toFormData.d.ts +13 -0
- package/dist/src/crud/utils/toFormData.d.ts.map +1 -0
- package/dist/src/crud/utils/useFormRecord.d.ts +42 -0
- package/dist/src/crud/utils/useFormRecord.d.ts.map +1 -0
- package/dist/src/data/createRestResourceHandlers.d.ts +5 -5
- package/dist/src/data/createRestResourceHandlers.d.ts.map +1 -1
- package/dist/src/data/dataProviderTypes.d.ts +6 -5
- package/dist/src/data/dataProviderTypes.d.ts.map +1 -1
- package/dist/src/data/parseFormErrorHelpers.d.ts +44 -6
- package/dist/src/data/parseFormErrorHelpers.d.ts.map +1 -1
- package/dist/src/data/resourceHandlers.d.ts +1 -1
- package/dist/src/data/resourceHandlers.d.ts.map +1 -1
- package/dist/src/index.d.ts +4 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/layouts/AdminLayout.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/crud/InlineFormSet.tsx +28 -85
- package/src/crud/ResourceForm.tsx +60 -66
- package/src/crud/ResourceFormModal.tsx +25 -25
- package/src/crud/ResourceList.tsx +1 -0
- package/src/crud/columns/ImageColumn.tsx +58 -0
- package/src/crud/columns/TextColumn.tsx +2 -2
- package/src/crud/context/InlineFieldsRegistry.tsx +61 -0
- package/src/crud/context/PayloadFieldsContext.tsx +62 -0
- package/src/crud/fields/FieldWrapper.tsx +87 -87
- package/src/crud/fields/FileField.tsx +123 -0
- package/src/crud/fields/ImageField.tsx +120 -0
- package/src/crud/fields/uploadFieldUtils.ts +21 -0
- package/src/crud/fields/useUploadPreviewUrl.ts +20 -0
- package/src/crud/index.ts +23 -19
- package/src/crud/types.ts +9 -7
- package/src/crud/utils/buildFormPayload.ts +24 -0
- package/src/crud/utils/buildInlineRowsPayload.ts +37 -0
- package/src/crud/utils/buildResourceFormSubmitBody.ts +29 -0
- package/src/crud/utils/formErrors.ts +42 -38
- package/src/crud/utils/{getByPath.ts → getFormValue.ts} +2 -2
- package/src/crud/utils/hasUploadValues.ts +9 -0
- package/src/crud/utils/nestedFieldPath.ts +8 -0
- package/src/crud/utils/prepareFormSubmitBody.ts +17 -0
- package/src/crud/utils/{setByPath.ts → setFormValue.ts} +2 -2
- package/src/crud/utils/toFormData.ts +81 -0
- package/src/crud/utils/useFormRecord.ts +156 -0
- package/src/data/createRestResourceHandlers.ts +19 -13
- package/src/data/dataProviderTypes.ts +7 -5
- package/src/data/dataUtils.test.ts +118 -6
- package/src/data/parseFormErrorHelpers.test.ts +138 -84
- package/src/data/parseFormErrorHelpers.ts +311 -224
- package/src/data/resourceHandlers.ts +1 -1
- package/src/index.ts +23 -11
- package/src/layouts/AdminLayout.tsx +2 -1
- package/dist/src/crud/context/SubmitFieldsContext.d.ts +0 -18
- package/dist/src/crud/context/SubmitFieldsContext.d.ts.map +0 -1
- package/dist/src/crud/utils/getByPath.d.ts +0 -3
- package/dist/src/crud/utils/getByPath.d.ts.map +0 -1
- package/dist/src/crud/utils/inlineArrayName.d.ts +0 -3
- package/dist/src/crud/utils/inlineArrayName.d.ts.map +0 -1
- package/dist/src/crud/utils/inlineFieldName.d.ts +0 -3
- package/dist/src/crud/utils/inlineFieldName.d.ts.map +0 -1
- package/dist/src/crud/utils/pickBySources.d.ts +0 -6
- package/dist/src/crud/utils/pickBySources.d.ts.map +0 -1
- package/dist/src/crud/utils/setByPath.d.ts +0 -3
- package/dist/src/crud/utils/setByPath.d.ts.map +0 -1
- package/dist/src/crud/utils/useResourceFormData.d.ts +0 -47
- package/dist/src/crud/utils/useResourceFormData.d.ts.map +0 -1
- package/src/crud/context/SubmitFieldsContext.tsx +0 -62
- package/src/crud/utils/inlineArrayName.ts +0 -4
- package/src/crud/utils/inlineFieldName.ts +0 -8
- package/src/crud/utils/pickBySources.ts +0 -24
- package/src/crud/utils/useResourceFormData.ts +0 -231
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { MutableRefObject, ReactNode } from 'react';
|
|
2
|
-
/** Tracks which top-level field sources are included in the save payload. */
|
|
3
|
-
export declare function SubmitFieldsProvider({ children, fieldsRef, }: {
|
|
4
|
-
children: ReactNode;
|
|
5
|
-
fieldsRef: MutableRefObject<Set<string>>;
|
|
6
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
7
|
-
export declare function useSubmitFieldsOptional(): MutableRefObject<Set<string>> | null;
|
|
8
|
-
/**
|
|
9
|
-
* Remember a field's `source` for submit (`pickBySources`).
|
|
10
|
-
* Top-level fields only — skip for inline row cells (nested `name` paths).
|
|
11
|
-
*/
|
|
12
|
-
export declare function useSubmitField(source: string, enabled?: boolean): void;
|
|
13
|
-
/**
|
|
14
|
-
* Remember a field's `source` for tab/step error highlighting.
|
|
15
|
-
* Top-level fields only — no-op outside FormTabs / FormSteps.
|
|
16
|
-
*/
|
|
17
|
-
export declare function useSectionField(source: string, enabled?: boolean): void;
|
|
18
|
-
//# sourceMappingURL=SubmitFieldsContext.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SubmitFieldsContext.d.ts","sourceRoot":"","sources":["../../../../src/crud/context/SubmitFieldsContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAMf,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,EAAE,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CAC1C,2CAMA;AAED,wBAAgB,uBAAuB,yCAEtC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,UAAO,QAU5D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,UAAO,QAU7D"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getByPath.d.ts","sourceRoot":"","sources":["../../../../src/crud/utils/getByPath.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,GACX,OAAO,CAQT"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"inlineArrayName.d.ts","sourceRoot":"","sources":["../../../../src/crud/utils/inlineArrayName.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,UAE9D"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"inlineFieldName.d.ts","sourceRoot":"","sources":["../../../../src/crud/utils/inlineFieldName.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,UAGf"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build a submit payload from form values using declared field `source` paths.
|
|
3
|
-
* Flat `username` → `{ username }`; nested `invoiceLine.product` → `{ invoiceLine: { product } }`.
|
|
4
|
-
*/
|
|
5
|
-
export declare function pickBySources(values: Record<string, unknown>, sources: string[]): Record<string, unknown>;
|
|
6
|
-
//# sourceMappingURL=pickBySources.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pickBySources.d.ts","sourceRoot":"","sources":["../../../../src/crud/utils/pickBySources.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE,MAAM,EAAE,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"setByPath.d.ts","sourceRoot":"","sources":["../../../../src/crud/utils/setByPath.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,IAAI,CAYN"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { MessageInstance } from 'antd/es/message/interface';
|
|
2
|
-
import { MutableRefObject } from 'react';
|
|
3
|
-
import { NavigateFunction } from 'react-router-dom';
|
|
4
|
-
import { DataProvider } from '../../data/dataProviderTypes';
|
|
5
|
-
import { FieldValues, UseFormReturn } from 'react-hook-form';
|
|
6
|
-
import { InlineFormSetProps } from '../InlineFormSet';
|
|
7
|
-
export type ResourceFormInlineConfig = Pick<InlineFormSetProps, "resource" | "foreignKey" | "name">;
|
|
8
|
-
export declare function resolveInlineArrayName(config: ResourceFormInlineConfig): string;
|
|
9
|
-
type LoadOptions<T extends FieldValues> = {
|
|
10
|
-
dp: DataProvider;
|
|
11
|
-
resource: string;
|
|
12
|
-
id: string | undefined;
|
|
13
|
-
isNew: boolean;
|
|
14
|
-
form: UseFormReturn<T>;
|
|
15
|
-
message: MessageInstance;
|
|
16
|
-
defaultValues?: Partial<T>;
|
|
17
|
-
inlines?: ResourceFormInlineConfig[];
|
|
18
|
-
};
|
|
19
|
-
/** Edit mode: fetch parent (+ inline rows) → form.reset → bump formVersion for field arrays. */
|
|
20
|
-
export declare function useResourceFormLoad<T extends FieldValues>({ dp, resource, id, isNew, form, message, defaultValues, inlines, }: LoadOptions<T>): {
|
|
21
|
-
loading: boolean;
|
|
22
|
-
formVersion: number;
|
|
23
|
-
existingInlineIds: Record<string, (string | number)[]>;
|
|
24
|
-
};
|
|
25
|
-
type SubmitOptions<T extends FieldValues & {
|
|
26
|
-
id?: unknown;
|
|
27
|
-
}> = {
|
|
28
|
-
dp: DataProvider;
|
|
29
|
-
resource: string;
|
|
30
|
-
id: string | undefined;
|
|
31
|
-
isNew: boolean;
|
|
32
|
-
form: UseFormReturn<T>;
|
|
33
|
-
message: MessageInstance;
|
|
34
|
-
navigate: NavigateFunction;
|
|
35
|
-
listPath: string;
|
|
36
|
-
submitFieldsRef: MutableRefObject<Set<string>>;
|
|
37
|
-
inlines?: ResourceFormInlineConfig[];
|
|
38
|
-
existingInlineIds: Record<string, (string | number)[]>;
|
|
39
|
-
onSaved?: (record: T) => void;
|
|
40
|
-
stayOnPage?: boolean;
|
|
41
|
-
};
|
|
42
|
-
/** Save: pickBySources → create/update parent → saveInlineRows for each inline config. */
|
|
43
|
-
export declare function useResourceFormSubmit<T extends FieldValues & {
|
|
44
|
-
id?: unknown;
|
|
45
|
-
}>({ dp, resource, id, isNew, form, message, navigate, listPath, submitFieldsRef, inlines, existingInlineIds, onSaved, stayOnPage, }: SubmitOptions<T>): (values: T) => Promise<void>;
|
|
46
|
-
export {};
|
|
47
|
-
//# sourceMappingURL=useResourceFormData.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useResourceFormData.d.ts","sourceRoot":"","sources":["../../../../src/crud/utils/useResourceFormData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,kBAAkB,CAAC;AAM1B,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,kBAAkB,EAClB,UAAU,GAAG,YAAY,GAAG,MAAM,CACnC,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,wBAAwB,UAEtE;AAED,KAAK,WAAW,CAAC,CAAC,SAAS,WAAW,IAAI;IACxC,EAAE,EAAE,YAAY,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACvB,OAAO,EAAE,eAAe,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO,CAAC,EAAE,wBAAwB,EAAE,CAAC;CACtC,CAAC;AAEF,gGAAgG;AAChG,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,WAAW,EAAE,EACzD,EAAE,EACF,QAAQ,EACR,EAAE,EACF,KAAK,EACL,IAAI,EACJ,OAAO,EACP,aAAa,EACb,OAAO,GACR,EAAE,WAAW,CAAC,CAAC,CAAC;;;;EAyDhB;AAED,KAAK,aAAa,CAAC,CAAC,SAAS,WAAW,GAAG;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAE,IAAI;IAC7D,EAAE,EAAE,YAAY,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACvB,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,wBAAwB,EAAE,CAAC;IACrC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IACvD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAC;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,0FAA0F;AAC1F,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,WAAW,GAAG;IAAE,EAAE,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,EAC9E,EAAE,EACF,QAAQ,EACR,EAAE,EACF,KAAK,EACL,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,OAAO,EACP,iBAAiB,EACjB,OAAO,EACP,UAAU,GACX,EAAE,aAAa,CAAC,CAAC,CAAC,YAEA,CAAC,mBAoFnB"}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createContext,
|
|
3
|
-
useContext,
|
|
4
|
-
useEffect,
|
|
5
|
-
type MutableRefObject,
|
|
6
|
-
type ReactNode,
|
|
7
|
-
} from "react";
|
|
8
|
-
import { useFormSectionSourcesOptional } from "./FormSectionContext";
|
|
9
|
-
|
|
10
|
-
const SubmitFieldsContext =
|
|
11
|
-
createContext<MutableRefObject<Set<string>> | null>(null);
|
|
12
|
-
|
|
13
|
-
/** Tracks which top-level field sources are included in the save payload. */
|
|
14
|
-
export function SubmitFieldsProvider({
|
|
15
|
-
children,
|
|
16
|
-
fieldsRef,
|
|
17
|
-
}: {
|
|
18
|
-
children: ReactNode;
|
|
19
|
-
fieldsRef: MutableRefObject<Set<string>>;
|
|
20
|
-
}) {
|
|
21
|
-
return (
|
|
22
|
-
<SubmitFieldsContext.Provider value={fieldsRef}>
|
|
23
|
-
{children}
|
|
24
|
-
</SubmitFieldsContext.Provider>
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function useSubmitFieldsOptional() {
|
|
29
|
-
return useContext(SubmitFieldsContext);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Remember a field's `source` for submit (`pickBySources`).
|
|
34
|
-
* Top-level fields only — skip for inline row cells (nested `name` paths).
|
|
35
|
-
*/
|
|
36
|
-
export function useSubmitField(source: string, enabled = true) {
|
|
37
|
-
const fieldsRef = useSubmitFieldsOptional();
|
|
38
|
-
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (!enabled || !fieldsRef) return;
|
|
41
|
-
fieldsRef.current.add(source);
|
|
42
|
-
return () => {
|
|
43
|
-
fieldsRef.current.delete(source);
|
|
44
|
-
};
|
|
45
|
-
}, [fieldsRef, source, enabled]);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Remember a field's `source` for tab/step error highlighting.
|
|
50
|
-
* Top-level fields only — no-op outside FormTabs / FormSteps.
|
|
51
|
-
*/
|
|
52
|
-
export function useSectionField(source: string, enabled = true) {
|
|
53
|
-
const sectionRef = useFormSectionSourcesOptional();
|
|
54
|
-
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
if (!enabled || !sectionRef) return;
|
|
57
|
-
sectionRef.current.add(source);
|
|
58
|
-
return () => {
|
|
59
|
-
sectionRef.current.delete(source);
|
|
60
|
-
};
|
|
61
|
-
}, [sectionRef, source, enabled]);
|
|
62
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { getByPath } from "./getByPath";
|
|
2
|
-
import { setByPath } from "./setByPath";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Build a submit payload from form values using declared field `source` paths.
|
|
6
|
-
* Flat `username` → `{ username }`; nested `invoiceLine.product` → `{ invoiceLine: { product } }`.
|
|
7
|
-
*/
|
|
8
|
-
export function pickBySources(
|
|
9
|
-
values: Record<string, unknown>,
|
|
10
|
-
sources: string[],
|
|
11
|
-
): Record<string, unknown> {
|
|
12
|
-
if (sources.length === 0) {
|
|
13
|
-
return { ...values };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const result: Record<string, unknown> = {};
|
|
17
|
-
for (const source of sources) {
|
|
18
|
-
const value = getByPath(values, source);
|
|
19
|
-
if (value !== undefined) {
|
|
20
|
-
setByPath(result, source, value);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return result;
|
|
24
|
-
}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import type { MessageInstance } from "antd/es/message/interface";
|
|
2
|
-
import {
|
|
3
|
-
useCallback,
|
|
4
|
-
useState,
|
|
5
|
-
type MutableRefObject,
|
|
6
|
-
} from "react";
|
|
7
|
-
import type { NavigateFunction } from "react-router-dom";
|
|
8
|
-
import type { DataProvider } from "../../data/dataProviderTypes";
|
|
9
|
-
import { isAbortError } from "../../data/abortError";
|
|
10
|
-
import {
|
|
11
|
-
type DefaultValues,
|
|
12
|
-
type FieldValues,
|
|
13
|
-
type UseFormReturn,
|
|
14
|
-
} from "react-hook-form";
|
|
15
|
-
import {
|
|
16
|
-
loadInlineRows,
|
|
17
|
-
saveInlineRows,
|
|
18
|
-
type InlineFormSetProps,
|
|
19
|
-
} from "../InlineFormSet";
|
|
20
|
-
import { inlineArrayName } from "./inlineArrayName";
|
|
21
|
-
import { pickBySources } from "./pickBySources";
|
|
22
|
-
import { parseAndApplyFormErrors } from "./formErrors";
|
|
23
|
-
import { useAbortableEffect } from "./useAbortableEffect";
|
|
24
|
-
|
|
25
|
-
export type ResourceFormInlineConfig = Pick<
|
|
26
|
-
InlineFormSetProps,
|
|
27
|
-
"resource" | "foreignKey" | "name"
|
|
28
|
-
>;
|
|
29
|
-
|
|
30
|
-
export function resolveInlineArrayName(config: ResourceFormInlineConfig) {
|
|
31
|
-
return inlineArrayName(config.resource, config.name);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
type LoadOptions<T extends FieldValues> = {
|
|
35
|
-
dp: DataProvider;
|
|
36
|
-
resource: string;
|
|
37
|
-
id: string | undefined;
|
|
38
|
-
isNew: boolean;
|
|
39
|
-
form: UseFormReturn<T>;
|
|
40
|
-
message: MessageInstance;
|
|
41
|
-
defaultValues?: Partial<T>;
|
|
42
|
-
inlines?: ResourceFormInlineConfig[];
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/** Edit mode: fetch parent (+ inline rows) → form.reset → bump formVersion for field arrays. */
|
|
46
|
-
export function useResourceFormLoad<T extends FieldValues>({
|
|
47
|
-
dp,
|
|
48
|
-
resource,
|
|
49
|
-
id,
|
|
50
|
-
isNew,
|
|
51
|
-
form,
|
|
52
|
-
message,
|
|
53
|
-
defaultValues,
|
|
54
|
-
inlines,
|
|
55
|
-
}: LoadOptions<T>) {
|
|
56
|
-
const [loading, setLoading] = useState(!isNew);
|
|
57
|
-
const [formVersion, setFormVersion] = useState(0);
|
|
58
|
-
const [existingInlineIds, setExistingInlineIds] = useState<
|
|
59
|
-
Record<string, (string | number)[]>
|
|
60
|
-
>({});
|
|
61
|
-
|
|
62
|
-
const load = useCallback(
|
|
63
|
-
async (signal?: AbortSignal) => {
|
|
64
|
-
if (isNew || !id) {
|
|
65
|
-
if (defaultValues) {
|
|
66
|
-
form.reset({ ...defaultValues } as DefaultValues<T>);
|
|
67
|
-
}
|
|
68
|
-
setLoading(false);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
setLoading(true);
|
|
72
|
-
try {
|
|
73
|
-
const res = await dp.getOne(resource, id, { signal });
|
|
74
|
-
if (signal?.aborted) return;
|
|
75
|
-
const record = res.data as DefaultValues<T>;
|
|
76
|
-
if (inlines?.length) {
|
|
77
|
-
const merged = { ...record } as DefaultValues<T>;
|
|
78
|
-
const idsMap: Record<string, (string | number)[]> = {};
|
|
79
|
-
for (const cfg of inlines) {
|
|
80
|
-
const arrayName = resolveInlineArrayName(cfg);
|
|
81
|
-
const { rows, ids } = await loadInlineRows(
|
|
82
|
-
dp,
|
|
83
|
-
cfg.resource,
|
|
84
|
-
cfg.foreignKey,
|
|
85
|
-
id,
|
|
86
|
-
);
|
|
87
|
-
(merged as Record<string, unknown>)[arrayName] = rows;
|
|
88
|
-
idsMap[arrayName] = ids;
|
|
89
|
-
}
|
|
90
|
-
if (signal?.aborted) return;
|
|
91
|
-
form.reset(merged);
|
|
92
|
-
setExistingInlineIds(idsMap);
|
|
93
|
-
setFormVersion((v) => v + 1);
|
|
94
|
-
} else {
|
|
95
|
-
form.reset(record);
|
|
96
|
-
setFormVersion((v) => v + 1);
|
|
97
|
-
}
|
|
98
|
-
} catch (e) {
|
|
99
|
-
if (!isAbortError(e)) {
|
|
100
|
-
message.error(e instanceof Error ? e.message : "Load failed");
|
|
101
|
-
}
|
|
102
|
-
} finally {
|
|
103
|
-
if (!signal?.aborted) setLoading(false);
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
[dp, resource, id, isNew, form, message, defaultValues, inlines],
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
useAbortableEffect((signal) => load(signal), [load]);
|
|
110
|
-
|
|
111
|
-
return { loading, formVersion, existingInlineIds };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
type SubmitOptions<T extends FieldValues & { id?: unknown }> = {
|
|
115
|
-
dp: DataProvider;
|
|
116
|
-
resource: string;
|
|
117
|
-
id: string | undefined;
|
|
118
|
-
isNew: boolean;
|
|
119
|
-
form: UseFormReturn<T>;
|
|
120
|
-
message: MessageInstance;
|
|
121
|
-
navigate: NavigateFunction;
|
|
122
|
-
listPath: string;
|
|
123
|
-
submitFieldsRef: MutableRefObject<Set<string>>;
|
|
124
|
-
inlines?: ResourceFormInlineConfig[];
|
|
125
|
-
existingInlineIds: Record<string, (string | number)[]>;
|
|
126
|
-
onSaved?: (record: T) => void;
|
|
127
|
-
stayOnPage?: boolean;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
/** Save: pickBySources → create/update parent → saveInlineRows for each inline config. */
|
|
131
|
-
export function useResourceFormSubmit<T extends FieldValues & { id?: unknown }>({
|
|
132
|
-
dp,
|
|
133
|
-
resource,
|
|
134
|
-
id,
|
|
135
|
-
isNew,
|
|
136
|
-
form,
|
|
137
|
-
message,
|
|
138
|
-
navigate,
|
|
139
|
-
listPath,
|
|
140
|
-
submitFieldsRef,
|
|
141
|
-
inlines,
|
|
142
|
-
existingInlineIds,
|
|
143
|
-
onSaved,
|
|
144
|
-
stayOnPage,
|
|
145
|
-
}: SubmitOptions<T>) {
|
|
146
|
-
return useCallback(
|
|
147
|
-
async (values: T) => {
|
|
148
|
-
try {
|
|
149
|
-
const raw = values as Record<string, unknown>;
|
|
150
|
-
let payload = pickBySources(raw, Array.from(submitFieldsRef.current));
|
|
151
|
-
if (inlines?.length) {
|
|
152
|
-
for (const cfg of inlines) {
|
|
153
|
-
delete payload[resolveInlineArrayName(cfg)];
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
let saved: T;
|
|
158
|
-
if (isNew) {
|
|
159
|
-
const res = await dp.create(resource, payload);
|
|
160
|
-
saved = res.data as T;
|
|
161
|
-
message.success("Created");
|
|
162
|
-
} else if (id) {
|
|
163
|
-
const res = await dp.update(resource, { id, data: payload });
|
|
164
|
-
saved = res.data as T;
|
|
165
|
-
message.success("Updated");
|
|
166
|
-
} else {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const parentId = saved.id as string | number;
|
|
171
|
-
if (inlines?.length && parentId != null) {
|
|
172
|
-
for (const cfg of inlines) {
|
|
173
|
-
const arrayName = resolveInlineArrayName(cfg);
|
|
174
|
-
const rows =
|
|
175
|
-
((form.getValues(arrayName as never) as unknown) as
|
|
176
|
-
| Record<string, unknown>[]
|
|
177
|
-
| undefined) ?? [];
|
|
178
|
-
const inlineOk = await saveInlineRows(dp, {
|
|
179
|
-
resource: cfg.resource,
|
|
180
|
-
foreignKey: cfg.foreignKey,
|
|
181
|
-
parentId,
|
|
182
|
-
rows,
|
|
183
|
-
existingIds: existingInlineIds[arrayName] ?? [],
|
|
184
|
-
onRowError: (e, index) => {
|
|
185
|
-
const row = rows[index];
|
|
186
|
-
const rowId = row?.id as string | number | undefined;
|
|
187
|
-
return parseAndApplyFormErrors(dp, form, message, e, {
|
|
188
|
-
resource: cfg.resource,
|
|
189
|
-
mutation:
|
|
190
|
-
rowId != null &&
|
|
191
|
-
(existingInlineIds[arrayName] ?? []).some(
|
|
192
|
-
(existingId) => existingId === rowId,
|
|
193
|
-
)
|
|
194
|
-
? "update"
|
|
195
|
-
: "create",
|
|
196
|
-
inlineArrayName: arrayName,
|
|
197
|
-
rowIndex: index,
|
|
198
|
-
});
|
|
199
|
-
},
|
|
200
|
-
});
|
|
201
|
-
if (!inlineOk) return;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
onSaved?.(saved);
|
|
206
|
-
if (!stayOnPage) navigate(listPath);
|
|
207
|
-
} catch (e) {
|
|
208
|
-
parseAndApplyFormErrors(dp, form, message, e, {
|
|
209
|
-
resource,
|
|
210
|
-
mutation: isNew ? "create" : "update",
|
|
211
|
-
});
|
|
212
|
-
message.error(e instanceof Error ? e.message : "Save failed");
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
[
|
|
216
|
-
dp,
|
|
217
|
-
resource,
|
|
218
|
-
id,
|
|
219
|
-
isNew,
|
|
220
|
-
form,
|
|
221
|
-
message,
|
|
222
|
-
navigate,
|
|
223
|
-
listPath,
|
|
224
|
-
submitFieldsRef,
|
|
225
|
-
inlines,
|
|
226
|
-
existingInlineIds,
|
|
227
|
-
onSaved,
|
|
228
|
-
stayOnPage,
|
|
229
|
-
],
|
|
230
|
-
);
|
|
231
|
-
}
|