"use client"; import * as React from "react"; import { toast } from "sonner"; import { Loader2, Save, Mail, FolderSearch, Server, Wifi, WifiOff, Check, RefreshCw, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardHeader, CardTitle, CardDescription, } from "@/components/ui/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; type SourceSettings = { emailImapHost: string; emailImapPort: number; emailImapUser: string; emailImapPass: string; emailImapTls: boolean; emailFolder: string; emailWatching: boolean; emailProcessed: string; emailProcessedFolder: string; ftpEnabled: boolean; ftpHost: string; ftpPort: number; ftpUser: string; ftpPass: string; ftpTls: boolean; ftpIncomingDir: string; ftpProcessedDir: string; watchDir: string; watching: boolean; }; export default function UploadSourcesPage() { const [settings, setSettings] = React.useState({ emailImapHost: "", emailImapPort: 993, emailImapUser: "", emailImapPass: "", emailImapTls: true, emailFolder: "INBOX", emailWatching: false, emailProcessed: "mark_read", emailProcessedFolder: "Processed", ftpEnabled: false, ftpHost: "", ftpPort: 21, ftpUser: "", ftpPass: "", ftpTls: true, ftpIncomingDir: "/incoming", ftpProcessedDir: "/processed", watchDir: "", watching: false, }); const [loading, setLoading] = React.useState(true); const [saving, setSaving] = React.useState(false); const [emailTestStatus, setEmailTestStatus] = React.useState< "idle" | "testing" | "success" | "error" >("idle"); const [ftpTestStatus, setFtpTestStatus] = React.useState< "idle" | "testing" | "success" | "error" >("idle"); const [scanning, setScanning] = React.useState(false); React.useEffect(() => { fetch("/api/settings") .then((r) => r.json()) .then((data) => { setSettings({ emailImapHost: data.emailImapHost || "", emailImapPort: data.emailImapPort || 993, emailImapUser: data.emailImapUser || "", emailImapPass: data.emailImapPass || "", emailImapTls: data.emailImapTls ?? true, emailFolder: data.emailFolder || "INBOX", emailWatching: data.emailWatching || false, emailProcessed: data.emailProcessed || "mark_read", emailProcessedFolder: data.emailProcessedFolder || "Processed", ftpEnabled: data.ftpEnabled || false, ftpHost: data.ftpHost || "", ftpPort: data.ftpPort || 21, ftpUser: data.ftpUser || "", ftpPass: data.ftpPass || "", ftpTls: data.ftpTls ?? true, ftpIncomingDir: data.ftpIncomingDir || "/incoming", ftpProcessedDir: data.ftpProcessedDir || "/processed", watchDir: data.watchDir || "", watching: data.watching || false, }); setLoading(false); }) .catch(() => setLoading(false)); }, []); const handleSave = async () => { setSaving(true); try { const payload = { ...settings } as Record; if (payload.emailImapPass === "••••••••") delete payload.emailImapPass; if (payload.ftpPass === "••••••••") delete payload.ftpPass; const res = await fetch("/api/settings", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (!res.ok) throw new Error(); toast.success("Upload source settings saved"); } catch { toast.error("Failed to save settings"); } finally { setSaving(false); } }; const testEmail = async () => { setEmailTestStatus("testing"); try { const res = await fetch("/api/email-watch/test", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ host: settings.emailImapHost, port: settings.emailImapPort, user: settings.emailImapUser, pass: settings.emailImapPass, tls: settings.emailImapTls, }), }); const data = await res.json(); setEmailTestStatus(res.ok ? "success" : "error"); if (res.ok) toast.success(data.message || "Email connection successful"); else toast.error(data.error || "Email connection failed"); } catch { setEmailTestStatus("error"); toast.error("Email test failed"); } }; const scanNow = async () => { setScanning(true); try { const res = await fetch("/api/email-watch", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "scan" }), }); if (res.ok) { const data = await res.json(); toast.success(`Scanned ${data.processed || 0} emails`); } else { const data = await res.json().catch(() => ({})); toast.error(data.error || "Scan failed"); } } catch { toast.error("Scan failed"); } finally { setScanning(false); } }; const testFtp = async () => { setFtpTestStatus("testing"); try { const res = await fetch("/api/ftp-watch/test", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ host: settings.ftpHost, port: settings.ftpPort, user: settings.ftpUser, pass: settings.ftpPass, tls: settings.ftpTls, incomingDir: settings.ftpIncomingDir, }), }); const data = await res.json(); setFtpTestStatus(res.ok && data.ok ? "success" : "error"); if (res.ok && data.ok) { toast.success(`FTP connected — ${data.files ?? 0} file(s) found`); } else { toast.error(data.error || "FTP connection failed"); } } catch { setFtpTestStatus("error"); toast.error("FTP test failed"); } }; const toggleFolderWatch = async () => { const endpoint = settings.watching ? "/api/watch/stop" : "/api/watch/start"; try { const res = await fetch(endpoint, { method: "POST" }); if (res.ok) { setSettings((s) => ({ ...s, watching: !s.watching })); toast.success( settings.watching ? "Folder watch stopped" : "Folder watch started" ); } } catch { toast.error("Failed to toggle folder watch"); } }; if (loading) { return (
); } return (
{/* Email Inbox */} Email Inbox Auto-import response cards from an email inbox via IMAP.
setSettings((s) => ({ ...s, emailImapHost: e.target.value })) } placeholder="imap.gmail.com" />
setSettings((s) => ({ ...s, emailImapPort: parseInt(e.target.value) || 993, })) } />
setSettings((s) => ({ ...s, emailImapUser: e.target.value })) } />
setSettings((s) => ({ ...s, emailImapPass: e.target.value })) } />
setSettings((s) => ({ ...s, emailImapTls: v })) } />
setSettings((s) => ({ ...s, emailFolder: e.target.value })) } />
setSettings((s) => ({ ...s, emailWatching: v })) } />
{/* FTP */} FTP Server Poll an FTP server for new scanned files. Files are downloaded, processed, then moved to a “processed” directory.
setSettings((s) => ({ ...s, ftpEnabled: !!v })) } />
setSettings((s) => ({ ...s, ftpHost: e.target.value })) } placeholder="ftp.example.com" />
setSettings((s) => ({ ...s, ftpPort: parseInt(e.target.value) || 21, })) } />
setSettings((s) => ({ ...s, ftpUser: e.target.value })) } />
setSettings((s) => ({ ...s, ftpPass: e.target.value })) } />
setSettings((s) => ({ ...s, ftpTls: !!v })) } />
setSettings((s) => ({ ...s, ftpIncomingDir: e.target.value })) } placeholder="/incoming" />
setSettings((s) => ({ ...s, ftpProcessedDir: e.target.value, })) } placeholder="/processed" />
{/* Folder Watch */} Folder Watch Monitor a local directory for new scanned files.
setSettings((s) => ({ ...s, watchDir: e.target.value })) } placeholder="/mnt/scans" />
{settings.watching && ( Active )}
); }