Add email integration feature

- Add /api/emails/unread endpoint to fetch unread emails from sales@cardoza.construction
- Add 'Review Unread Sales Emails' button in filter buttons section
- Implement draggable modal window to display emails
- Show unread count badge on email button
- Click emails to open in Outlook web
- Add permission error handling for Azure AD Mail.Read permission
- Position modal with high z-index to ensure visibility
This commit is contained in:
2026-01-02 06:07:51 +00:00
parent ae306e946b
commit 9ca7d2e5c2
2 changed files with 304 additions and 2 deletions
+70
View File
@@ -50,6 +50,76 @@ app.get('/health', (c) => {
return c.json({ ok: true, pbDB: process.env.PB_DB });
});
// Email endpoint - fetch unread emails from sales@cardoza.construction
app.get('/api/emails/unread', async (c) => {
try {
const pbToken = c.req.header('X-PB-Token');
if (!pbToken) {
return c.json({ success: false, message: 'Missing pbToken' }, 401);
}
// Validate user token
setUserPocketBaseAuth(pbToken);
try {
await pb.collection(process.env.PB_AUTH_COLLECTION || 'Users').authRefresh();
} catch (e) {
console.error('auth_validation_failed', { message: (e as Error)?.message });
return c.json({ success: false, message: 'Invalid token' }, 401);
}
// Get Graph API token
const graphToken = await getGraphToken();
// Fetch unread emails from sales@cardoza.construction mailbox
const mailboxEmail = 'sales@cardoza.construction';
const graphUrl = `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(mailboxEmail)}/messages?$filter=isRead eq false&$select=id,subject,from,receivedDateTime,bodyPreview,webLink&$orderby=receivedDateTime desc&$top=50`;
const response = await fetch(graphUrl, {
headers: {
'Authorization': `Bearer ${graphToken}`,
'Content-Type': 'application/json'
}
});
if (!response.ok) {
const errorText = await response.text();
console.error('graph_api_error', { status: response.status, error: errorText });
// Provide helpful error message
if (response.status === 403) {
return c.json({
success: false,
message: 'Permission denied. The application needs Mail.Read permission in Azure AD to access the sales mailbox.',
hint: 'Ask your Azure AD administrator to grant "Mail.Read" application permission to this app.',
error: errorText
}, 403);
}
return c.json({ success: false, message: 'Failed to fetch emails', error: errorText }, response.status);
}
const data = await response.json();
const emails = data.value || [];
return c.json({
success: true,
emails: emails.map((email: any) => ({
id: email.id,
subject: email.subject || '(No Subject)',
from: email.from?.emailAddress?.name || email.from?.emailAddress?.address || 'Unknown',
fromEmail: email.from?.emailAddress?.address || '',
receivedDateTime: email.receivedDateTime,
bodyPreview: email.bodyPreview || '',
webLink: email.webLink || ''
}))
});
} catch (err) {
console.error('email_fetch_error', { message: (err as Error)?.message, stack: (err as Error)?.stack });
return c.json({ success: false, message: 'Internal error: ' + (err as Error)?.message }, 500);
}
});
// Submit endpoint
app.post('/api/submit', async (c) => {
try {