Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- 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.
178 lines
5.7 KiB
TypeScript
178 lines
5.7 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import { Check, Copy } from "lucide-react"
|
|
import { toast } from "sonner"
|
|
import { TechBrandSelectLabel } from "@/components/admin/settings/tech-brand-select-label"
|
|
import {
|
|
SettingsCard,
|
|
SettingsField,
|
|
SettingsGrid,
|
|
SettingsHint,
|
|
SettingsToggleRow,
|
|
} from "@/components/settings/settings-kit"
|
|
import { useOrgSettingsStore } from "@/lib/admin-settings/org-settings-store"
|
|
import type { DriveMountOAuthProvider, DriveMountOAuthSettings } from "@/lib/admin-settings/org-settings-types"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Input } from "@/components/ui/input"
|
|
import { buildDriveMountOAuthRedirectURI } from "@/lib/drive/drive-mount-oauth"
|
|
|
|
const PROVIDERS: {
|
|
id: DriveMountOAuthProvider
|
|
label: string
|
|
hint: string
|
|
icon: string
|
|
}[] = [
|
|
{
|
|
id: "google",
|
|
label: "Google Drive",
|
|
hint: "Console Google Cloud — API Drive, redirect URI ci-dessous",
|
|
icon: "logos:google-drive",
|
|
},
|
|
{
|
|
id: "dropbox",
|
|
label: "Dropbox",
|
|
hint: "App Dropbox — permissions files.metadata.read, files.content.read/write",
|
|
icon: "logos:dropbox",
|
|
},
|
|
{
|
|
id: "microsoft",
|
|
label: "Microsoft OneDrive",
|
|
hint: "Azure AD — Microsoft Graph Files.ReadWrite",
|
|
icon: "logos:microsoft-onedrive",
|
|
},
|
|
]
|
|
|
|
const SECRET_KEYS: Record<DriveMountOAuthProvider, "mount_oauth_google" | "mount_oauth_dropbox" | "mount_oauth_microsoft"> = {
|
|
google: "mount_oauth_google",
|
|
dropbox: "mount_oauth_dropbox",
|
|
microsoft: "mount_oauth_microsoft",
|
|
}
|
|
|
|
export function DriveMountOAuthSection({
|
|
draft,
|
|
onChange,
|
|
embedded = false,
|
|
}: {
|
|
draft: DriveMountOAuthSettings
|
|
onChange: (next: DriveMountOAuthSettings) => void
|
|
embedded?: boolean
|
|
}) {
|
|
const secrets = useOrgSettingsStore((s) => s.meta?.secrets)
|
|
const [redirectUri, setRedirectUri] = useState("")
|
|
const [copied, setCopied] = useState(false)
|
|
|
|
useEffect(() => {
|
|
setRedirectUri(buildDriveMountOAuthRedirectURI())
|
|
}, [])
|
|
|
|
const updateProvider = (provider: DriveMountOAuthProvider, patch: Partial<DriveMountOAuthSettings[typeof provider]>) => {
|
|
onChange({
|
|
...draft,
|
|
[provider]: { ...draft[provider], ...patch },
|
|
})
|
|
}
|
|
|
|
const copyRedirectUri = async () => {
|
|
const uri = redirectUri || buildDriveMountOAuthRedirectURI()
|
|
try {
|
|
await navigator.clipboard.writeText(uri)
|
|
setCopied(true)
|
|
toast.success("URI de redirection copiée")
|
|
window.setTimeout(() => setCopied(false), 2000)
|
|
} catch {
|
|
toast.error("Impossible de copier l'URI")
|
|
}
|
|
}
|
|
|
|
const content = (
|
|
<>
|
|
<SettingsField
|
|
label="URI de redirection OAuth"
|
|
hint="Basée sur l'URL actuelle du navigateur. Enregistrez-la chez chaque fournisseur OAuth (Google, Dropbox, Microsoft)."
|
|
>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
className="h-9 flex-1 font-mono text-xs"
|
|
readOnly
|
|
value={redirectUri}
|
|
placeholder="Chargement…"
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
className="h-9 shrink-0 gap-1.5 px-3"
|
|
onClick={() => void copyRedirectUri()}
|
|
disabled={!redirectUri}
|
|
>
|
|
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
|
Copier
|
|
</Button>
|
|
</div>
|
|
</SettingsField>
|
|
<div className="space-y-4">
|
|
{PROVIDERS.map(({ id, label, hint, icon }) => {
|
|
const provider = draft[id]
|
|
const configured = Boolean(secrets?.[SECRET_KEYS[id]]?.configured)
|
|
return (
|
|
<div key={id} className="space-y-3 rounded-md border border-mail-border bg-mail-surface-muted/40 p-3">
|
|
<SettingsToggleRow
|
|
variant="plain"
|
|
title={
|
|
<TechBrandSelectLabel icon={icon} className="text-sm font-medium">
|
|
{label}
|
|
</TechBrandSelectLabel>
|
|
}
|
|
description={hint}
|
|
checked={provider.enabled}
|
|
onCheckedChange={(enabled) => updateProvider(id, { enabled })}
|
|
/>
|
|
{provider.enabled ? (
|
|
<SettingsGrid columns={1}>
|
|
<SettingsField label="Client ID">
|
|
<Input
|
|
className="h-9 font-mono text-xs"
|
|
value={provider.client_id}
|
|
onChange={(e) => updateProvider(id, { client_id: e.target.value })}
|
|
autoComplete="off"
|
|
/>
|
|
</SettingsField>
|
|
<SettingsField
|
|
label="Client secret"
|
|
hint={
|
|
configured && !provider.client_secret.trim() ? (
|
|
<SettingsHint>Secret configuré</SettingsHint>
|
|
) : undefined
|
|
}
|
|
>
|
|
<Input
|
|
className="h-9 font-mono text-xs"
|
|
type="password"
|
|
value={provider.client_secret}
|
|
onChange={(e) => updateProvider(id, { client_secret: e.target.value })}
|
|
placeholder={configured ? "•••••••• (laisser vide pour conserver)" : "Coller le secret"}
|
|
autoComplete="off"
|
|
/>
|
|
</SettingsField>
|
|
</SettingsGrid>
|
|
) : null}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
</>
|
|
)
|
|
|
|
if (embedded) return <div className="space-y-4">{content}</div>
|
|
|
|
return (
|
|
<SettingsCard
|
|
title="Connexion cloud (OAuth)"
|
|
description="Permet aux utilisateurs de monter Google Drive, Dropbox ou OneDrive depuis UltiDrive."
|
|
>
|
|
{content}
|
|
</SettingsCard>
|
|
)
|
|
}
|