ultisuite-client/components/admin/settings/sections/search-section.tsx
R3D347HR4Y 8f81d7aba1
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat(admin-settings): refactor admin settings components for improved usability and consistency
- Replaced legacy components with new `SettingsCard`, `SettingsField`, and `SettingsToggleRow` for a unified design.
- Enhanced `AdminListControls` to support compact mode and improved pagination controls.
- Updated various sections including `AiAssistantSection`, `AuthenticationSection`, and `DriveMountOAuthSection` to utilize new components, streamlining the settings interface.
- Improved accessibility and user experience across admin settings with clearer labels and hints.
- Deprecated old components while maintaining backward compatibility for existing admin sections.
2026-06-15 11:10:17 +02:00

198 lines
7.7 KiB
TypeScript

"use client"
import { OrgSettingsSection } from "@/components/admin/settings/org-settings-form"
import {
SettingsCard,
SettingsField,
SettingsGrid,
SettingsToggleRow,
} from "@/components/settings/settings-kit"
import { DeployLockedHint, useDeployFieldLocked } from "@/components/admin/settings/deploy-locked-hint"
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 {
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}>
<SettingsCard
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.
</>
}
>
<SettingsToggleRow
title="Imposer la config organisation"
description="Sinon, chaque utilisateur configure ses propres fournisseurs."
checked={search.enforce_org_search}
onCheckedChange={(enforce_org_search) => setSearch({ enforce_org_search })}
/>
<WebSearchProvidersEditor
value={webSearch}
onChange={(web_search) => setSearch({ web_search })}
providerSecrets={providerSecrets}
columns={1}
/>
</SettingsCard>
<SettingsCard
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}
>
<SettingsField label="Moteur">
<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>
</SettingsField>
{suiteEngine === "meilisearch" ? (
<SettingsGrid columns={1}>
<SettingsField
label="URL Meilisearch"
hint={
meiliURLLocked ? (
<DeployLockedHint section="search" field="meilisearch_url" />
) : undefined
}
>
<Input
className="h-9"
value={meiliURL}
disabled={meiliURLLocked}
onChange={(e) => setSearch({ meilisearch_url: e.target.value })}
/>
</SettingsField>
<SettingsField
label="Clé API"
hint={
meiliKeyLocked ? (
<DeployLockedHint section="search" field="meilisearch_api_key" />
) : undefined
}
>
<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}
/>
</SettingsField>
</SettingsGrid>
) : null}
{suiteEngine === "typesense" ? (
<SettingsGrid columns={1}>
<SettingsField
label="URL Typesense"
hint={
typesenseURLLocked ? (
<DeployLockedHint section="search" field="typesense_url" />
) : undefined
}
>
<Input
className="h-9"
value={typesenseURL}
disabled={typesenseURLLocked}
onChange={(e) => setSearch({ typesense_url: e.target.value })}
/>
</SettingsField>
<SettingsField
label="Clé API"
hint={
typesenseKeyLocked ? (
<DeployLockedHint section="search" field="typesense_api_key" />
) : undefined
}
>
<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}
/>
</SettingsField>
</SettingsGrid>
) : null}
</SettingsCard>
</AutomationTabMasonry>
</OrgSettingsSection>
)
}