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 ComposeEmailHeader from './ComposeEmailHeader';
|
||||
import ComposeEmailForm from './ComposeEmailForm';
|
||||
import ComposeEmailFooter from './ComposeEmailFooter';
|
||||
import RichEmailEditor from './RichEmailEditor';
|
||||
import QuotedEmailContent from './QuotedEmailContent';
|
||||
|
||||
// Import from the centralized utils
|
||||
import {
|
||||
@ -33,9 +30,6 @@ import { EmailMessage, EmailAddress } from '@/types/email';
|
||||
*
|
||||
* All code that needs to compose emails should import this component from:
|
||||
* @/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
|
||||
@ -57,12 +51,6 @@ interface ComposeEmailProps {
|
||||
}) => 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) {
|
||||
const { initialEmail, type = 'new', onClose, onSend } = props;
|
||||
|
||||
@ -72,6 +60,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
const [bcc, setBcc] = useState<string>('');
|
||||
const [subject, setSubject] = useState<string>('');
|
||||
const [emailContent, setEmailContent] = useState<string>('');
|
||||
const [quotedContent, setQuotedContent] = useState<string>('');
|
||||
const [showCc, setShowCc] = useState<boolean>(false);
|
||||
const [showBcc, setShowBcc] = useState<boolean>(false);
|
||||
const [sending, setSending] = useState<boolean>(false);
|
||||
@ -81,6 +70,8 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
type: string;
|
||||
}>>([]);
|
||||
|
||||
const editorRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Initialize the form when replying to or forwarding an email
|
||||
useEffect(() => {
|
||||
if (initialEmail && type !== 'new') {
|
||||
@ -100,8 +91,11 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
// Set subject
|
||||
setSubject(formatted.subject);
|
||||
|
||||
// Set the email content (use HTML if available)
|
||||
setEmailContent(formatted.content.html || formatted.content.text);
|
||||
// Set the quoted content (original email)
|
||||
setQuotedContent(formatted.content.html || formatted.content.text);
|
||||
|
||||
// Start with empty content for the reply
|
||||
setEmailContent('');
|
||||
}
|
||||
else if (type === 'forward') {
|
||||
// Get formatted data for forward
|
||||
@ -110,8 +104,11 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
// Set subject
|
||||
setSubject(formatted.subject);
|
||||
|
||||
// Set the email content (use HTML if available)
|
||||
setEmailContent(formatted.content.html || formatted.content.text);
|
||||
// Set the quoted content (original email)
|
||||
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 (initialEmail.attachments && initialEmail.attachments.length > 0) {
|
||||
@ -154,12 +151,17 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
setSending(true);
|
||||
|
||||
try {
|
||||
// Combine the new content with the quoted content
|
||||
const fullContent = type !== 'new'
|
||||
? `${emailContent}<div class="quoted-content">${quotedContent}</div>`
|
||||
: emailContent;
|
||||
|
||||
await onSend({
|
||||
to,
|
||||
cc: cc || undefined,
|
||||
bcc: bcc || undefined,
|
||||
subject,
|
||||
body: emailContent,
|
||||
body: fullContent,
|
||||
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(() => {
|
||||
// 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(() => {
|
||||
// Focus the editor
|
||||
editorContainer.focus();
|
||||
setTimeout(() => {
|
||||
if (editorRef.current) {
|
||||
editorRef.current.focus();
|
||||
|
||||
// Make sure all scroll containers are at the top
|
||||
editorContainer.scrollTop = 0;
|
||||
|
||||
// Find all possible scrollable parent containers
|
||||
const scrollContainers = [
|
||||
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 to top
|
||||
const scrollElements = [
|
||||
editorRef.current,
|
||||
document.querySelector('.overflow-y-auto'),
|
||||
document.querySelector('.compose-email-body')
|
||||
];
|
||||
|
||||
// Scroll all containers to top
|
||||
scrollContainers.forEach(container => {
|
||||
if (container) {
|
||||
container.scrollTop = 0;
|
||||
scrollElements.forEach(el => {
|
||||
if (el instanceof HTMLElement) {
|
||||
el.scrollTop = 0;
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@ -209,7 +203,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
type={type}
|
||||
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">
|
||||
{/* To Field */}
|
||||
<div className="flex-none">
|
||||
@ -281,17 +275,30 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Message Body */}
|
||||
{/* Message Body - Simplified Editor */}
|
||||
<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>
|
||||
<div className="flex-1 border border-gray-300 rounded-md overflow-hidden">
|
||||
<RichEmailEditor
|
||||
initialContent={emailContent}
|
||||
onChange={setEmailContent}
|
||||
minHeight="200px"
|
||||
maxHeight="none"
|
||||
preserveFormatting={true}
|
||||
/>
|
||||
<div className="email-editor-container flex flex-col h-full">
|
||||
{/* Simple editor for new content */}
|
||||
<div
|
||||
ref={editorRef}
|
||||
className="simple-editor p-3 min-h-[100px] outline-none"
|
||||
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>
|
||||
|
||||
@ -376,6 +383,56 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
</Button>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user