ultisuite-client/components/admin/settings/sections/search-section.tsx
R3D347HR4Y 9e9fd208ad
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat(admin-settings): enhance admin settings with new components and layout improvements
- Introduced new components for managing admin settings, including AdminListControls, AdminSettingsCard, and TechBrandSelectLabel.
- Implemented dynamic loading for admin settings sections to optimize performance.
- Enhanced the layout of various admin settings sections for better user experience.
- Updated the AiAssistantSection to include LLM provider management and improved model selection.
- Refactored authentication settings to streamline configuration and improve accessibility.
2026-06-15 00:22:20 +02:00

191 lines
7.9 KiB
TypeScript

"use client"
import { OrgSettingsSection } from "@/components/admin/settings/org-settings-form"
import { AdminSettingsCard } from "@/components/admin/settings/admin-settings-card"
import { DeployLockedHint, useDeployFieldLocked } from "@/components/admin/settings/deploy-locked-hint"
import { FieldGroup } from "@/components/admin/settings/field-group"
import { AutomationTabMasonry } from "@/components/gmail/settings/automation/automation-tab-masonry"
import { WebSearchProvidersEditor } from "@/components/web-search/web-search-providers-editor"
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 { normalizeSearchProviders } from "@/lib/web-search/search-provider-catalog"
import { TechBrandSelectLabel } from "@/components/admin/settings/tech-brand-select-label"
export function SearchSection() {
const search = useOrgSettingsStore((s) => s.search)
const setSearch = useOrgSettingsStore((s) => s.setSearch)
const effective = useOrgSettingsStore((s) => s.meta?.effective.search)
const providerSecrets = useOrgSettingsStore((s) => s.meta?.secrets?.web_search_providers)
const webSearch = normalizeSearchProviders(search.web_search)
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 (contacts, UltiAI)."
policySection="search"
>
<AutomationTabMasonry columns={2}>
<AdminSettingsCard
title="Recherche web"
description={
<>
Fournisseurs pour l&apos;enrichissement IA contacts et le tool UltiAI{" "}
<code className="rounded bg-muted px-1">web_search</code>. Les utilisateurs peuvent
surcharger cette config dans leurs réglages si l&apos;imposition org. est désactivée.
</>
}
>
<label className="flex items-center justify-between gap-4 rounded-lg border p-3">
<FieldGroup>
<p className="text-sm font-medium">Imposer la config organisation</p>
<p className="text-xs text-muted-foreground">
Sinon, chaque utilisateur configure ses propres fournisseurs.
</p>
</FieldGroup>
<Switch
checked={search.enforce_org_search}
onCheckedChange={(enforce_org_search) => setSearch({ enforce_org_search })}
/>
</label>
<WebSearchProvidersEditor
value={webSearch}
onChange={(web_search) => setSearch({ web_search })}
providerSecrets={providerSecrets}
columns={1}
/>
</AdminSettingsCard>
<AdminSettingsCard
title="Recherche suite"
description="Moteur d'indexation pour la recherche globale (variables SEARCH_ENGINE côté serveur)."
hint={engineLocked ? <DeployLockedHint section="search" field="suite_engine" /> : null}
>
<FieldGroup>
<Label>Moteur</Label>
<Select
value={suiteEngine}
disabled={engineLocked}
onValueChange={(suite_engine) =>
setSearch({
suite_engine: suite_engine as typeof search.suite_engine,
})
}
>
<SelectTrigger className="h-9 w-full min-w-0">
<SelectValue>
<TechBrandSelectLabel brand={suiteEngine}>
{suiteEngine === "postgres"
? "PostgreSQL (full-text)"
: suiteEngine === "meilisearch"
? "Meilisearch"
: "Typesense"}
</TechBrandSelectLabel>
</SelectValue>
</SelectTrigger>
<SelectContent>
<SelectItem value="postgres">
<TechBrandSelectLabel brand="postgres">PostgreSQL (full-text)</TechBrandSelectLabel>
</SelectItem>
<SelectItem value="meilisearch">
<TechBrandSelectLabel brand="meilisearch">Meilisearch</TechBrandSelectLabel>
</SelectItem>
<SelectItem value="typesense">
<TechBrandSelectLabel brand="typesense">Typesense</TechBrandSelectLabel>
</SelectItem>
</SelectContent>
</Select>
</FieldGroup>
{suiteEngine === "meilisearch" ? (
<div className="grid min-w-0 gap-4">
<FieldGroup>
<Label>URL Meilisearch</Label>
<Input
className="h-9"
value={meiliURL}
disabled={meiliURLLocked}
onChange={(e) => setSearch({ meilisearch_url: e.target.value })}
/>
{meiliURLLocked ? (
<DeployLockedHint section="search" field="meilisearch_url" />
) : null}
</FieldGroup>
<FieldGroup>
<Label>Clé API</Label>
<Input
className="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}
/>
{meiliKeyLocked ? (
<DeployLockedHint section="search" field="meilisearch_api_key" />
) : null}
</FieldGroup>
</div>
) : null}
{suiteEngine === "typesense" ? (
<div className="grid min-w-0 gap-4">
<FieldGroup>
<Label>URL Typesense</Label>
<Input
className="h-9"
value={typesenseURL}
disabled={typesenseURLLocked}
onChange={(e) => setSearch({ typesense_url: e.target.value })}
/>
{typesenseURLLocked ? (
<DeployLockedHint section="search" field="typesense_url" />
) : null}
</FieldGroup>
<FieldGroup>
<Label>Clé API</Label>
<Input
className="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}
/>
{typesenseKeyLocked ? (
<DeployLockedHint section="search" field="typesense_api_key" />
) : null}
</FieldGroup>
</div>
) : null}
</AdminSettingsCard>
</AutomationTabMasonry>
</OrgSettingsSection>
)
}