mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-16 08:25:18 +02:00
Fix/multiple generation (#104)
* fixes #100 * Fix test * fix: fix bad configuration issue
This commit is contained in:
parent
90b690efff
commit
56953bbd09
18 changed files with 758 additions and 460 deletions
|
|
@ -27,6 +27,8 @@ import {
|
|||
type KeyValueItem,
|
||||
ParameterEditor,
|
||||
type ToolParameter,
|
||||
UrlInput,
|
||||
validateUrl,
|
||||
} from "@/components/http";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
|
|
@ -151,8 +153,9 @@ export default function ToolDetailPage() {
|
|||
|
||||
const handleSave = async () => {
|
||||
// Validate URL
|
||||
if (!url.trim()) {
|
||||
setError("URL is required");
|
||||
const urlValidation = validateUrl(url);
|
||||
if (!urlValidation.valid) {
|
||||
setError(urlValidation.error || "Invalid URL");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -431,10 +434,11 @@ const data = await response.json();`;
|
|||
|
||||
<div className="grid gap-2">
|
||||
<Label>Endpoint URL</Label>
|
||||
<Input
|
||||
<UrlInput
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
onChange={setUrl}
|
||||
placeholder="https://api.example.com/appointments"
|
||||
showValidation
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import {
|
|||
HttpMethodSelector,
|
||||
KeyValueEditor,
|
||||
type KeyValueItem,
|
||||
UrlInput,
|
||||
validateUrl,
|
||||
} from "@/components/http";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
|
@ -57,8 +59,9 @@ export const WebhookNode = memo(({ data, selected, id }: WebhookNodeProps) => {
|
|||
|
||||
const handleSave = async () => {
|
||||
// Validate endpoint URL
|
||||
if (!endpointUrl.trim()) {
|
||||
setEndpointError('Endpoint URL is required');
|
||||
const urlValidation = validateUrl(endpointUrl);
|
||||
if (!urlValidation.valid) {
|
||||
setEndpointError(urlValidation.error || 'Invalid URL');
|
||||
return;
|
||||
}
|
||||
setEndpointError(null);
|
||||
|
|
@ -284,10 +287,11 @@ const WebhookNodeEditForm = ({
|
|||
<Label className="text-xs text-muted-foreground">
|
||||
The URL to send the webhook request to.
|
||||
</Label>
|
||||
<Input
|
||||
<UrlInput
|
||||
value={endpointUrl}
|
||||
onChange={(e) => setEndpointUrl(e.target.value)}
|
||||
onChange={setEndpointUrl}
|
||||
placeholder="https://api.example.com/webhook"
|
||||
showValidation
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@ export { CredentialSelector } from "./credential-selector";
|
|||
export { type HttpMethod, HttpMethodSelector } from "./http-method-selector";
|
||||
export { KeyValueEditor, type KeyValueItem } from "./key-value-editor";
|
||||
export { ParameterEditor, type ParameterType,type ToolParameter } from "./parameter-editor";
|
||||
export { UrlInput, type UrlValidationResult,validateUrl } from "./url-input";
|
||||
|
|
|
|||
106
ui/src/components/http/url-input.tsx
Normal file
106
ui/src/components/http/url-input.tsx
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
// URL regex pattern that validates:
|
||||
// - http:// or https:// protocol (required)
|
||||
// - Optional username:password@
|
||||
// - Domain name or IP address
|
||||
// - Optional port number
|
||||
// - Optional path, query string, and fragment
|
||||
const URL_REGEX =
|
||||
/^https?:\/\/(?:[\w-]+(?::[\w-]+)?@)?(?:[\w-]+\.)*[\w-]+(?::\d{1,5})?(?:\/[^\s]*)?$/i;
|
||||
|
||||
export interface UrlValidationResult {
|
||||
valid: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export function validateUrl(url: string): UrlValidationResult {
|
||||
const trimmedUrl = url.trim();
|
||||
|
||||
if (!trimmedUrl) {
|
||||
return { valid: false, error: "URL is required" };
|
||||
}
|
||||
|
||||
if (!URL_REGEX.test(trimmedUrl)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: "Invalid URL format. Must start with http:// or https://",
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
interface UrlInputProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
/** Show validation error styling and message inline */
|
||||
showValidation?: boolean;
|
||||
/** Called when validation state changes */
|
||||
onValidationChange?: (result: UrlValidationResult) => void;
|
||||
}
|
||||
|
||||
export function UrlInput({
|
||||
value,
|
||||
onChange,
|
||||
placeholder = "https://api.example.com/endpoint",
|
||||
disabled = false,
|
||||
className,
|
||||
showValidation = false,
|
||||
onValidationChange,
|
||||
}: UrlInputProps) {
|
||||
const [touched, setTouched] = useState(false);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = e.target.value;
|
||||
onChange(newValue);
|
||||
|
||||
if (onValidationChange && (touched || newValue)) {
|
||||
onValidationChange(validateUrl(newValue));
|
||||
}
|
||||
},
|
||||
[onChange, onValidationChange, touched]
|
||||
);
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
setTouched(true);
|
||||
const trimmedValue = value.trim();
|
||||
if (trimmedValue !== value) {
|
||||
onChange(trimmedValue);
|
||||
}
|
||||
if (onValidationChange && trimmedValue) {
|
||||
onValidationChange(validateUrl(trimmedValue));
|
||||
}
|
||||
}, [onChange, onValidationChange, value]);
|
||||
|
||||
const validation = validateUrl(value);
|
||||
const showError = showValidation && touched && !validation.valid && value;
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<Input
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
showError && "border-destructive focus-visible:ring-destructive",
|
||||
className
|
||||
)}
|
||||
/>
|
||||
{showError && (
|
||||
<p className="text-xs text-destructive">{validation.error}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue