Add staff email client with WebSocket real-time updates

Implements a complete email client for platform staff members:

Backend:
- Add routing_mode field to PlatformEmailAddress (PLATFORM/STAFF)
- Create staff_email app with models for folders, emails, attachments, labels
- IMAP service for fetching emails with folder mapping
- SMTP service for sending emails with attachment support
- Celery tasks for periodic sync and full sync operations
- WebSocket consumer for real-time notifications
- Comprehensive API viewsets with filtering and actions

Frontend:
- Thunderbird-style three-pane email interface
- Multi-account support with drag-and-drop ordering
- Email composer with rich text editor
- Email viewer with thread support
- Real-time WebSocket updates for new emails and sync status
- 94 unit tests covering models, serializers, views, services, and consumers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 01:50:40 -05:00
parent 7b380fa903
commit 18eeda62e8
62 changed files with 8943 additions and 410 deletions

View File

@@ -7,7 +7,7 @@ import UserProfileDropdown from '../components/UserProfileDropdown';
import NotificationDropdown from '../components/NotificationDropdown';
import LanguageSelector from '../components/LanguageSelector';
import TicketModal from '../components/TicketModal';
import FloatingHelpButton from '../components/FloatingHelpButton';
import HelpButton from '../components/HelpButton';
import { useTicket } from '../hooks/useTickets';
import { useScrollToTop } from '../hooks/useScrollToTop';
@@ -26,7 +26,7 @@ const PlatformLayout: React.FC<PlatformLayoutProps> = ({ user, darkMode, toggleT
const location = useLocation();
// Pages that need edge-to-edge rendering (no padding)
const noPaddingRoutes = ['/help/api-docs'];
const noPaddingRoutes = ['/help/api-docs', '/platform/email'];
useScrollToTop(mainContentRef);
@@ -43,9 +43,6 @@ const PlatformLayout: React.FC<PlatformLayoutProps> = ({ user, darkMode, toggleT
return (
<div className="flex h-screen bg-gray-100 dark:bg-gray-900">
{/* Floating Help Button */}
<FloatingHelpButton />
{/* Mobile menu */}
<div className={`fixed inset-y-0 left-0 z-40 transform ${isMobileMenuOpen ? 'translate-x-0' : '-translate-x-full'} transition-transform duration-300 ease-in-out md:hidden`}>
<PlatformSidebar user={user} isCollapsed={false} toggleCollapse={() => { }} />
@@ -86,6 +83,7 @@ const PlatformLayout: React.FC<PlatformLayoutProps> = ({ user, darkMode, toggleT
{darkMode ? <Sun size={20} /> : <Moon size={20} />}
</button>
<NotificationDropdown onTicketClick={handleTicketClick} />
<HelpButton />
<UserProfileDropdown user={user} />
</div>
</header>