Skip to content

Commit 1366fdf

Browse files
authored
Merge pull request #15 from workfloworchestrator/1663-code-cleanup
1663 code cleanup
2 parents 35cd3d5 + 65fd200 commit 1366fdf

File tree

8 files changed

+132
-52
lines changed

8 files changed

+132
-52
lines changed

backend/main.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,17 @@ class SubmitFormPage(FormPage):
3737
def read_root():
3838
return {"Hello": "World"}
3939

40+
4041
@dataclass(frozen=True, **SLOTS)
4142
class ExtraData(GroupedMetadata):
4243
props: dict
4344

4445
def __iter__(self) -> Iterator[BaseMetadata]:
4546
yield Field(json_schema_extra=self.props)
4647

48+
4749
@app.post("/form")
48-
async def form(form_data: list[dict]=[]):
50+
async def form(form_data: list[dict] = []):
4951
def form_generator(state: State):
5052
class TestForm(FormPage):
5153
model_config = ConfigDict(title="Form Title")
@@ -55,12 +57,14 @@ class TestForm(FormPage):
5557

5658
form_data_1 = yield TestForm
5759

58-
# class TestForm2(SubmitFormPage):
59-
# model_config = ConfigDict(title="Form 2 Title")
60-
#
61-
# name_2: str | None = None
62-
# form_data_2 = yield TestForm2
63-
return form_data_1.model_dump() #| form_data_2.model_dump()
60+
class TestForm2(SubmitFormPage):
61+
model_config = ConfigDict(title="Form 2 Title")
62+
63+
name_2: str | None = None
64+
65+
form_data_2 = yield TestForm2
66+
67+
return form_data_1.model_dump() | form_data_2.model_dump()
6468

65-
data = post_form(form_generator, state={}, user_inputs=form_data)
66-
return data
69+
post_form(form_generator, state={}, user_inputs=form_data)
70+
return "OK!"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'pydantic-forms': patch
3+
---
4+
5+
Readds multistep forms

frontend/apps/example/src/app/page.tsx

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
'use client';
22

33
import { PydanticForm } from 'pydantic-forms';
4-
import type { PydanticFormProvider } from 'pydantic-forms';
4+
import type {
5+
PydanticFormApiProvider,
6+
PydanticFormCustomDataProvider,
7+
PydanticFormLabelProvider,
8+
} from 'pydantic-forms';
59

610
import styles from './page.module.css';
711

