picoclaw/web/frontend/src/components/agent/tools/web-search-tab.tsx

113 lines
3.5 KiB
TypeScript

import { useTranslation } from "react-i18next"
import type { WebSearchConfigResponse } from "@/api/tools"
import { ConfigChangeNotice } from "@/components/config-change-notice"
import { Button } from "@/components/ui/button"
import { Skeleton } from "@/components/ui/skeleton"
import type { WebSearchDraftUpdater } from "./types"
import { WebSearchGeneralSettings } from "./web-search-general-settings"
import { WebSearchProviderSettings } from "./web-search-provider-settings"
interface WebSearchTabProps {
draft: WebSearchConfigResponse | null
providerLabelMap: Map<string, string>
expandedProvider: string | null
isLoading: boolean
hasError: boolean
isSaving: boolean
isDirty: boolean
onSave: () => void
onToggleProviderExpand: (providerId: string) => void
onUpdateDraft: WebSearchDraftUpdater
}
export function WebSearchTab({
draft,
providerLabelMap,
expandedProvider,
isLoading,
hasError,
isSaving,
isDirty,
onSave,
onToggleProviderExpand,
onUpdateDraft,
}: WebSearchTabProps) {
const { t } = useTranslation()
return (
<div className="animate-in fade-in slide-in-from-bottom-2 space-y-12 pt-2 duration-500">
{hasError ? (
<div className="py-20 text-center">
<p className="text-destructive font-medium">
{t(
"pages.agent.tools.web_search.load_error",
"Failed to load web search configuration",
)}
</p>
</div>
) : isLoading || !draft ? (
<LoadingState />
) : (
<>
<div className="flex flex-col gap-6 sm:flex-row sm:items-start sm:justify-between">
<div className="max-w-xl space-y-3">
<h1 className="text-foreground/90 text-2xl font-semibold tracking-tight">
{t(
"pages.agent.tools.web_search.title",
"Web Search Configuration",
)}
</h1>
<p className="text-muted-foreground/80 text-[14px] leading-relaxed">
{t(
"pages.agent.tools.web_search.description",
"Configure how the web search tool behaves by default, including whether the model may use its built-in search capability.",
)}
</p>
</div>
<Button
onClick={onSave}
disabled={!isDirty || isSaving}
className="h-10 shrink-0 rounded-xl px-6 shadow-sm transition-all active:scale-95"
>
{t("pages.agent.tools.web_search.save", "Save Changes")}
</Button>
</div>
{isDirty && (
<ConfigChangeNotice
kind="save"
title={t("common.saveChangesTitle")}
description={t("pages.agent.tools.web_search.unsaved_prompt")}
/>
)}
<div className="space-y-10">
<WebSearchGeneralSettings
draft={draft}
onUpdateDraft={onUpdateDraft}
/>
<WebSearchProviderSettings
providerLabelMap={providerLabelMap}
settings={draft.settings}
expandedProvider={expandedProvider}
onToggleProviderExpand={onToggleProviderExpand}
onUpdateDraft={onUpdateDraft}
/>
</div>
</>
)}
</div>
)
}
function LoadingState() {
return (
<div className="space-y-8">
<Skeleton className="h-24 rounded-2xl" />
<Skeleton className="h-64 rounded-2xl" />
</div>
)
}