Skip to content

Commit c793c9a

Browse files
committed
feat: improve error handling
1 parent 2d97883 commit c793c9a

File tree

2 files changed

+144
-91
lines changed

2 files changed

+144
-91
lines changed

src/pages/agents/create-edit/AgentResources.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,11 @@ interface AgentResourcesProps {
8989

9090
export default function AgentResources(props: AgentResourcesProps) {
9191
const { classes, cx } = useStyles()
92-
const { control, register } = useFormContext()
92+
const {
93+
control,
94+
register,
95+
formState: { errors },
96+
} = useFormContext()
9397
const [focusedApiKeyIndex, setFocusedApiKeyIndex] = React.useState<number | null>(null)
9498

9599
const {
@@ -181,6 +185,18 @@ export default function AgentResources(props: AgentResourcesProps) {
181185
return localIndex === 0 ? '48px' : '28px'
182186
}
183187

188+
// Get errors for this specific index
189+
const getFieldError = (fieldName: string) => {
190+
const fieldPath = name.split('.')
191+
const errorObj = fieldPath.reduce((acc: any, path: string) => acc?.[path], errors)
192+
return errorObj?.[index]?.[fieldName]
193+
}
194+
195+
const field1Error = getFieldError(fieldNames.field1)
196+
const field2Error = getFieldError(fieldNames.field2)
197+
const field3Error = getFieldError(fieldNames.field3)
198+
const field4Error = getFieldError(fieldNames.field4)
199+
184200
return (
185201
<Box key={field.id} sx={{ display: 'flex', alignItems: 'center' }}>
186202
<FormRow
@@ -198,7 +214,8 @@ export default function AgentResources(props: AgentResourcesProps) {
198214
disabled={disabled}
199215
noMarginTop={compressed}
200216
label={showLabel && localIndex === 0 ? labels.field1 : ''}
201-
error={error}
217+
error={!!field1Error}
218+
helperText={field1Error?.message?.toString()}
202219
InputProps={{
203220
readOnly: frozen,
204221
}}
@@ -210,7 +227,8 @@ export default function AgentResources(props: AgentResourcesProps) {
210227
disabled={disabled}
211228
noMarginTop={compressed}
212229
label={showLabel && localIndex === 0 ? labels.field2 : ''}
213-
error={error}
230+
error={!!field2Error}
231+
helperText={field2Error?.message?.toString()}
214232
InputProps={{
215233
readOnly: frozen,
216234
}}
@@ -222,7 +240,8 @@ export default function AgentResources(props: AgentResourcesProps) {
222240
disabled={disabled}
223241
noMarginTop={compressed}
224242
label={showLabel && localIndex === 0 ? labels.field3 : ''}
225-
error={error}
243+
error={!!field3Error}
244+
helperText={field3Error?.message?.toString()}
226245
InputProps={{
227246
readOnly: frozen,
228247
}}
@@ -234,7 +253,8 @@ export default function AgentResources(props: AgentResourcesProps) {
234253
disabled={disabled}
235254
noMarginTop={compressed}
236255
label={showLabel && localIndex === 0 ? labels.field4 : ''}
237-
error={error}
256+
error={!!field4Error}
257+
helperText={field4Error?.message?.toString()}
238258
type={focusedApiKeyIndex === index ? 'text' : 'password'}
239259
onFocus={() => setFocusedApiKeyIndex(index)}
240260
onBlur={() => setFocusedApiKeyIndex(null)}

src/pages/agents/create-edit/AgentsCreateEditPage.tsx

Lines changed: 119 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AppBar, Box, Grid, Tab, Tabs, Typography } from '@mui/material'
1+
import { AppBar, Box, Grid, Tab, Tabs, Tooltip, Typography } from '@mui/material'
22
import { TextField } from 'components/forms/TextField'
33
import { Autocomplete } from 'components/forms/Autocomplete'
44
import { AutoResizableTextarea } from 'components/forms/TextArea'
@@ -8,7 +8,7 @@ import React, { useEffect, useState } from 'react'
88
import { FormProvider, Resolver, useForm } from 'react-hook-form'
99
import { yupResolver } from '@hookform/resolvers/yup'
1010
import { Redirect, RouteComponentProps } from 'react-router-dom'
11-
import { isEqual } from 'lodash'
11+
import { isEmpty, isEqual } from 'lodash'
1212
import {
1313
CreateAplAgentApiArg,
1414
useCreateAplAgentMutation,
@@ -60,9 +60,24 @@ export default function AgentsCreateEditPage({
6060

6161
const [tab, setTab] = useState(0)
6262
const handleTabChange = (event, newTab) => {
63-
setTab(newTab)
63+
setTab(newTab as number)
6464
}
6565

66+
const tabHasErrors = (tabIndex: number): boolean => {
67+
switch (tabIndex) {
68+
case 0: // Settings tab
69+
return !!(errors.metadata?.name || errors.spec?.foundationModel)
70+
case 1: // Playground tab
71+
return !!errors.spec?.agentInstructions
72+
case 2: // Workflow resources tab
73+
return !!(errors.spec?.tools || errors.spec?.routes)
74+
default:
75+
return false
76+
}
77+
}
78+
79+
const DEFAULT_AGENT_INSTRUCTIONS = 'You are a helpful assistant. Give clear answers to the users.'
80+
6681
type FormType = CreateAplAgentApiArg['body']
6782

6883
const defaultValues: FormType = {
@@ -90,6 +105,7 @@ export default function AgentsCreateEditPage({
90105
watch,
91106
formState: { errors },
92107
setValue,
108+
trigger,
93109
} = methods
94110

95111
useEffect(() => {
@@ -103,6 +119,17 @@ export default function AgentsCreateEditPage({
103119
else create({ teamId, body })
104120
}
105121

122+
const handleFormSubmit = async () => {
123+
const formData = watch()
124+
if (isEmpty(formData.spec.agentInstructions)) {
125+
setValue('spec.agentInstructions', DEFAULT_AGENT_INSTRUCTIONS)
126+
await trigger('spec.agentInstructions')
127+
}
128+
handleSubmit(onSubmit)()
129+
}
130+
131+
const requiredFieldsFilled = !!watch('metadata.name') && !!watch('spec.foundationModel')
132+
106133
const mutating = isLoadingCreate || isLoadingUpdate || isLoadingDelete
107134
if (!mutating && (isSuccessCreate || isSuccessUpdate || isSuccessDelete))
108135
return <Redirect to={`/teams/${teamId}/agents`} />
@@ -111,6 +138,48 @@ export default function AgentsCreateEditPage({
111138

112139
if (loading) return <PaperLayout loading title={t('TITLE_AGENT')} />
113140

141+
const formButtons = (
142+
<Box>
143+
{agentName && (
144+
<DeleteButton
145+
onDelete={() => del({ teamId, agentName })}
146+
resourceName={watch('metadata.name')}
147+
resourceType='agent'
148+
data-cy='button-delete-agent'
149+
sx={{ float: 'right', textTransform: 'capitalize', ml: 2 }}
150+
loading={isLoadingDelete}
151+
disabled={isLoadingDelete || isLoadingCreate || isLoadingUpdate}
152+
/>
153+
)}
154+
<Tooltip
155+
title={
156+
!agentName && !requiredFieldsFilled
157+
? 'Please fill in required fields: Agent name, Foundation model, and Instructions'
158+
: ''
159+
}
160+
>
161+
<Box sx={{ float: 'right' }}>
162+
<LoadingButton
163+
onClick={handleFormSubmit}
164+
variant='contained'
165+
color='primary'
166+
sx={{ textTransform: 'none' }}
167+
loading={isLoadingCreate || isLoadingUpdate}
168+
disabled={
169+
isLoadingCreate ||
170+
isLoadingUpdate ||
171+
isLoadingDelete ||
172+
(!agentName && !requiredFieldsFilled) ||
173+
(agentName && data && isEqual(data, watch()))
174+
}
175+
>
176+
{agentName ? 'Save Changes' : 'Create Agent'}
177+
</LoadingButton>
178+
</Box>
179+
</Tooltip>
180+
</Box>
181+
)
182+
114183
return (
115184
<Grid>
116185
<PaperLayout loading={loading || isError} title={t('TITLE_AGENT')}>
@@ -130,18 +199,54 @@ export default function AgentsCreateEditPage({
130199
},
131200
}}
132201
>
133-
<Tab label='Settings' value={0} sx={{ textTransform: 'capitalize', padding: '12px 16px' }} />
134-
<Tab label='Playground' value={1} sx={{ textTransform: 'capitalize', padding: '12px 16px' }} />
135-
<Tab label='Workflow resources' value={2} sx={{ textTransform: 'capitalize', padding: '12px 16px' }} />
202+
<Tab
203+
label='Settings'
204+
value={0}
205+
sx={{
206+
textTransform: 'capitalize',
207+
padding: '12px 16px',
208+
...(tab !== 0 &&
209+
tabHasErrors(0) && {
210+
borderBottom: '2px solid',
211+
borderBottomColor: 'error.main',
212+
}),
213+
}}
214+
/>
215+
<Tab
216+
label='Playground'
217+
value={1}
218+
sx={{
219+
textTransform: 'capitalize',
220+
padding: '12px 16px',
221+
...(tab !== 1 &&
222+
tabHasErrors(1) && {
223+
borderBottom: '2px solid',
224+
borderBottomColor: 'error.main',
225+
}),
226+
}}
227+
/>
228+
<Tab
229+
label='Workflow resources'
230+
value={2}
231+
sx={{
232+
textTransform: 'capitalize',
233+
padding: '12px 16px',
234+
...(tab !== 2 &&
235+
tabHasErrors(2) && {
236+
borderBottom: '2px solid',
237+
borderBottomColor: 'error.main',
238+
}),
239+
}}
240+
/>
136241
</Tabs>
137242
</AppBar>
138243
<FormProvider {...methods}>
139-
<form onSubmit={handleSubmit(onSubmit)}>
244+
<form>
140245
<TabPanel value={tab} index={0}>
141246
<Section noPaddingTop>
142247
<FormRow spacing={10}>
143248
<TextField
144-
label='Agent name'
249+
label='Agent name *'
145250
width='large'
146251
placeholder='my-agent'
147252
{...register('metadata.name')}
@@ -159,7 +264,7 @@ export default function AgentsCreateEditPage({
159264

160265
<FormRow spacing={10}>
161266
<Autocomplete
162-
label='Foundation model'
267+
label='Foundation model *'
163268
width='large'
164269
placeholder='Select a foundation model'
165270
options={
@@ -181,37 +286,15 @@ export default function AgentsCreateEditPage({
181286
</FormRow>
182287
</Section>
183288

184-
{agentName && (
185-
<DeleteButton
186-
onDelete={() => del({ teamId, agentName })}
187-
resourceName={watch('metadata.name')}
188-
resourceType='agent'
189-
data-cy='button-delete-agent'
190-
sx={{ float: 'right', textTransform: 'capitalize', ml: 2 }}
191-
loading={isLoadingDelete}
192-
disabled={isLoadingDelete || isLoadingCreate || isLoadingUpdate}
193-
/>
194-
)}
195-
<LoadingButton
196-
type='submit'
197-
variant='contained'
198-
color='primary'
199-
sx={{ float: 'right', textTransform: 'none' }}
200-
loading={isLoadingCreate || isLoadingUpdate}
201-
disabled={
202-
isLoadingCreate || isLoadingUpdate || isLoadingDelete || (agentName && data && isEqual(data, watch()))
203-
}
204-
>
205-
{agentName ? 'Save Changes' : 'Create Agent'}
206-
</LoadingButton>
289+
{formButtons}
207290
</TabPanel>
208291

209292
<TabPanel value={tab} index={1}>
210293
<Section noPaddingTop>
211294
<FormRow spacing={10}>
212295
<AutoResizableTextarea
213-
label='Instructions'
214-
placeholder='Enter agent instructions...'
296+
label='Instructions *'
297+
placeholder={`Enter agent instructions... (Leave empty to use the default: '${DEFAULT_AGENT_INSTRUCTIONS}')`}
215298
minRows={12}
216299
maxRows={40}
217300
minWidth={400}
@@ -226,34 +309,7 @@ export default function AgentsCreateEditPage({
226309
</FormRow>
227310
</Section>
228311

229-
<Box sx={{ my: 2 }}>
230-
{agentName && (
231-
<DeleteButton
232-
onDelete={() => del({ teamId, agentName })}
233-
resourceName={watch('metadata.name')}
234-
resourceType='agent'
235-
data-cy='button-delete-agent'
236-
sx={{ float: 'right', textTransform: 'capitalize', ml: 2 }}
237-
loading={isLoadingDelete}
238-
disabled={isLoadingDelete || isLoadingCreate || isLoadingUpdate}
239-
/>
240-
)}
241-
<LoadingButton
242-
type='submit'
243-
variant='contained'
244-
color='primary'
245-
sx={{ float: 'right', textTransform: 'none' }}
246-
loading={isLoadingCreate || isLoadingUpdate}
247-
disabled={
248-
isLoadingCreate ||
249-
isLoadingUpdate ||
250-
isLoadingDelete ||
251-
(agentName && data && isEqual(data, watch()))
252-
}
253-
>
254-
{agentName ? 'Save Changes' : 'Create Agent'}
255-
</LoadingButton>
256-
</Box>
312+
<Box sx={{ my: 2 }}>{formButtons}</Box>
257313

258314
{agentName ? (
259315
<Box sx={{ clear: 'both' }}>
@@ -303,7 +359,6 @@ export default function AgentsCreateEditPage({
303359
}}
304360
/>
305361
</FormRow>
306-
307362
<Divider spacingBottom={10} />
308363
<AgentResources
309364
title='Agent route(s)'
@@ -348,29 +403,7 @@ export default function AgentsCreateEditPage({
348403
/>
349404
</Section>
350405

351-
{agentName && (
352-
<DeleteButton
353-
onDelete={() => del({ teamId, agentName })}
354-
resourceName={watch('metadata.name')}
355-
resourceType='agent'
356-
data-cy='button-delete-agent'
357-
sx={{ float: 'right', textTransform: 'capitalize', ml: 2 }}
358-
loading={isLoadingDelete}
359-
disabled={isLoadingDelete || isLoadingCreate || isLoadingUpdate}
360-
/>
361-
)}
362-
<LoadingButton
363-
type='submit'
364-
variant='contained'
365-
color='primary'
366-
sx={{ float: 'right', textTransform: 'none' }}
367-
loading={isLoadingCreate || isLoadingUpdate}
368-
disabled={
369-
isLoadingCreate || isLoadingUpdate || isLoadingDelete || (agentName && data && isEqual(data, watch()))
370-
}
371-
>
372-
{agentName ? 'Save Changes' : 'Create Agent'}
373-
</LoadingButton>
406+
{formButtons}
374407
</TabPanel>
375408
</form>
376409
</FormProvider>

0 commit comments

Comments
 (0)