feat: Add SMTP settings and collapsible email configuration UI

- Add SMTP fields to TicketEmailSettings model (host, port, TLS/SSL, credentials, from email/name)
- Update serializers with SMTP fields and is_smtp_configured flag
- Add TicketEmailTestSmtpView for testing SMTP connections
- Update frontend API types and hooks for SMTP settings
- Add collapsible IMAP and SMTP configuration sections with "Configured" badges
- Fix TypeScript errors in mockData.ts (missing required fields, type mismatches)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-11-29 18:28:29 -05:00
parent 0c7d76e264
commit cfc1b36ada
94 changed files with 13419 additions and 1121 deletions

View File

@@ -0,0 +1,169 @@
/**
* API client for ticket email settings
*/
import apiClient from './client';
export interface TicketEmailSettings {
// IMAP settings (inbound)
imap_host: string;
imap_port: number;
imap_use_ssl: boolean;
imap_username: string;
imap_password_masked: string;
imap_folder: string;
// SMTP settings (outbound)
smtp_host: string;
smtp_port: number;
smtp_use_tls: boolean;
smtp_use_ssl: boolean;
smtp_username: string;
smtp_password_masked: string;
smtp_from_email: string;
smtp_from_name: string;
// General settings
support_email_address: string;
support_email_domain: string;
is_enabled: boolean;
delete_after_processing: boolean;
check_interval_seconds: number;
max_attachment_size_mb: number;
allowed_attachment_types: string[];
// Status
last_check_at: string | null;
last_error: string;
emails_processed_count: number;
is_configured: boolean;
is_imap_configured: boolean;
is_smtp_configured: boolean;
created_at: string;
updated_at: string;
}
export interface TicketEmailSettingsUpdate {
// IMAP settings
imap_host?: string;
imap_port?: number;
imap_use_ssl?: boolean;
imap_username?: string;
imap_password?: string;
imap_folder?: string;
// SMTP settings
smtp_host?: string;
smtp_port?: number;
smtp_use_tls?: boolean;
smtp_use_ssl?: boolean;
smtp_username?: string;
smtp_password?: string;
smtp_from_email?: string;
smtp_from_name?: string;
// General settings
support_email_address?: string;
support_email_domain?: string;
is_enabled?: boolean;
delete_after_processing?: boolean;
check_interval_seconds?: number;
max_attachment_size_mb?: number;
allowed_attachment_types?: string[];
}
export interface TestConnectionResult {
success: boolean;
message: string;
}
export interface FetchNowResult {
success: boolean;
message: string;
processed: number;
}
export interface IncomingTicketEmail {
id: number;
message_id: string;
from_address: string;
from_name: string;
to_address: string;
subject: string;
body_text: string;
extracted_reply: string;
ticket: number | null;
ticket_subject: string;
matched_user: number | null;
ticket_id_from_email: string;
processing_status: 'PENDING' | 'PROCESSED' | 'FAILED' | 'SPAM' | 'NO_MATCH' | 'DUPLICATE';
processing_status_display: string;
error_message: string;
email_date: string;
received_at: string;
processed_at: string | null;
}
/**
* Get ticket email settings
*/
export const getTicketEmailSettings = async (): Promise<TicketEmailSettings> => {
const response = await apiClient.get('/api/tickets/email-settings/');
return response.data;
};
/**
* Update ticket email settings
*/
export const updateTicketEmailSettings = async (
data: TicketEmailSettingsUpdate
): Promise<TicketEmailSettings> => {
const response = await apiClient.patch('/api/tickets/email-settings/', data);
return response.data;
};
/**
* Test IMAP connection
*/
export const testImapConnection = async (): Promise<TestConnectionResult> => {
const response = await apiClient.post('/api/tickets/email-settings/test-imap/');
return response.data;
};
/**
* Test SMTP connection
*/
export const testSmtpConnection = async (): Promise<TestConnectionResult> => {
const response = await apiClient.post('/api/tickets/email-settings/test-smtp/');
return response.data;
};
// Legacy alias for backwards compatibility
export const testEmailConnection = testImapConnection;
/**
* Manually trigger email fetch
*/
export const fetchEmailsNow = async (): Promise<FetchNowResult> => {
const response = await apiClient.post('/api/tickets/email-settings/fetch-now/');
return response.data;
};
/**
* Get incoming email audit log
*/
export const getIncomingEmails = async (params?: {
status?: string;
ticket?: number;
}): Promise<IncomingTicketEmail[]> => {
const response = await apiClient.get('/api/tickets/incoming-emails/', { params });
return response.data;
};
/**
* Reprocess a failed incoming email
*/
export const reprocessIncomingEmail = async (id: number): Promise<{
success: boolean;
message: string;
comment_id?: number;
ticket_id?: number;
}> => {
const response = await apiClient.post(`/api/tickets/incoming-emails/${id}/reprocess/`);
return response.data;
};