courrier multi account restore compose

This commit is contained in:
alma 2025-04-29 09:39:13 +02:00
parent c2bb904fde
commit 5bade2283c
2 changed files with 107 additions and 119 deletions

View File

@ -180,15 +180,9 @@ export default function CourrierPage() {
]);
const [selectedAccount, setSelectedAccount] = useState<Account | null>(null);
// Track expanded folders for each account
const [expandedAccounts, setExpandedAccounts] = useState<Record<string, boolean>>({});
// Track selected folder per account
const [selectedFolders, setSelectedFolders] = useState<Record<string, string>>({});
// Track folder visibility per account
const [visibleFolders, setVisibleFolders] = useState<Record<string, string[]>>({});
// Add state for modals/dialogs
const [showEditModal, setShowEditModal] = useState(false);
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
@ -198,34 +192,6 @@ export default function CourrierPage() {
const [editLoading, setEditLoading] = useState(false);
const [deleteLoading, setDeleteLoading] = useState(false);
// Debug accounts state
useEffect(() => {
console.log('Current accounts state:', accounts);
console.log('Expanded accounts:', expandedAccounts);
console.log('Selected account:', selectedAccount);
console.log('Show folders:', showFolders);
}, [accounts, expandedAccounts, selectedAccount, showFolders]);
// Debug selectedAccount state
useEffect(() => {
console.log('Selected account changed:', selectedAccount);
if (selectedAccount) {
console.log('Selected account folders:', selectedAccount.folders);
}
}, [selectedAccount]);
// Add useEffect for debugging
useEffect(() => {
if (typeof window !== 'undefined') {
console.log('[DEBUG] Rendering UI with:', {
accountsCount: accounts.length,
selectedAccountId: selectedAccount?.id,
showFolders,
currentFolder
});
}
}, [accounts, selectedAccount, showFolders, currentFolder]);
// Calculate unread count for each account and folder
useEffect(() => {
// Create a map to store unread counts per account and folder
@ -440,12 +406,6 @@ export default function CourrierPage() {
setSelectedAccount(updatedAccounts[1]);
setShowFolders(true);
// Ensure folders are visible for the selected account
setExpandedAccounts(prev => ({
...prev,
[updatedAccounts[1].id]: true
}));
// Set initial selected folder
setSelectedFolders(prev => ({
...prev,
@ -714,14 +674,9 @@ export default function CourrierPage() {
const handleAccountSelect = (account: Account) => {
setSelectedAccount(account);
setShowFolders(true);
if (account.id !== 'loading-account') {
setExpandedAccounts(prev => ({
...prev,
[account.id]: true
}));
handleMailboxChange('INBOX', account.id);
}
handleMailboxChange('INBOX', account.id);
};
const handleAddAccount = async (accountData: AccountData) => {
@ -733,24 +688,6 @@ export default function CourrierPage() {
// }));
};
// Debug folder rendering
useEffect(() => {
if (selectedAccount) {
console.log('Selected account folders:', selectedAccount.folders);
console.log('Is account expanded:', expandedAccounts[selectedAccount.id]);
}
}, [selectedAccount, expandedAccounts]);
// On page load/refresh, expand all accounts so their folders are always visible
useEffect(() => {
// Expand all real accounts (not loading-account) on load/refresh
const expanded: Record<string, boolean> = {};
accounts.forEach(a => {
if (a.id !== 'loading-account') expanded[a.id] = true;
});
setExpandedAccounts(expanded);
}, [accounts]);
return (
<>
<SimplifiedLoadingFix />
@ -765,7 +702,6 @@ export default function CourrierPage() {
selectedAccount={selectedAccount}
selectedFolders={selectedFolders}
currentFolder={currentFolder}
expandedAccounts={expandedAccounts}
loading={loading}
unreadCount={unreadCount}
showAddAccountForm={showAddAccountForm}
@ -777,9 +713,6 @@ export default function CourrierPage() {
}}
onComposeNew={handleComposeNew}
onAccountSelect={handleAccountSelect}
onToggleExpand={(accountId, expanded) => {
setExpandedAccounts(prev => ({ ...prev, [accountId]: expanded }));
}}
onShowAddAccountForm={setShowAddAccountForm}
onAddAccount={async (formData) => {
setLoading(true);

View File

@ -16,35 +16,28 @@ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
import { Input } from '@/components/ui/input';
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
interface Account {
id: string;
name: string;
email: string;
color: string;
folders: string[];
}
import { IconArrowDown, IconCheck, IconChevronDown, IconChevronUp, IconEdit, IconMail, IconMailbox, IconPencil, IconPlus, IconRefresh, IconTrash } from '@tabler/icons-react';
import { Account } from '../../types';
interface EmailSidebarProps {
accounts: Account[];
selectedAccount: Account | null;
selectedFolders: Record<string, string>;
currentFolder: string;
expandedAccounts: Record<string, boolean>;
loading: boolean;
unreadCount: number;
unreadCount: Record<string, Record<string, number>>;
showAddAccountForm: boolean;
// Actions
showFolders?: boolean;
onFolderChange: (folder: string, accountId: string) => void;
onRefresh: () => void;
onComposeNew: () => void;
onAccountSelect: (account: Account) => void;
onToggleExpand: (accountId: string, expanded: boolean) => void;
onShowAddAccountForm: (show: boolean) => void;
onAddAccount: (formData: FormData) => Promise<void>;
onAddAccount: (formData: any) => Promise<void>;
onEditAccount: (account: Account) => void;
onDeleteAccount: (account: Account) => void;
onSelectEmail?: (emailId: string, accountId: string, folder: string) => void;
onShowFoldersToggle?: (show: boolean) => void;
}
export default function EmailSidebar({
@ -52,20 +45,68 @@ export default function EmailSidebar({
selectedAccount,
selectedFolders,
currentFolder,
expandedAccounts,
loading,
unreadCount,
showAddAccountForm,
showFolders = true,
onFolderChange,
onRefresh,
onComposeNew,
onAccountSelect,
onToggleExpand,
onShowAddAccountForm,
onAddAccount,
onEditAccount,
onDeleteAccount
onDeleteAccount,
onSelectEmail,
onShowFoldersToggle
}: EmailSidebarProps) {
const [isSaving, setIsSaving] = useState(false);
const [formData, setFormData] = useState({
email: '',
password: '',
displayName: '',
host: '',
port: '993',
useSSL: true,
smtpHost: '',
smtpPort: '587',
smtpUseSSL: false
});
const [activeTab, setActiveTab] = useState('imap');
// Handle form submission
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsSaving(true);
try {
await onAddAccount(formData);
setFormData({
email: '',
password: '',
displayName: '',
host: '',
port: '993',
useSSL: true,
smtpHost: '',
smtpPort: '587',
smtpUseSSL: false
});
onShowAddAccountForm(false);
} catch (err) {
console.error('Failed to add account:', err);
} finally {
setIsSaving(false);
}
};
// Handle input changes
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
// Get the appropriate icon for a folder
const getFolderIcon = (folder: string) => {
@ -116,9 +157,9 @@ export default function EmailSidebar({
<div className="flex items-center w-full">
{getFolderIcon(baseFolder)}
<span className="ml-2 truncate text-gray-700">{formatFolderName(baseFolder)}</span>
{baseFolder === 'INBOX' && unreadCount > 0 && (
{baseFolder === 'INBOX' && unreadCount[accountId]?.[folder] > 0 && (
<span className="ml-auto bg-blue-500 text-white text-[10px] px-1.5 rounded-full">
{unreadCount}
{unreadCount[accountId][folder]}
</span>
)}
</div>
@ -163,14 +204,28 @@ export default function EmailSidebar({
<div className="p-3 border-b border-gray-100">
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium text-gray-500">Accounts</span>
<Button
variant="ghost"
size="sm"
className="h-7 w-7 p-0 text-gray-400 hover:text-gray-600"
onClick={() => onShowAddAccountForm(!showAddAccountForm)}
>
<PlusIcon className="h-4 w-4" />
</Button>
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="sm"
className="h-7 w-7 p-0 text-gray-400 hover:text-gray-600"
onClick={() => onShowFoldersToggle?.(showFolders ? false : true)}
>
{showFolders ? (
<ChevronUp className="h-4 w-4" />
) : (
<ChevronDown className="h-4 w-4" />
)}
</Button>
<Button
variant="ghost"
size="sm"
className="h-7 w-7 p-0 text-gray-400 hover:text-gray-600"
onClick={() => onShowAddAccountForm(!showAddAccountForm)}
>
<PlusIcon className="h-4 w-4" />
</Button>
</div>
</div>
{/* Display all accounts */}
@ -179,10 +234,7 @@ export default function EmailSidebar({
{showAddAccountForm && (
<div className="mb-2 p-2 border border-gray-200 rounded-md bg-white">
<h4 className="text-xs font-medium mb-0.5 text-gray-700">Add IMAP Account</h4>
<form onSubmit={async (e) => {
e.preventDefault();
await onAddAccount(new FormData(e.currentTarget));
}}>
<form onSubmit={handleSubmit}>
<div>
<Tabs defaultValue="imap" className="w-full">
<TabsList className="grid w-full grid-cols-2 h-6 mb-0.5 bg-gray-100">
@ -198,6 +250,8 @@ export default function EmailSidebar({
placeholder="email@example.com"
className="h-7 text-xs bg-white border-gray-300 mb-0.5 text-gray-900"
required
value={formData.email}
onChange={handleChange}
/>
</div>
<div>
@ -208,14 +262,18 @@ export default function EmailSidebar({
placeholder="•••••••••"
className="h-7 text-xs bg-white border-gray-300 mb-0.5 text-gray-900"
required
value={formData.password}
onChange={handleChange}
/>
</div>
<div>
<Input
id="display_name"
name="display_name"
name="displayName"
placeholder="John Doe"
className="h-7 text-xs bg-white border-gray-300 mb-0.5 text-gray-900"
value={formData.displayName}
onChange={handleChange}
/>
</div>
<div>
@ -225,6 +283,8 @@ export default function EmailSidebar({
placeholder="imap.example.com"
className="h-7 text-xs bg-white border-gray-300 mb-0.5 text-gray-900"
required
value={formData.host}
onChange={handleChange}
/>
</div>
<div className="flex gap-1">
@ -236,12 +296,14 @@ export default function EmailSidebar({
className="h-7 text-xs bg-white border-gray-300 text-gray-900"
defaultValue="993"
required
value={formData.port}
onChange={handleChange}
/>
</div>
<div className="flex items-center pl-1">
<div className="flex items-center space-x-1">
<Checkbox id="secure" name="secure" defaultChecked />
<Label htmlFor="secure" className="text-xs">SSL</Label>
<Checkbox id="useSSL" name="useSSL" defaultChecked={formData.useSSL} onChange={handleChange} />
<Label htmlFor="useSSL" className="text-xs">SSL</Label>
</div>
</div>
</div>
@ -251,24 +313,28 @@ export default function EmailSidebar({
<div>
<Input
id="smtp_host"
name="smtp_host"
name="smtpHost"
placeholder="smtp.example.com"
className="h-7 text-xs bg-white border-gray-300 mb-0.5 text-gray-900"
value={formData.smtpHost}
onChange={handleChange}
/>
</div>
<div className="flex gap-1">
<div className="flex-1">
<Input
id="smtp_port"
name="smtp_port"
name="smtpPort"
placeholder="587"
className="h-7 text-xs bg-white border-gray-300 text-gray-900"
defaultValue="587"
value={formData.smtpPort}
onChange={handleChange}
/>
</div>
<div className="flex items-center pl-1">
<div className="flex items-center space-x-1">
<Checkbox id="smtp_secure" name="smtp_secure" defaultChecked />
<Checkbox id="smtp_secure" name="smtpUseSSL" defaultChecked={formData.smtpUseSSL} onChange={handleChange} />
<Label htmlFor="smtp_secure" className="text-xs">SSL</Label>
</div>
</div>
@ -283,9 +349,9 @@ export default function EmailSidebar({
<Button
type="submit"
className="flex-1 h-6 text-xs bg-blue-500 hover:bg-blue-600 text-white rounded-md px-2 py-0"
disabled={loading}
disabled={isSaving}
>
{loading ? <Loader2 className="h-3 w-3 animate-spin mr-1" /> : null}
{isSaving ? <Loader2 className="h-3 w-3 animate-spin mr-1" /> : null}
Test & Add
</Button>
<Button
@ -335,20 +401,9 @@ export default function EmailSidebar({
</DropdownMenuContent>
</DropdownMenu>
)}
{/* Expand/collapse arrow */}
{account.id !== 'loading-account' && (
<button
type="button"
className="ml-1 text-gray-400 hover:text-gray-600 cursor-pointer flex items-center justify-center h-5 w-5"
tabIndex={-1}
onClick={e => { e.stopPropagation(); onToggleExpand(account.id, !expandedAccounts[account.id]); }}
>
{expandedAccounts[account.id] ? <ChevronUp className="h-3 w-3" /> : <ChevronDown className="h-3 w-3" />}
</button>
)}
</div>
{/* Show folders for any expanded account */}
{expandedAccounts[account.id] && account.folders && account.folders.length > 0 && (
{/* Show folders for each account when selected and when folders are visible */}
{selectedAccount?.id === account.id && showFolders && account.folders && account.folders.length > 0 && (
<div className="pl-4">
{account.folders.map((folder) => renderFolderButton(folder, account.id))}
</div>