react
This commit is contained in:
130
frontend/src/components/MailboxSettingsModal.jsx
Normal file
130
frontend/src/components/MailboxSettingsModal.jsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { FiCornerUpRight, FiCalendar, FiSlash } from 'react-icons/fi';
|
||||
import Modal from './Modal';
|
||||
import LoadingOverlay from './LoadingOverlay';
|
||||
import Forwarding from './Forwarding';
|
||||
import OutOfOffice from './OutOfOffice';
|
||||
import BlockedSenders from './BlockedSenders';
|
||||
import { mailboxesAPI } from '../services/api';
|
||||
|
||||
const TABS = [
|
||||
{ id: 'fwd', label: 'Forwarding', icon: FiCornerUpRight },
|
||||
{ id: 'ooo', label: 'Out of Office', icon: FiCalendar },
|
||||
{ id: 'block', label: 'Blocklist', icon: FiSlash },
|
||||
];
|
||||
|
||||
const MailboxSettingsModal = ({ open, email, initialTab = 'fwd', onClose, onToast }) => {
|
||||
const [activeTab, setActiveTab] = useState(initialTab);
|
||||
const [rule, setRule] = useState(null);
|
||||
const [blocklist, setBlocklist] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => { setActiveTab(initialTab); }, [initialTab, email]);
|
||||
|
||||
// Load both /rules and /blocklist in parallel when the modal opens.
|
||||
useEffect(() => {
|
||||
if (!open || !email) return;
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [r, b] = await Promise.all([
|
||||
mailboxesAPI.getRules(email).catch(() => ({
|
||||
email_address: email, ooo_active: false, ooo_message: '', forwards: [],
|
||||
})),
|
||||
mailboxesAPI.getBlocklist(email).catch(() => ({
|
||||
email_address: email, blocked_patterns: [],
|
||||
})),
|
||||
]);
|
||||
if (cancelled) return;
|
||||
setRule(r || { email_address: email, ooo_active: false, ooo_message: '', forwards: [] });
|
||||
setBlocklist(b || { email_address: email, blocked_patterns: [] });
|
||||
} catch (err) {
|
||||
if (!cancelled) onToast?.(`Failed to load settings: ${err.message}`, 'error');
|
||||
} finally {
|
||||
if (!cancelled) setLoading(false);
|
||||
}
|
||||
})();
|
||||
return () => { cancelled = true; };
|
||||
}, [open, email, onToast]);
|
||||
|
||||
// Merge updates with existing rule and persist.
|
||||
const saveRule = async (updates) => {
|
||||
const merged = {
|
||||
ooo_active: rule?.ooo_active ?? false,
|
||||
ooo_message: rule?.ooo_message ?? '',
|
||||
forwards: rule?.forwards ?? [],
|
||||
...updates,
|
||||
};
|
||||
try {
|
||||
const saved = await mailboxesAPI.putRules(email, merged);
|
||||
setRule({ email_address: email, ...merged, ...saved });
|
||||
onToast?.('Rule saved', 'success');
|
||||
} catch (err) {
|
||||
onToast?.(`Failed to save: ${err.message}`, 'error');
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const saveBlocklist = async (patterns) => {
|
||||
try {
|
||||
const saved = await mailboxesAPI.putBlocklist(email, patterns);
|
||||
setBlocklist({ email_address: email, blocked_patterns: patterns, ...saved });
|
||||
onToast?.('Block list saved', 'success');
|
||||
} catch (err) {
|
||||
onToast?.(`Failed to save: ${err.message}`, 'error');
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
title={email || 'Mailbox settings'}
|
||||
subtitle="Forwarding, auto-reply and blocklist"
|
||||
size="md"
|
||||
>
|
||||
<div className="relative min-h-[400px]">
|
||||
{/* Tabs */}
|
||||
<div className="border-b border-gray-200 mb-6">
|
||||
<div className="flex gap-1">
|
||||
{TABS.map((t) => {
|
||||
const Icon = t.icon;
|
||||
const isActive = activeTab === t.id;
|
||||
return (
|
||||
<button
|
||||
key={t.id}
|
||||
onClick={() => setActiveTab(t.id)}
|
||||
className={`flex items-center gap-2 px-4 py-3 text-sm font-medium transition-colors ${
|
||||
isActive
|
||||
? 'border-b-2 border-primary-600 text-primary-700 -mb-px'
|
||||
: 'border-b-2 border-transparent text-gray-600 hover:text-gray-900 hover:border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
{t.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
{loading || !rule || !blocklist ? (
|
||||
<div className="py-16 flex items-center justify-center">
|
||||
<LoadingOverlay message="Loading settings..." />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{activeTab === 'fwd' && <Forwarding rule={rule} onSave={saveRule} />}
|
||||
{activeTab === 'ooo' && <OutOfOffice rule={rule} onSave={saveRule} />}
|
||||
{activeTab === 'block' && <BlockedSenders blocklist={blocklist} onSave={saveBlocklist} />}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default MailboxSettingsModal;
|
||||
Reference in New Issue
Block a user