diff --git a/client/src/pages/ImportSettings.tsx b/client/src/pages/ImportSettings.tsx index ae94326..1499df4 100644 --- a/client/src/pages/ImportSettings.tsx +++ b/client/src/pages/ImportSettings.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useMemo } from "react"; import { trpc } from "@/lib/trpc"; import AzureAdExportSettingsCard from "@/components/AzureAdExportSettingsCard"; import { Button } from "@/components/ui/button"; @@ -8,6 +8,7 @@ import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { Badge } from "@/components/ui/badge"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { useAuth } from "@/_core/hooks/useAuth"; import { toast } from "sonner"; import { Loader2, Save, Upload, FolderOpen, Mail, Play, Square, Download, @@ -153,6 +154,8 @@ function BackupSection() { export default function ImportSettings() { + const { user } = useAuth(); + const isAdmin = user?.role === "admin"; // Chargement différé : on ne charge les statuts de services qu'après le montage const { data: settings, isLoading } = trpc.importSettings.get.useQuery(undefined, { staleTime: 30_000, @@ -173,6 +176,18 @@ export default function ImportSettings() { const testAzureConnectionMutation = trpc.importSettings.testAzureConnection.useMutation(); const testSharePointUploadMutation = trpc.importSettings.testSharePointUpload.useMutation(); const testEmailConnectionMutation = trpc.importSettings.testEmailConnection.useMutation(); + const { data: emailAccounts, isLoading: emailAccountsLoading } = trpc.emailImportService.getConfiguredAccounts.useQuery(undefined, { + enabled: isAdmin, + staleTime: 30_000, + }); + const [selectedEmailAccountId, setSelectedEmailAccountId] = useState(""); + const selectedEmailAccountUserId = Number(selectedEmailAccountId); + const { data: selectedEmailAccountStatus } = trpc.emailImportService.accountStatus.useQuery( + { userId: selectedEmailAccountUserId }, + { enabled: isAdmin && Number.isInteger(selectedEmailAccountUserId) && selectedEmailAccountUserId > 0 }, + ); + const startEmailAccountMutation = trpc.emailImportService.startAccount.useMutation(); + const stopEmailAccountMutation = trpc.emailImportService.stopAccount.useMutation(); // Manual import const [manualImportEnabled, setManualImportEnabled] = useState(true); @@ -257,6 +272,22 @@ export default function ImportSettings() { } }, [settings]); + const configuredEmailAccounts = useMemo( + () => (emailAccounts ?? []).filter((account) => account.isConfigured === 1), + [emailAccounts], + ); + + useEffect(() => { + if (!isAdmin || configuredEmailAccounts.length === 0) { + setSelectedEmailAccountId(""); + return; + } + + if (!configuredEmailAccounts.some((account) => String(account.userId) === selectedEmailAccountId)) { + setSelectedEmailAccountId(String(configuredEmailAccounts[0].userId)); + } + }, [isAdmin, configuredEmailAccounts, selectedEmailAccountId]); + const computedExportMode = (): "browser" | "folder" | "both" => { if (bapExportBrowser && bapExportFolder) return "both"; if (bapExportFolder) return "folder"; @@ -537,8 +568,9 @@ export default function ImportSettings() { /> - {emailImportEnabled && ( -
+
+ {emailImportEnabled && ( + <> {/* Mode d'authentification */}
@@ -568,7 +600,7 @@ export default function ImportSettings() {
{emailImportAuthMode === "oauth2" && (
- Office 365 : Microsoft a désactivé l'auth basique pour Exchange Online. Utilisez ce mode avec les credentials Azure AD déjà configurés dans l'onglet Export. + Office 365 : Microsoft a désactivé l'auth basique pour Exchange Online. Utilisez ce mode avec les paramètres Azure AD configurés dans la carte Microsoft Azure AD au début de cette page.
)}
@@ -600,7 +632,7 @@ export default function ImportSettings() { {emailImportAuthMode === "oauth2" && (
- En mode OAuth2, les credentials Azure AD (Tenant ID, Client ID, Client Secret) configurés dans l'onglet Export > SharePoint seront utilisés automatiquement. + En mode OAuth2, les paramètres Azure AD (Tenant ID, Client ID, Client Secret) configurés dans la carte Microsoft Azure AD seront utilisés automatiquement.
)}
@@ -741,12 +773,12 @@ export default function ImportSettings() { const result = await startEmailServiceMutation.mutateAsync(); if (result.success) { await utils.emailImportService.status.invalidate(); - toast.success("Service démarré"); + toast.success(result.message); } else { - toast.error("Impossible de démarrer le service"); + toast.error(result.message || "Impossible de démarrer le service", { duration: 8000 }); } - } catch { - toast.error("Impossible de démarrer le service"); + } catch (error: any) { + toast.error(error?.message || "Impossible de démarrer le service", { duration: 8000 }); } }} disabled={!emailImportEnabled || startEmailServiceMutation.isPending} @@ -757,8 +789,82 @@ export default function ImportSettings() { )}
+

+ Enregistrez les paramètres avant de démarrer. Le service ne démarre que si le test IMAP est concluant. +

+ + )} + + {isAdmin && ( +
+
+

Compte source administré

+

+ Démarrez la boîte déjà configurée, même si ses paramètres appartiennent à un autre compte applicatif. Aucun identifiant n’est affiché ni recopié. +

+
+
+ + {selectedEmailAccountStatus?.isRunning ? ( + + ) : ( + + )} +
+
+ )}
- )} diff --git a/drizzle/0036_broken_rattler.sql b/drizzle/0036_broken_rattler.sql index a98f2aa..ab22549 100644 --- a/drizzle/0036_broken_rattler.sql +++ b/drizzle/0036_broken_rattler.sql @@ -1,4 +1,4 @@ -CREATE TABLE `webImportSources` ( +CREATE TABLE IF NOT EXISTS `webImportSources` ( `id` int AUTO_INCREMENT NOT NULL, `userId` int NOT NULL, `name` varchar(100) NOT NULL, diff --git a/todo.md b/todo.md index 837f406..93854bb 100644 --- a/todo.md +++ b/todo.md @@ -956,3 +956,9 @@ - [x] Résoudre une destination par facture d’abonnement sélectionnée selon les règles actives - [x] Préserver les paramètres globaux comme repli lorsqu’aucune règle ne correspond - [x] Ajouter les tests de priorité et de regroupement des exports d’abonnements + +## Déploiement des règles d’export d’abonnements +- [x] Synchroniser la version validée et reconstruire le service applicatif de recette +- [x] Vérifier en recette les routes, le conteneur, le volume PDF et le flux de résolution par facture +- [x] Synchroniser la même version et reconstruire le service applicatif de production +- [x] Vérifier en production les routes, le conteneur, le volume PDF et le dashboard associé