Skip to content

Commit 1c8d04a

Browse files
authored
feat: add validation for agent import URL (#1544)
1 parent deb6bd3 commit 1c8d04a

File tree

2 files changed

+41
-21
lines changed

2 files changed

+41
-21
lines changed

apps/agentstack-ui/src/modules/agents/components/import/ImportAgentsModal.tsx

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import type { ModalProps } from '#contexts/Modal/modal-context.ts';
2727
import { useImportAgent } from '#modules/agents/hooks/useImportAgent.ts';
2828
import type { ImportAgentFormValues } from '#modules/agents/types.ts';
2929
import { ProviderSource } from '#modules/providers/types.ts';
30+
import { isValidUrl } from '#utils/url.ts';
3031

3132
import classes from './ImportAgentsModal.module.scss';
3233

@@ -43,10 +44,10 @@ export function ImportAgentsModal({ onRequestClose, ...modalProps }: ModalProps)
4344
register,
4445
handleSubmit,
4546
resetField,
46-
formState: { isValid },
47+
formState: { isValid, errors },
4748
control,
4849
} = useForm<ImportAgentFormValues>({
49-
mode: 'onChange',
50+
mode: 'onTouched',
5051
defaultValues: {
5152
source: featureFlags.ProviderBuilds ? ProviderSource.GitHub : ProviderSource.Docker,
5253
},
@@ -139,25 +140,34 @@ export function ImportAgentsModal({ onRequestClose, ...modalProps }: ModalProps)
139140
<RadioButton labelText="Container image URL" value={ProviderSource.Docker} />
140141
</RadioButtonGroup>
141142

142-
{sourceField.value === ProviderSource.GitHub ? (
143-
<TextInput
144-
id={`${id}:location`}
145-
size="lg"
146-
labelText="GitHub repository URL"
147-
placeholder="Enter your agent’s GitHub repository URL"
148-
hideLabel
149-
{...register('location', { required: true, disabled: isPending })}
150-
/>
151-
) : (
152-
<TextInput
153-
id={`${id}:location`}
154-
size="lg"
155-
labelText="Container image URL"
156-
placeholder="Enter your agent’s container image URL"
157-
hideLabel
158-
{...register('location', { required: true, disabled: isPending })}
159-
/>
160-
)}
143+
<TextInput
144+
id={`${id}:location`}
145+
size="lg"
146+
hideLabel
147+
invalid={Boolean(errors.location)}
148+
invalidText={errors.location?.message}
149+
{...(sourceField.value === ProviderSource.GitHub
150+
? {
151+
labelText: 'GitHub repository URL',
152+
placeholder: 'Enter your agent’s GitHub repository URL',
153+
}
154+
: {
155+
labelText: 'Container image URL',
156+
placeholder: 'Enter your agent’s container image URL',
157+
})}
158+
{...register('location', {
159+
required: 'Enter your agent’s location.',
160+
disabled: isPending,
161+
setValueAs: (value: string) => value.trim(),
162+
validate: (value: string) => {
163+
if (sourceField.value === ProviderSource.GitHub) {
164+
return isValidUrl(value) || 'Enter a valid GitHub repository URL.';
165+
}
166+
167+
return true;
168+
},
169+
})}
170+
/>
161171
</div>
162172
)}
163173

apps/agentstack-ui/src/utils/url.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,13 @@
77
const ABSOLUTE_URL_REGEX = new RegExp('^(?:[a-z+]+:)?//', 'i');
88

99
export const isAbsoluteUrl = (url: string) => ABSOLUTE_URL_REGEX.test(url);
10+
11+
export const isValidUrl = (value: string): boolean => {
12+
try {
13+
const { protocol } = new URL(value);
14+
15+
return protocol === 'http:' || protocol === 'https:';
16+
} catch {
17+
return false;
18+
}
19+
};

0 commit comments

Comments
 (0)