import { useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Switch } from "@/components/ui/switch"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { ChevronDown, ChevronUp, ExternalLink, Plus, Trash2, } from "lucide-react"; import { useDeleteServiceInstance, useSaveServiceInstance, useServiceInstances, useTestServiceInstance, } from "../hooks/useServices"; import { useServiceTypes } from "../hooks/useServices"; import { useDashboards, useDeleteDashboard, useSaveDashboard, } from "../hooks/useDashboards"; import type { SecretFieldInfo, ServiceInstance, ServiceInstanceInput, ServiceTestResult, ServiceTypeInfo, } from "../types"; import { SectionCard } from "../components/SectionCard"; import { ConfirmDialog } from "../components/ConfirmDialog"; import { DialogFooter } from "../components/DialogFooter"; import { getServiceBinding } from "../integrations/registry"; import { ServiceTestPanel } from "../components/ServiceTestPanel"; import { serviceLinkTarget } from "../components/PinnedServiceLink"; import type { NamedDashboardInput } from "../api/dashboards"; interface CreateDraft { serviceType: string; name: string; config: Record; secrets: Record; enabled: boolean; } function emptyDraft(serviceType: string): CreateDraft { return { serviceType, name: "", config: {}, secrets: {}, enabled: true }; } function Field({ label, htmlFor, helper, children, }: { label: string; htmlFor: string; helper?: string; children: React.ReactNode; }) { return (
{children} {helper ? (

{helper}

) : null}
); } function ServiceConfigFields({ type, config, onChange, }: { type: ServiceTypeInfo; config: Record; onChange: (config: Record) => void; }) { const properties = ( type.config_schema as { properties?: Record< string, { type?: string; description?: string; format?: string } >; } ).properties ?? {}; // Multi-line resizable textarea for fields that hold complex values (opt-in // via `format: "textarea"`, or well-known multi-line keys). const TEXTAREA_KEYS = new Set(["notes", "command"]); return (
{Object.entries(properties).map(([key, schema]) => { const isNumber = schema.type === "integer" || schema.type === "number"; const isTextarea = schema.format === "textarea" || TEXTAREA_KEYS.has(key); return ( {isTextarea ? (