812
export default function Home() {
9-
const pydanticFormProvider: PydanticFormProvider = async ({
13+
const pydanticFormApiProvider: PydanticFormApiProvider = async ({
1014
requestBody,
1115
}) => {
1216
const fetchResult = await fetch('http://localhost:8000/form', {
@@ -20,17 +24,65 @@ export default function Home() {
2024
return jsonResult;
2125
};
2226

27+
const pydanticLabelProvider: PydanticFormLabelProvider = async () => {
28+
return new Promise((resolve) => {
29+
resolve({
30+
labels: {
31+
name: 'LABEL NAAM',
32+
name_info: 'DESCRIPTION NAAM',
33+
},
34+
data: {
35+
name: 'VALUE NAAM',
36+
},
37+
});
38+
});
39+
};
40+
41+
const pydanticCustomDataProvider: PydanticFormCustomDataProvider = () => {
42+
return new Promise((resolve) => {
43+
resolve({
44+
name: 'CUSTOM VALUE NAAM',
45+
});
46+
});
47+
};
48+
49+
const ResetButtonAlternative = () => (
50+
<button type="button">Alternative reset</button>
51+
);
52+
53+
const CancelButtonAlternative = () => (
54+
<button type="button">Alternative cancel</button>
55+
);
56+
2357
return (
2458
<div className={styles.page}>
2559
<h1>Pydantic Form</h1>
2660

2761
<PydanticForm
2862
id="theForm"
63+
title="The form title"
64+
sendLabel="Send label"
65+
successNotice="Custom success notice"
2966
onSuccess={() => {
67+
// console.log(fieldValues, summaryData, response);
3068
alert('Form submitted successfully');
3169
}}
70+
onCancel={() => {
71+
alert('Form cancelled');
72+
}}
73+
onChange={() => {
74+
// console.log('onChange', fieldValues);
75+
}}
3276
config={{
33-
formProvider: pydanticFormProvider,
77+
apiProvider: pydanticFormApiProvider,
78+
labelProvider: pydanticLabelProvider,
79+
customDataProvider: pydanticCustomDataProvider,
80+
resetButtonAlternative: ResetButtonAlternative(),
81+
cancelButton: CancelButtonAlternative(),
82+
allowUntouchedSubmit: true,
83+
onFieldChangeHandler: () => {
84+
// console.log('calling onFieldChangeHandler', field);
85+
},
3486
}}
3587
headerComponent={<div>HEADER COMPONENT</div>}
3688
footerComponent={<div>FOOTER COMPONENT</div>}

frontend/packages/pydantic-forms/src/core/PydanticFormContextProvider.tsx

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
getFormValuesFromFieldOrLabels,
2828
} from '@/core/helper';
2929
import {
30-
usePydanticForm,
30+
useApiProvider,
3131
usePydanticFormParser,
3232
useRefParser,
3333
} from '@/core/hooks';
@@ -78,7 +78,7 @@ function PydanticFormContextProvider({
7878
const {
7979
customDataProvider,
8080
labelProvider,
81-
formProvider,
81+
apiProvider,
8282
fieldDetailProvider,
8383
onFieldChangeHandler,
8484
customDataProviderCacheKey,
@@ -93,13 +93,21 @@ function PydanticFormContextProvider({
9393
cancelButton,
9494
} = config;
9595

96+
// TODO: Fix this again
9697
// option to enable the debug mode on the fly in the browser
9798
// by setting localStorage.setItem("pydanticFormsDebugMode", "true")
9899
// reload is required
99100
const debugMode = false;
100101

101102
// eslint-disable-next-line @typescript-eslint/no-explicit-any
102-
const [formInputData, setFormInputData] = useState<any>([]);
103+
const [formInputData, setFormInputData] = useState<object[]>([]);
104+
105+
const addFormInputData = (formInput: object) => {
106+
setFormInputData((currentInputs) => {
107+
return [...currentInputs, formInput];
108+
});
109+
};
110+
103111
const [errorDetails, setErrorDetails] =
104112
useState<PydanticFormValidationErrorDetails>();
105113
const [isFullFilled, setIsFullFilled] = useState(false);
@@ -111,18 +119,18 @@ function PydanticFormContextProvider({
111119
const { data: formLabels, isLoading: isLoadingFormLabels } =
112120
useLabelProvider(labelProvider, formKey, formIdKey);
113121

114-
const { data: customData, isLoading: isCustomDataLoading } =
122+
const { data: customData, isLoading: isLoadingCustomData } =
115123
useCustomDataProvider(
116124
customDataProviderCacheKey ?? 100,
117125
customDataProvider,
118126
);
119127

120128
// fetch the form definition using SWR hook
121129
const {
122-
data: apiErrorResp,
130+
data: apiResponse,
123131
isLoading: isLoadingSchema,
124132
error,
125-
} = usePydanticForm(formKey, formInputData, formProvider, metaData);
133+
} = useApiProvider(formKey, formInputData, apiProvider, metaData);
126134

127135
// we cache the form scheme so when there is an error, we still have the form
128136
// the form is not in the error response
@@ -170,14 +178,13 @@ function PydanticFormContextProvider({
170178

171179
rhfRef.current = rhf;
172180

173-
/* TODO: implement this
181+
/* TODO: Reimplement
174182
// prevent user from navigating away when there are unsaved changes
175183
const hasUnsavedData =
176184
!saveToLeavePageInCurrentState &&
177185
!isFullFilled &&
178186
rhf.formState.isDirty;
179187
180-
/* TODO: implement this
181188
useLeavePageConfirm(
182189
hasUnsavedData,
183190
'Er zijn aanpassingen in het formulier. \nWeet je zeker dat je de pagina wilt verlaten?',
@@ -226,36 +233,43 @@ function PydanticFormContextProvider({
226233
});
227234

228235
if (skipSuccessNotice) {
229-
onSuccess(values, summaryData);
236+
onSuccess(values, summaryData, apiResponse || {});
230237
} else {
231238
setTimeout(() => {
232-
onSuccess?.(values, summaryData);
233-
}, 1500);
239+
onSuccess?.(values, summaryData, apiResponse || {});
240+
}, 1500); // Delay to allow notice to show first
234241
}
235-
}, [isFullFilled, skipSuccessNotice, onSuccess, rhf, formData]);
242+
}, [
243+
isFullFilled,
244+
skipSuccessNotice,
245+
onSuccess,
246+
rhf,
247+
formData,
248+
apiResponse,
249+
]);
236250

237251
// a useeffect for whenever the error response updates
238252
// sometimes we need to update the form,
239253
// some we need to update the errors
240254
useEffect(() => {
241-
if (apiErrorResp?.success) {
255+
if (apiResponse?.success) {
242256
setIsFullFilled(true);
243257
return;
244258
}
245259

246260
// when we receive a form from the JSON, we fully reset the scheme
247-
if (apiErrorResp?.form) {
248-
setRawSchema(apiErrorResp.form);
261+
if (apiResponse?.form) {
262+
setRawSchema(apiResponse.form);
249263
setErrorDetails(undefined);
250264
}
251265

252266
// when we receive errors, we append to the scheme
253-
if (apiErrorResp?.validation_errors) {
254-
setErrorDetails(getErrorDetailsFromResponse(apiErrorResp));
267+
if (apiResponse?.validation_errors) {
268+
setErrorDetails(getErrorDetailsFromResponse(apiResponse));
255269
}
256270

257271
setIsSending(false);
258-
}, [apiErrorResp, onSuccess, rhf, skipSuccessNotice]);
272+
}, [apiResponse, onSuccess, rhf, skipSuccessNotice]);
259273

260274
const resetFormData = useCallback(() => {
261275
if (!formData) {
@@ -292,7 +306,7 @@ function PydanticFormContextProvider({
292306

293307
const submitFormFn = useCallback(() => {
294308
setIsSending(true);
295-
setFormInputData([rhf?.getValues()]);
309+
addFormInputData(rhf?.getValues());
296310

297311
window.scrollTo(0, 0);
298312
}, [rhf]);
@@ -320,7 +334,7 @@ function PydanticFormContextProvider({
320334
[resetFormData, rhf],
321335
);
322336

323-
// with this we have the possiblity to have listeners for specific fields
337+
// with this we have the possibility to have listeners for specific fields
324338
// this could be used to trigger validations of related fields, casting changes to elsewhere, etc.
325339
useEffect(() => {
326340
let sub: Subscription;
@@ -349,7 +363,7 @@ function PydanticFormContextProvider({
349363
isLoadingFormLabels ||
350364
isLoadingSchema ||
351365
isLoadingSchema ||
352-
(customDataProvider ? isCustomDataLoading : false);
366+
(customDataProvider ? isLoadingCustomData : false);
353367

354368
const PydanticFormContextState = {
355369
// to prevent an issue where the sending state hangs

frontend/packages/pydantic-forms/src/core/helper.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { ControllerRenderProps, FieldValues, useForm } from 'react-hook-form';
77

88
import fieldsConfig from '@/components/config';
99
import {
10-
PydanticFormApiErrorResponse,
10+
PydanticFormApiResponse,
1111
PydanticFormApiResponsePropertyResolved,
1212
PydanticFormData,
1313
PydanticFormField,
@@ -27,7 +27,7 @@ import { insertItemAtIndex } from '@/utils';
2727
* @returns A object better usable for displaying errors
2828
*/
2929
export const getErrorDetailsFromResponse = function (
30-
apiErrorResp: PydanticFormApiErrorResponse,
30+
apiErrorResp: PydanticFormApiResponse,
3131
) {
3232
return {
3333
detail: apiErrorResp.detail ?? '',
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export * from './usePydanticForm';
1+
export * from './useApiProvider';
22
export * from './useRefParser';
33
export * from './usePydanticFormParser';

frontend/packages/pydantic-forms/src/core/hooks/usePydanticForm.tsx renamed to frontend/packages/pydantic-forms/src/core/hooks/useApiProvider.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
import useSWR, { SWRConfiguration } from 'swr';
1818

1919
import type {
20-
PydanticFormApiErrorResponse,
20+
PydanticFormApiProvider,
21+
PydanticFormApiResponse,
2122
PydanticFormMetaData,
22-
PydanticFormProvider,
2323
} from '@/types';
2424

2525
const ignoreApiErrors = async (
@@ -35,28 +35,29 @@ const ignoreApiErrors = async (
3535
}
3636
};
3737

38-
export function usePydanticForm(
38+
export function useApiProvider(
3939
formKey: string,
4040
formInputData: PydanticFormMetaData, // TODO: This doesn't seem right
41-
formProvider: PydanticFormProvider,
41+
apiProvider: PydanticFormApiProvider,
4242
metaData?: PydanticFormMetaData,
4343
cacheKey?: number,
4444
swrConfig?: SWRConfiguration,
4545
) {
46-
return useSWR<PydanticFormApiErrorResponse>(
46+
return useSWR<PydanticFormApiResponse>(
4747
// cache key
4848
[formKey, formInputData, metaData, swrConfig, cacheKey],
4949

5050
// return val
5151
async ([formKey, formInputData]) => {
5252
// TODO: Readd sending metadata along with request
5353
const requestBody = formInputData;
54-
const formProviderRequest = formProvider({
54+
55+
const apiProviderRequest = apiProvider({
5556
formKey,
5657
requestBody,
5758
});
5859
const req = (await ignoreApiErrors(
59-
formProviderRequest,
60+
apiProviderRequest,
6061
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6162
)) as any;
6263

@@ -65,6 +66,7 @@ export function usePydanticForm(
6566
(!req.validation_errors && !req.form)
6667
) {
6768
return {
69+
response: req,
6870
success: true,
6971
};
7072
}

0 commit comments

Comments
 (0)