@@ -6,17 +6,21 @@ import {
6
6
CopyButton ,
7
7
Flex ,
8
8
FormControl ,
9
+ SectionHeading ,
9
10
Stack ,
10
11
Subheading ,
11
12
Text ,
12
13
TextInput ,
13
14
} from '@contentful/f36-components' ;
14
15
import { useSDK } from '@contentful/react-apps-toolkit' ;
15
- import { AppActionProps } from 'contentful-management' ;
16
+ import { AppActionCallRawResponseProps , AppActionProps } from 'contentful-management' ;
16
17
import { useState } from 'react' ;
17
- import { ActionResultData , ActionResultType } from '../locations/Page' ;
18
+ import { ActionResultType } from '../locations/Page' ;
18
19
import ActionResult from './ActionResult' ;
19
20
import { styles } from './AppActionCard.styles' ;
21
+ import Forma36Form from './rjsf/Forma36Form' ;
22
+ import validator from '@rjsf/validator-ajv8' ;
23
+ import type { IChangeEvent } from '@rjsf/core' ;
20
24
21
25
interface Props {
22
26
action : AppActionProps ;
@@ -26,50 +30,66 @@ const AppActionCard = (props: Props) => {
26
30
const [ actionResults , setActionResults ] = useState < ActionResultType [ ] > ( [ ] ) ;
27
31
const [ loadingAction , setLoadingAction ] = useState < string | null > ( null ) ;
28
32
const [ actionParameters , setActionParameters ] = useState < any > ( { } ) ;
33
+ const [ schemaErrorsByAction , setSchemaErrorsByAction ] = useState < Record < string , number > > ( { } ) ;
29
34
30
35
const sdk = useSDK < PageAppSDK > ( ) ;
31
36
const { action } = props ;
32
37
33
38
const callAction = async ( action : AppActionProps ) => {
34
39
setLoadingAction ( action . sys . id ) ;
40
+ let response : AppActionCallRawResponseProps | undefined ;
35
41
try {
36
- const result = ( await sdk . cma . appActionCall . createWithResult (
42
+ const result = await sdk . cma . appActionCall . createWithResult (
37
43
{
38
44
appDefinitionId : sdk . ids . app || '' ,
39
45
appActionId : action . sys . id ,
40
46
} ,
41
47
{
42
48
parameters : actionParameters [ action . sys . id ] || { } ,
43
49
}
44
- ) ) as unknown as ActionResultData ;
50
+ ) ;
45
51
46
52
const timestamp = new Date ( ) . toLocaleString ( ) ;
47
- const call : any = result as any ;
48
- const callId = ( call as any ) ?. sys ?. id ;
53
+ const call = result ;
54
+ const callId = call ?. sys ?. id ;
49
55
const base = { timestamp, actionId : action . sys . id , callId } as const ;
56
+ if ( call . sys . appActionCallResponse ) {
57
+ response = await sdk . cma . appActionCall . getResponse ( {
58
+ appDefinitionId : sdk . ids . app || '' ,
59
+ appActionId : action . sys . id ,
60
+ callId,
61
+ } ) ;
62
+ }
50
63
51
64
if ( call ?. status === 'succeeded' ) {
52
- setActionResults ( ( prev ) => [ { success : true , data : call , ...base } , ...prev ] ) ;
65
+ setActionResults ( ( prev ) => [ { success : true , call, ...base } , ...prev ] ) ;
53
66
} else if ( call ?. status === 'failed' ) {
54
67
setActionResults ( ( prev ) => [
55
68
{
56
69
success : false ,
57
- data : call ,
70
+ call,
71
+ response,
58
72
error : call ?. error || new Error ( 'App action failed' ) ,
59
73
...base ,
60
74
} ,
61
75
...prev ,
62
76
] ) ;
63
77
} else {
64
78
setActionResults ( ( prev ) => [
65
- { success : false , data : call , error : new Error ( 'App action still processing' ) , ...base } ,
79
+ {
80
+ success : false ,
81
+ call,
82
+ response,
83
+ error : new Error ( 'App action still processing' ) ,
84
+ ...base ,
85
+ } ,
66
86
...prev ,
67
87
] ) ;
68
88
}
69
89
} catch ( error ) {
70
90
const timestamp = new Date ( ) . toLocaleString ( ) ;
71
91
setActionResults ( ( prev ) => [
72
- { success : false , error, timestamp, actionId : action . sys . id } ,
92
+ { success : false , error, timestamp, actionId : action . sys . id , response } ,
73
93
...prev ,
74
94
] ) ;
75
95
} finally {
@@ -136,12 +156,31 @@ const AppActionCard = (props: Props) => {
136
156
} ;
137
157
138
158
const isButtonDisabled = ( ) => {
139
- const parameters = ( action as any ) . parameters as any [ ] | undefined ;
140
- const requiredParameters = parameters ?. filter ( ( param : any ) => param . required ) ?? [ ] ;
159
+ const actionId = action . sys . id ;
160
+ const formData = actionParameters [ actionId ] || { } ;
141
161
142
- const hasEmptyRequiredParameters = requiredParameters . find ( ( param : any ) => {
143
- const paramValue = actionParameters [ action . sys . id ] ?. [ param . id ] ;
144
- return ! paramValue ;
162
+ const hasSchema = action . parametersSchema ;
163
+ if ( hasSchema ) {
164
+ const schema = action . parametersSchema ;
165
+ const requiredKeys : string [ ] = Array . isArray ( schema ?. required ) ? schema . required : [ ] ;
166
+ const hasEmptyRequired = requiredKeys . some ( ( key ) => {
167
+ const value = formData ?. [ key ] ;
168
+ if ( value === undefined || value === null ) return true ;
169
+ if ( typeof value === 'string' && value . trim ( ) === '' ) return true ;
170
+ if ( typeof value === 'number' && Number . isNaN ( value ) ) return true ;
171
+ return false ;
172
+ } ) ;
173
+ const hasErrors = Boolean ( schemaErrorsByAction [ actionId ] ) ;
174
+ return hasErrors || hasEmptyRequired ;
175
+ }
176
+
177
+ const customAction = action as unknown as {
178
+ parameters ?: Array < { id : string ; required ?: boolean } > ;
179
+ } ;
180
+ const requiredParameters = customAction . parameters ?. filter ( ( param ) => param . required ) || [ ] ;
181
+ const hasEmptyRequiredParameters = requiredParameters . some ( ( param ) => {
182
+ const paramValue = formData ?. [ param . id ] ;
183
+ return paramValue === undefined || paramValue === null || paramValue === '' ;
145
184
} ) ;
146
185
147
186
return Boolean ( hasEmptyRequiredParameters ) ;
@@ -175,8 +214,34 @@ const AppActionCard = (props: Props) => {
175
214
</ Button >
176
215
</ Box >
177
216
</ Flex >
178
- { Array . isArray ( ( action as any ) . parameters ) &&
179
- ( action as { parameters : any [ ] } ) . parameters . length ? (
217
+ { action . parametersSchema ? (
218
+ < Box marginTop = "spacingS" >
219
+ < Box marginBottom = "spacingS" >
220
+ < SectionHeading as = "h4" > Parameters</ SectionHeading >
221
+ </ Box >
222
+ < Forma36Form
223
+ schema = { action . parametersSchema }
224
+ formData = { actionParameters [ action . sys . id ] || { } }
225
+ validator = { validator }
226
+ liveValidate
227
+ showErrorList = { false }
228
+ uiSchema = { { 'ui:submitButtonOptions' : { norender : true } } }
229
+ onChange = { ( e : IChangeEvent < Record < string , unknown > > ) => {
230
+ setActionParameters ( {
231
+ ...actionParameters ,
232
+ [ action . sys . id ] : e . formData ,
233
+ } ) ;
234
+ const errorCount = Array . isArray ( e . errors ) ? e . errors . length : 0 ;
235
+ setSchemaErrorsByAction ( {
236
+ ...schemaErrorsByAction ,
237
+ [ action . sys . id ] : errorCount ,
238
+ } ) ;
239
+ } } >
240
+ < > </ >
241
+ </ Forma36Form >
242
+ </ Box >
243
+ ) : Array . isArray ( ( action as any ) . parameters ) &&
244
+ ( action as { parameters : any [ ] } ) . parameters . length ? (
180
245
< Box marginTop = "spacingS" >
181
246
< Box marginBottom = "spacingM" >
182
247
< Subheading as = "h4" > Parameters</ Subheading >
0 commit comments