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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user