feat(messages): Phase 3 — editable-template composer, reply + create actions
The CRM action buttons and Reply now open a send-composer, not a silent
templated send.
- New send-composer (MessageComposer): loads the rendered template (via
previewTemplate) or a reply stub into a fully-editable body — the admin can
rewrite it or drop a note anywhere before sending. On send it goes out as-is
(server-sanitized), no template re-render.
- Backend: emailProcessor.sendRawEmail() sends admin-edited HTML via the
configured SMTP identity; POST /admin/email/send sanitizes + sends + records
the message in email_queue as a 'manual' send.
- Migration 155: email_queue.origin ('system' default | 'manual'). The Sent
stream now splits by origin — Automated ▸ Sent = system, Customers ▸ Sent =
the human/edited messages (which finally populates that folder). /queue gains
an origin filter + returns origin.
- Toolbar wired: Reply enabled on inbound customer mail (prefilled + quoted);
Create Quote/Contract/Invoice open the composer with that template loaded;
Gallery opens a blank compose. Accounting/Forward/Archive/Delete stay disabled
(later phases). After send, jumps to Customers ▸ Sent.
Deferred to a later phase: two-way IMAP write-back; per-identity SMTP (manual
sends currently use the global from address). Frontend build + migration boot
verified.
This commit is contained in:
@@ -15,6 +15,8 @@ export interface EmailQueueItem {
|
||||
eventId: number | null;
|
||||
eventName: string | null;
|
||||
eventSlug: string | null;
|
||||
/** 'system' = app-generated (Automated), 'manual' = admin-composed (Customers Sent). */
|
||||
origin?: 'system' | 'manual';
|
||||
}
|
||||
|
||||
export interface EmailQueueListResponse {
|
||||
@@ -244,6 +246,7 @@ export const emailService = {
|
||||
async listQueue(params: {
|
||||
status?: EmailQueueStatus;
|
||||
emailType?: string;
|
||||
origin?: 'system' | 'manual';
|
||||
q?: string;
|
||||
from?: string;
|
||||
to?: string;
|
||||
@@ -260,6 +263,11 @@ export const emailService = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/** Send a human-composed (edited) email — reply or document message. */
|
||||
async sendMessage(payload: { to: string; cc?: string; subject: string; html: string; replyToReceivedId?: number }): Promise<void> {
|
||||
await api.post('/admin/email/send', payload);
|
||||
},
|
||||
|
||||
// Get all email templates
|
||||
async getTemplates(): Promise<EmailTemplate[]> {
|
||||
const response = await api.get<EmailTemplate[]>('/admin/email/templates');
|
||||
|
||||
Reference in New Issue
Block a user