feat: améliorations SharePoint - test connexion Azure AD, alerte expiration secret, log statut upload dans BapHistory
This commit is contained in:
@@ -18,6 +18,9 @@ import {
|
||||
User,
|
||||
Download,
|
||||
RefreshCw,
|
||||
Cloud,
|
||||
CloudOff,
|
||||
ExternalLink,
|
||||
} from "lucide-react";
|
||||
|
||||
// Helper : télécharge un PDF annoté BAP depuis son URL de stockage
|
||||
@@ -269,6 +272,31 @@ export default function BapHistory() {
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (entry as any).sharepointUploadStatus === 'success' ? (
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="flex items-center gap-1 text-blue-600">
|
||||
<Cloud className="h-3.5 w-3.5" />
|
||||
<span className="text-xs">SharePoint</span>
|
||||
</div>
|
||||
{(entry as any).sharepointUploadPath && (
|
||||
<a
|
||||
href={(entry as any).sharepointUploadPath}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-500 hover:text-blue-700"
|
||||
title="Ouvrir dans SharePoint"
|
||||
>
|
||||
<ExternalLink className="h-3.5 w-3.5" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
) : (entry as any).sharepointUploadStatus === 'error' ? (
|
||||
<div className="flex items-center gap-1" title={(entry as any).sharepointUploadError || 'Erreur SharePoint'}>
|
||||
<div className="flex items-center gap-1 text-red-600">
|
||||
<CloudOff className="h-3.5 w-3.5" />
|
||||
<span className="text-xs">SP Erreur</span>
|
||||
</div>
|
||||
</div>
|
||||
) : entry.exportPath ? (
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="flex items-center gap-1 text-orange-600">
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { toast } from "sonner";
|
||||
import { Loader2, Save, Upload, FolderOpen, Mail, Play, Square, Download, Inbox, CheckCircle2, Monitor, FolderOutput } from "lucide-react";
|
||||
import { Loader2, Save, Upload, FolderOpen, Mail, Play, Square, Download, Inbox, CheckCircle2, Monitor, FolderOutput, Wifi, WifiOff, AlertTriangle, Calendar } from "lucide-react";
|
||||
import DashboardLayout from "@/components/DashboardLayout";
|
||||
|
||||
export default function ImportSettings() {
|
||||
@@ -50,6 +50,8 @@ export default function ImportSettings() {
|
||||
const [azureTenantId, setAzureTenantId] = useState("");
|
||||
const [azureClientId, setAzureClientId] = useState("");
|
||||
const [azureClientSecret, setAzureClientSecret] = useState("");
|
||||
const [azureSecretExpiresAt, setAzureSecretExpiresAt] = useState("");
|
||||
const testAzureConnectionMutation = trpc.importSettings.testAzureConnection.useMutation();
|
||||
|
||||
// Initialize form with settings from database
|
||||
useEffect(() => {
|
||||
@@ -83,6 +85,8 @@ export default function ImportSettings() {
|
||||
setAzureTenantId((settings as any).azureTenantId || "");
|
||||
setAzureClientId((settings as any).azureClientId || "");
|
||||
setAzureClientSecret((settings as any).azureClientSecret || "");
|
||||
const expDate = (settings as any).azureSecretExpiresAt;
|
||||
setAzureSecretExpiresAt(expDate ? new Date(expDate).toISOString().split('T')[0] : "");
|
||||
}
|
||||
}, [settings]);
|
||||
|
||||
@@ -112,6 +116,7 @@ export default function ImportSettings() {
|
||||
azureTenantId: azureTenantId || null,
|
||||
azureClientId: azureClientId || null,
|
||||
azureClientSecret: azureClientSecret || null,
|
||||
azureSecretExpiresAt: azureSecretExpiresAt ? new Date(azureSecretExpiresAt) : null,
|
||||
});
|
||||
|
||||
toast.success("Paramètres enregistrés avec succès");
|
||||
@@ -680,6 +685,72 @@ export default function ImportSettings() {
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">Stocké de façon sécurisée. Permissions requises : <code>Files.ReadWrite.All</code> et <code>Sites.ReadWrite.All</code></p>
|
||||
</div>
|
||||
|
||||
{/* Date d'expiration du secret */}
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="azureSecretExpiresAt" className="text-sm font-medium flex items-center gap-1">
|
||||
<Calendar className="h-3.5 w-3.5" />
|
||||
Date d'expiration du secret
|
||||
</Label>
|
||||
<Input
|
||||
id="azureSecretExpiresAt"
|
||||
type="date"
|
||||
value={azureSecretExpiresAt}
|
||||
onChange={(e) => setAzureSecretExpiresAt(e.target.value)}
|
||||
className="h-10"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">Renseignez la date d'expiration pour recevoir une alerte avant renouvellement</p>
|
||||
</div>
|
||||
|
||||
{/* Alerte expiration */}
|
||||
{azureSecretExpiresAt && (() => {
|
||||
const expDate = new Date(azureSecretExpiresAt);
|
||||
const daysLeft = Math.ceil((expDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
||||
if (daysLeft <= 0) return (
|
||||
<div className="flex items-center gap-2 p-3 bg-red-50 border border-red-200 rounded-lg text-red-700 text-sm">
|
||||
<AlertTriangle className="h-4 w-4 shrink-0" />
|
||||
<span><strong>Secret expiré</strong> — renouvelez-le immédiatement dans le portail Azure AD</span>
|
||||
</div>
|
||||
);
|
||||
if (daysLeft <= 30) return (
|
||||
<div className="flex items-center gap-2 p-3 bg-amber-50 border border-amber-200 rounded-lg text-amber-700 text-sm">
|
||||
<AlertTriangle className="h-4 w-4 shrink-0" />
|
||||
<span><strong>Expiration dans {daysLeft} jours</strong> — pensez à renouveler le secret Azure AD</span>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="flex items-center gap-2 p-3 bg-green-50 border border-green-200 rounded-lg text-green-700 text-sm">
|
||||
<CheckCircle2 className="h-4 w-4 shrink-0" />
|
||||
<span>Secret valide encore <strong>{daysLeft} jours</strong> (expire le {expDate.toLocaleDateString('fr-FR')})</span>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Bouton test connexion */}
|
||||
<div className="pt-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={async () => {
|
||||
const result = await testAzureConnectionMutation.mutateAsync();
|
||||
if (result.success) {
|
||||
toast.success(result.message || 'Connexion Azure AD réussie');
|
||||
} else {
|
||||
toast.error(result.error || 'Erreur de connexion Azure AD');
|
||||
}
|
||||
}}
|
||||
disabled={testAzureConnectionMutation.isPending || !azureTenantId || !azureClientId || !azureClientSecret}
|
||||
className="gap-2"
|
||||
>
|
||||
{testAzureConnectionMutation.isPending ? (
|
||||
<><Loader2 className="h-4 w-4 animate-spin" />Test en cours...</>
|
||||
) : (
|
||||
<><Wifi className="h-4 w-4" />Tester la connexion Azure AD</>
|
||||
)}
|
||||
</Button>
|
||||
<p className="text-xs text-muted-foreground mt-1">Vérifie le token OAuth2 et l'accès au site SharePoint</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user