ultisuite-client/components/admin/settings/sections/search-section.tsx
2026-06-07 21:55:42 +02:00

170 lines
6.4 KiB
TypeScript

"use client"
import { OrgSettingsSection } from "@/components/admin/settings/org-settings-form"
import { DeployLockedHint, useDeployFieldLocked } from "@/components/admin/settings/deploy-locked-hint"
import { useOrgSettingsStore } from "@/lib/admin-settings/org-settings-store"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
export function SearchSection() {
const search = useOrgSettingsStore((s) => s.search)
const setSearch = useOrgSettingsStore((s) => s.setSearch)
const effective = useOrgSettingsStore((s) => s.meta?.effective.search)
const brave = search.web_search.providers[0]
const engineLocked = useDeployFieldLocked("search", "suite_engine")
const meiliURLLocked = useDeployFieldLocked("search", "meilisearch_url")
const meiliKeyLocked = useDeployFieldLocked("search", "meilisearch_api_key")
const typesenseURLLocked = useDeployFieldLocked("search", "typesense_url")
const typesenseKeyLocked = useDeployFieldLocked("search", "typesense_api_key")
const suiteEngine = engineLocked
? ((effective?.suite_engine as typeof search.suite_engine) ?? search.suite_engine)
: search.suite_engine
const meiliURL = meiliURLLocked
? (effective?.meilisearch_url ?? search.meilisearch_url)
: search.meilisearch_url
const typesenseURL = typesenseURLLocked
? (effective?.typesense_url ?? search.typesense_url)
: search.typesense_url
return (
<OrgSettingsSection
title="Moteur de recherche"
description="Index de recherche suite (mail, drive) et recherche web pour l'IA contacts."
policySection="search"
>
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium">Recherche suite</CardTitle>
<CardDescription>
Moteur d&apos;indexation pour la recherche globale (variables SEARCH_ENGINE côté serveur).
</CardDescription>
{engineLocked ? <DeployLockedHint section="search" field="suite_engine" /> : null}
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label>Moteur</Label>
<Select
value={suiteEngine}
disabled={engineLocked}
onValueChange={(suite_engine) =>
setSearch({
suite_engine: suite_engine as typeof search.suite_engine,
})
}
>
<SelectTrigger className="mt-1 h-9">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="postgres">PostgreSQL (full-text)</SelectItem>
<SelectItem value="meilisearch">Meilisearch</SelectItem>
<SelectItem value="typesense">Typesense</SelectItem>
</SelectContent>
</Select>
</div>
{suiteEngine === "meilisearch" ? (
<div className="grid gap-3 sm:grid-cols-2">
<div>
<Label>URL Meilisearch</Label>
<Input
className="mt-1 h-9"
value={meiliURL}
disabled={meiliURLLocked}
onChange={(e) => setSearch({ meilisearch_url: e.target.value })}
/>
</div>
<div>
<Label>Clé API</Label>
<Input
className="mt-1 h-9"
type="password"
value={search.meilisearch_api_key}
disabled={meiliKeyLocked}
onChange={(e) => setSearch({ meilisearch_api_key: e.target.value })}
placeholder={meiliKeyLocked ? "Défini via MEILISEARCH_API_KEY" : undefined}
/>
</div>
</div>
) : null}
{suiteEngine === "typesense" ? (
<div className="grid gap-3 sm:grid-cols-2">
<div>
<Label>URL Typesense</Label>
<Input
className="mt-1 h-9"
value={typesenseURL}
disabled={typesenseURLLocked}
onChange={(e) => setSearch({ typesense_url: e.target.value })}
/>
</div>
<div>
<Label>Clé API</Label>
<Input
className="mt-1 h-9"
type="password"
value={search.typesense_api_key}
disabled={typesenseKeyLocked}
onChange={(e) => setSearch({ typesense_api_key: e.target.value })}
placeholder={typesenseKeyLocked ? "Défini via TYPESENSE_API_KEY" : undefined}
/>
</div>
</div>
) : null}
</CardContent>
</Card>
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-sm font-medium">Recherche web (Brave)</CardTitle>
<CardDescription>Utilisée pour l&apos;enrichissement IA des contacts.</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<label className="flex items-center justify-between gap-4 rounded-lg border p-3">
<div>
<p className="text-sm font-medium">Imposer la config organisation</p>
</div>
<Switch
checked={search.enforce_org_search}
onCheckedChange={(enforce_org_search) => setSearch({ enforce_org_search })}
/>
</label>
<div>
<Label>Token API Brave</Label>
<Input
className="mt-1 h-9"
type="password"
value={brave?.api_key ?? ""}
onChange={(e) =>
setSearch({
web_search: {
default_provider_id: "brave-default",
providers: [
{
id: "brave-default",
name: "Brave Search",
type: "brave",
api_key: e.target.value,
},
],
},
})
}
/>
</div>
</CardContent>
</Card>
</OrgSettingsSection>
)
}