Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Introduced new ContactAvatar and ContactAvatarPicker components for enhanced avatar management in contact views. - Updated ContactDetailView and ContactFormView to utilize the new avatar components, improving user experience when adding or editing contacts. - Enhanced ContactHoverCard and ContactRow components to display avatars, providing a more visually appealing interface. - Added loading and error states in ContactsListView for better user feedback during data fetching. - Implemented a new ContactsLoadState component to handle loading and error scenarios in the contacts list. - Updated package.json to include @formkit/auto-animate for improved UI animations.
124 lines
3.9 KiB
TypeScript
124 lines
3.9 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import { ExternalLink } from "lucide-react"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Input } from "@/components/ui/input"
|
|
import { Label } from "@/components/ui/label"
|
|
import {
|
|
useSearchSettings,
|
|
useUpdateSearchSettings,
|
|
} from "@/lib/api/hooks/use-contact-discovery"
|
|
import type { ApiSearchProvider, ApiSearchSettings } from "@/lib/contacts/discovery-types"
|
|
import {
|
|
CONTACTS_MUTED_TEXT,
|
|
CONTACTS_PRIMARY_BTN_CLASS,
|
|
} from "@/lib/contacts-chrome-classes"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
const BRAVE_PROVIDER_ID = "brave-default"
|
|
|
|
function defaultBraveProvider(): ApiSearchProvider {
|
|
return {
|
|
id: BRAVE_PROVIDER_ID,
|
|
name: "Brave Search",
|
|
type: "brave",
|
|
api_key: "",
|
|
}
|
|
}
|
|
|
|
function normalizeDraft(raw: ApiSearchSettings | undefined): ApiSearchSettings {
|
|
const providers = raw?.providers?.length ? raw.providers : [defaultBraveProvider()]
|
|
const brave = providers.find((p) => p.type === "brave") ?? defaultBraveProvider()
|
|
return {
|
|
default_provider_id: raw?.default_provider_id || brave.id,
|
|
providers: [brave],
|
|
}
|
|
}
|
|
|
|
export function SearchProvidersPanel() {
|
|
const { data: remote, isLoading } = useSearchSettings()
|
|
const updateSettings = useUpdateSearchSettings()
|
|
const [draft, setDraft] = useState<ApiSearchSettings>(normalizeDraft(undefined))
|
|
const [saved, setSaved] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (remote) {
|
|
setDraft(normalizeDraft(remote))
|
|
}
|
|
}, [remote])
|
|
|
|
const brave = draft.providers[0] ?? defaultBraveProvider()
|
|
|
|
function updateBrave(patch: Partial<ApiSearchProvider>) {
|
|
setDraft((prev) => {
|
|
const current = prev.providers[0] ?? defaultBraveProvider()
|
|
const updated = { ...current, ...patch }
|
|
return {
|
|
default_provider_id: updated.id,
|
|
providers: [updated],
|
|
}
|
|
})
|
|
}
|
|
|
|
async function handleSave() {
|
|
await updateSettings.mutateAsync(draft)
|
|
setSaved(true)
|
|
setTimeout(() => setSaved(false), 2000)
|
|
}
|
|
|
|
if (isLoading) {
|
|
return <p className={cn("text-sm", CONTACTS_MUTED_TEXT)}>Chargement…</p>
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-2xl space-y-6">
|
|
<div>
|
|
<h3 className="text-base font-medium">Fournisseurs de recherche</h3>
|
|
<p className={cn("mt-1 text-sm", CONTACTS_MUTED_TEXT)}>
|
|
Recherche web utilisée lors de l'amélioration IA des fiches contacts (profils
|
|
publics, réseaux sociaux, poste, entreprise).
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-3 rounded-lg border border-border p-4">
|
|
<div className="flex items-center justify-between gap-2">
|
|
<span className="text-sm font-medium">{brave.name}</span>
|
|
<a
|
|
href="https://api.search.brave.com"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className={cn("inline-flex items-center gap-1 text-xs hover:underline", CONTACTS_MUTED_TEXT)}
|
|
>
|
|
Obtenir une clé API
|
|
<ExternalLink className="h-3 w-3" />
|
|
</a>
|
|
</div>
|
|
<div>
|
|
<Label className="text-xs">Token API (X-Subscription-Token)</Label>
|
|
<Input
|
|
className="mt-1 h-9"
|
|
type="password"
|
|
value={brave.api_key ?? ""}
|
|
onChange={(e) => updateBrave({ api_key: e.target.value })}
|
|
placeholder="BSA…"
|
|
autoComplete="off"
|
|
/>
|
|
<p className={cn("mt-1.5 text-xs", CONTACTS_MUTED_TEXT)}>
|
|
Les 5 premiers résultats web sont ajoutés au prompt LLM avec un avertissement sur les
|
|
homonymes. Sans token, l'amélioration IA fonctionne sans recherche en ligne.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Button
|
|
onClick={handleSave}
|
|
disabled={updateSettings.isPending}
|
|
className={CONTACTS_PRIMARY_BTN_CLASS}
|
|
>
|
|
{updateSettings.isPending ? "Enregistrement…" : saved ? "Enregistré ✓" : "Enregistrer"}
|
|
</Button>
|
|
</div>
|
|
)
|
|
}
|