courrier preview
This commit is contained in:
parent
f9e0b323ce
commit
3584f72bf5
@ -12,10 +12,7 @@ import { Label } from '@/components/ui/label';
|
|||||||
|
|
||||||
// Import sub-components
|
// Import sub-components
|
||||||
import ComposeEmailHeader from './ComposeEmailHeader';
|
import ComposeEmailHeader from './ComposeEmailHeader';
|
||||||
import ComposeEmailForm from './ComposeEmailForm';
|
|
||||||
import ComposeEmailFooter from './ComposeEmailFooter';
|
|
||||||
import RichEmailEditor from './RichEmailEditor';
|
import RichEmailEditor from './RichEmailEditor';
|
||||||
import QuotedEmailContent from './QuotedEmailContent';
|
|
||||||
|
|
||||||
// Import from the centralized utils
|
// Import from the centralized utils
|
||||||
import {
|
import {
|
||||||
@ -33,9 +30,6 @@ import { EmailMessage, EmailAddress } from '@/types/email';
|
|||||||
*
|
*
|
||||||
* All code that needs to compose emails should import this component from:
|
* All code that needs to compose emails should import this component from:
|
||||||
* @/components/email/ComposeEmail
|
* @/components/email/ComposeEmail
|
||||||
*
|
|
||||||
* It uses the centralized email formatter from @/lib/utils/email-formatter.ts
|
|
||||||
* for consistent handling of email content and text direction.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Define interface for the modern props
|
// Define interface for the modern props
|
||||||
@ -57,12 +51,6 @@ interface ComposeEmailProps {
|
|||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a helper to fix table widths in HTML
|
|
||||||
function fixTableWidths(html: string): string {
|
|
||||||
if (!html) return html;
|
|
||||||
return html.replace(/<table(?![^>]*width)/g, '<table width="100%"');
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ComposeEmail(props: ComposeEmailProps) {
|
export default function ComposeEmail(props: ComposeEmailProps) {
|
||||||
const { initialEmail, type = 'new', onClose, onSend } = props;
|
const { initialEmail, type = 'new', onClose, onSend } = props;
|
||||||
|
|
||||||
@ -72,6 +60,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
|||||||
const [bcc, setBcc] = useState<string>('');
|
const [bcc, setBcc] = useState<string>('');
|
||||||
const [subject, setSubject] = useState<string>('');
|
const [subject, setSubject] = useState<string>('');
|
||||||
const [emailContent, setEmailContent] = useState<string>('');
|
const [emailContent, setEmailContent] = useState<string>('');
|
||||||
|
const [quotedContent, setQuotedContent] = useState<string>('');
|
||||||
const [showCc, setShowCc] = useState<boolean>(false);
|
const [showCc, setShowCc] = useState<boolean>(false);
|
||||||
const [showBcc, setShowBcc] = useState<boolean>(false);
|
const [showBcc, setShowBcc] = useState<boolean>(false);
|
||||||
const [sending, setSending] = useState<boolean>(false);
|
const [sending, setSending] = useState<boolean>(false);
|
||||||
@ -81,6 +70,8 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
|||||||
type: string;
|
type: string;
|
||||||
}>>([]);
|
}>>([]);
|
||||||
|
|
||||||
|
const editorRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
// Initialize the form when replying to or forwarding an email
|
// Initialize the form when replying to or forwarding an email
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialEmail && type !== 'new') {
|
if (initialEmail && type !== 'new') {
|
||||||
@ -100,8 +91,11 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
|||||||
// Set subject
|
// Set subject
|
||||||
setSubject(formatted.subject);
|
setSubject(formatted.subject);
|
||||||
|
|
||||||
// Set the email content (use HTML if available)
|
// Set the quoted content (original email)
|
||||||
setEmailContent(formatted.content.html || formatted.content.text);
|
setQuotedContent(formatted.content.html || formatted.content.text);
|
||||||
|
|
||||||
|
// Start with empty content for the reply
|
||||||
|
setEmailContent('');
|
||||||
}
|
}
|
||||||
else if (type === 'forward') {
|
else if (type === 'forward') {
|
||||||
// Get formatted data for forward
|
// Get formatted data for forward
|
||||||
@ -110,8 +104,11 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
|||||||
// Set subject
|
// Set subject
|
||||||
setSubject(formatted.subject);
|
setSubject(formatted.subject);
|
||||||
|
|
||||||
// Set the email content (use HTML if available)
|
// Set the quoted content (original email)
|
||||||
setEmailContent(formatted.content.html || formatted.content.text);
|
setQuotedContent(formatted.content.html || formatted.content.text);
|
||||||
|
|
||||||
|
// Start with empty content for the forward
|
||||||
|
setEmailContent('');
|
||||||
|
|
||||||
// If the original email has attachments, we should include them
|
// If the original email has attachments, we should include them
|
||||||
if (initialEmail.attachments && initialEmail.attachments.length > 0) {
|
if (initialEmail.attachments && initialEmail.attachments.length > 0) {
|
||||||
@ -154,12 +151,17 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
|||||||
setSending(true);
|
setSending(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Combine the new content with the quoted content
|
||||||
|
const fullContent = type !== 'new'
|
||||||
|
? `${emailContent}<div class="quoted-content">${quotedContent}</div>`
|
||||||
|
: emailContent;
|
||||||
|
|
||||||
await onSend({
|
await onSend({
|
||||||
to,
|
to,
|
||||||
cc: cc || undefined,
|
cc: cc || undefined,
|
||||||
bcc: bcc || undefined,
|
bcc: bcc || undefined,
|
||||||
subject,
|
subject,
|
||||||
body: emailContent,
|
body: fullContent,
|
||||||
attachments
|
attachments
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -173,34 +175,26 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Additional effect to ensure we scroll to the top and focus the editor
|
// Focus and scroll to top when opened
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Focus the editor and ensure it's scrolled to the top
|
|
||||||
const editorContainer = document.querySelector('.ql-editor') as HTMLElement;
|
|
||||||
if (editorContainer) {
|
|
||||||
// Set timeout to ensure DOM is fully rendered
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Focus the editor
|
if (editorRef.current) {
|
||||||
editorContainer.focus();
|
editorRef.current.focus();
|
||||||
|
|
||||||
// Make sure all scroll containers are at the top
|
// Scroll to top
|
||||||
editorContainer.scrollTop = 0;
|
const scrollElements = [
|
||||||
|
editorRef.current,
|
||||||
// Find all possible scrollable parent containers
|
document.querySelector('.overflow-y-auto'),
|
||||||
const scrollContainers = [
|
document.querySelector('.compose-email-body')
|
||||||
document.querySelector('.ql-container') as HTMLElement,
|
|
||||||
document.querySelector('.rich-email-editor-container') as HTMLElement,
|
|
||||||
document.querySelector('.h-full.flex.flex-col.p-6') as HTMLElement
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Scroll all containers to top
|
scrollElements.forEach(el => {
|
||||||
scrollContainers.forEach(container => {
|
if (el instanceof HTMLElement) {
|
||||||
if (container) {
|
el.scrollTop = 0;
|
||||||
container.scrollTop = 0;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 100);
|
|
||||||
}
|
}
|
||||||
|
}, 100);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -209,7 +203,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
|||||||
type={type}
|
type={type}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
<div className="flex-1 overflow-y-auto p-4">
|
<div className="flex-1 overflow-y-auto p-4 compose-email-body">
|
||||||
<div className="h-full flex flex-col p-4 space-y-2 overflow-y-auto">
|
<div className="h-full flex flex-col p-4 space-y-2 overflow-y-auto">
|
||||||
{/* To Field */}
|
{/* To Field */}
|
||||||
<div className="flex-none">
|
<div className="flex-none">
|
||||||
@ -281,17 +275,30 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Message Body */}
|
{/* Message Body - Simplified Editor */}
|
||||||
<div className="flex-1 min-h-[200px] flex flex-col overflow-hidden">
|
<div className="flex-1 min-h-[200px] flex flex-col overflow-hidden">
|
||||||
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
|
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
|
||||||
<div className="flex-1 border border-gray-300 rounded-md overflow-hidden">
|
<div className="flex-1 border border-gray-300 rounded-md overflow-hidden">
|
||||||
<RichEmailEditor
|
<div className="email-editor-container flex flex-col h-full">
|
||||||
initialContent={emailContent}
|
{/* Simple editor for new content */}
|
||||||
onChange={setEmailContent}
|
<div
|
||||||
minHeight="200px"
|
ref={editorRef}
|
||||||
maxHeight="none"
|
className="simple-editor p-3 min-h-[100px] outline-none"
|
||||||
preserveFormatting={true}
|
contentEditable={true}
|
||||||
|
dangerouslySetInnerHTML={{ __html: emailContent }}
|
||||||
|
onInput={(e) => setEmailContent(e.currentTarget.innerHTML)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Quoted content from original email */}
|
||||||
|
{quotedContent && (
|
||||||
|
<div className="quoted-email-content p-3 border-t border-gray-200 bg-gray-50">
|
||||||
|
<div
|
||||||
|
className="email-quoted-content"
|
||||||
|
dangerouslySetInnerHTML={{ __html: quotedContent }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -376,6 +383,56 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Styles for email display */}
|
||||||
|
<style jsx global>{`
|
||||||
|
.simple-editor {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-quoted-content {
|
||||||
|
color: #505050;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
border-left: 2px solid #ddd;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-quoted-content blockquote {
|
||||||
|
margin: 5px 0;
|
||||||
|
padding-left: 10px;
|
||||||
|
border-left: 2px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-quoted-content img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-quoted-content table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-quoted-content th,
|
||||||
|
.email-quoted-content td {
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-quoted-content th {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user