diff --git a/frontend/src/components/AgAgentPanel.jsx b/frontend/src/components/AgAgentPanel.jsx new file mode 100644 index 0000000..fca32f5 --- /dev/null +++ b/frontend/src/components/AgAgentPanel.jsx @@ -0,0 +1,110 @@ +import { + Calendar, + FileSearch, + ListChecks, + PenLine, + Receipt, + Sparkles, + Star, +} from "lucide-react"; +import { inboxIntelligence } from "../utils/inboxSignals"; + +function greeting(name) { + const hour = new Date().getHours(); + const period = hour < 12 ? "Good morning" : hour < 17 ? "Good afternoon" : "Good evening"; + return `${period}, ${name}`; +} + +const cards = [ + { + key: "important", + label: "Important emails", + hint: "Need a look", + icon: Star, + tone: "rose", + }, + { + key: "actionItems", + label: "Action items", + hint: "Need your response", + icon: ListChecks, + tone: "blue", + }, + { + key: "meetings", + label: "Meetings mentioned", + hint: "In this view", + icon: Calendar, + tone: "green", + }, + { + key: "invoices", + label: "Invoices & bills", + hint: "This week", + icon: Receipt, + tone: "amber", + }, +]; + +export default function AgAgentPanel({ account, emails, focus, onQuickAction }) { + const stats = inboxIntelligence(emails); + const firstName = account?.first_name || "there"; + + return ( +
+
+ +
+

AG Agent

+

Your email co-pilot

+
+
+ +
+ {greeting(firstName)} + Here's what needs your attention. + {focus &&

{focus}

} +
+ +
+ {cards.map((card) => { + const Icon = card.icon; + const count = stats[card.key] || 0; + return ( + + ); + })} +
+ +
+

Quick actions

+
+ + + + +
+
+
+ ); +} diff --git a/frontend/src/components/DashboardPage.jsx b/frontend/src/components/DashboardPage.jsx index d8d44b3..33e78e1 100644 --- a/frontend/src/components/DashboardPage.jsx +++ b/frontend/src/components/DashboardPage.jsx @@ -1,6 +1,8 @@ import { Bot, + ChevronDown, Clock3, + FileEdit, Inbox, ListFilter, LogOut, @@ -11,11 +13,12 @@ import { Settings, ShieldAlert, Sparkles, + Star, Tags, Trash2, X, } from "lucide-react"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { getCurrentAccount, getMyCompanyDomain } from "../api"; import Brand from "./Brand"; import CompanyDnsSetup from "./CompanyDnsSetup"; @@ -23,12 +26,22 @@ import ComposeModal from "./ComposeModal"; import MailboxView from "./MailboxView"; const mailItems = [ - [Inbox, "Inbox"], - [Send, "Outbox"], + [Inbox, "Inbox", "unread"], + [Star, "Starred", "starred"], + [Clock3, "Snoozed"], + [Send, "Sent"], + [FileEdit, "Drafts"], [ShieldAlert, "Spam"], [Trash2, "Trash"], [Clock3, "Scheduled"], - [Settings, "Settings"], +]; + +const labels = [ + ["Important", "rose"], + ["Projects", "violet"], + ["Clients", "blue"], + ["Finance", "amber"], + ["Personal", "green"], ]; const agentItems = [ @@ -36,6 +49,7 @@ const agentItems = [ [ListFilter, "Filters"], [Sparkles, "Summary"], [Plug, "Integration"], + [Settings, "Settings"], ]; function clearSession() { @@ -43,20 +57,21 @@ function clearSession() { sessionStorage.removeItem("agmail_account"); } -function NavGroup({ icon: Icon, title, items, active, onSelect }) { +function NavGroup({ icon: Icon, title, items, active, counts, onSelect }) { return (
-

{title}

+

{title}

@@ -75,6 +90,10 @@ export default function DashboardPage() { const [notice, setNotice] = useState(""); const [loading, setLoading] = useState(true); const [mailRefreshKey, setMailRefreshKey] = useState(0); + const [labelsOpen, setLabelsOpen] = useState(true); + const [lens, setLens] = useState("all"); + const [mailStats, setMailStats] = useState({ unread: 0, starred: 0, important: 0 }); + const handleStats = useCallback((stats) => setMailStats(stats), []); useEffect(() => { const token = sessionStorage.getItem("agmail_access_token"); @@ -110,7 +129,19 @@ export default function DashboardPage() { } function selectItem(item) { - setActive(item); + if (item === "Starred") { + setActive("Inbox"); + setLens("starred"); + } else if (item === "Sent") { + setActive("Outbox"); + setLens("all"); + } else if (labels.some(([name]) => name === item)) { + setActive("Inbox"); + setLens(item); + } else { + setActive(item); + setLens("all"); + } setMenuOpen(false); } @@ -179,7 +210,30 @@ export default function DashboardPage() { - + +
+ + {labelsOpen && ( + + )} +
{account.first_name[0]}{account.last_name[0]} @@ -195,9 +249,38 @@ export default function DashboardPage() { {workspaceLabel}
)} - {active === "Inbox" && } - {active === "Outbox" && } - {active === "Trash" && } + {active === "Inbox" && ( + selectItem("Settings")} + onStats={handleStats} + /> + )} + {active === "Outbox" && ( + selectItem("Settings")} + /> + )} + {active === "Trash" && ( + selectItem("Settings")} + /> + )} {!["Inbox", "Outbox", "Trash"].includes(active) && (
diff --git a/frontend/src/components/MailRow.jsx b/frontend/src/components/MailRow.jsx index 16aac00..9d38b6f 100644 --- a/frontend/src/components/MailRow.jsx +++ b/frontend/src/components/MailRow.jsx @@ -1,6 +1,15 @@ -import { MailOpen, Star } from "lucide-react"; +import { + Archive, + Clock3, + MailOpen, + Paperclip, + Sparkles, + Star, + Trash2, +} from "lucide-react"; import SenderAvatar from "./SenderAvatar"; import { senderDisplayName } from "../utils/senderAvatar"; +import { attachmentCount, emailLabels } from "../utils/inboxSignals"; function formatTimestamp(value) { if (!value) return ""; @@ -21,7 +30,19 @@ function formatTimestamp(value) { }).format(date); } -export default function MailRow({ email, selected, onOpen, onToggleStar }) { +export default function MailRow({ + email, + selected, + checked, + onOpen, + onToggleStar, + onToggleCheck, + onTrash, + onMarkUnread, + onArchive, + onSnooze, + onSummarize, +}) { const unread = email.is_read === false; const starred = email.is_starred === true; const subject = email.subject?.trim() || "(No subject)"; @@ -29,6 +50,8 @@ export default function MailRow({ email, selected, onOpen, onToggleStar }) { const timestamp = email.received_at || email.sent_at || email.created_at || email.updated_at; const senderName = senderDisplayName(email); + const labels = emailLabels(email); + const files = attachmentCount(email); function handleKeyDown(event) { if (event.key === "Enter" || event.key === " ") { @@ -37,9 +60,9 @@ export default function MailRow({ email, selected, onOpen, onToggleStar }) { } } - function handleStarClick(event) { + function stop(event, action) { event.stopPropagation(); - onToggleStar?.(email, !starred); + action?.(); } return ( @@ -51,25 +74,48 @@ export default function MailRow({ email, selected, onOpen, onToggleStar }) { onKeyDown={handleKeyDown} aria-label={`${unread ? "Unread" : "Read"} email from ${senderName}: ${subject}`} > - - - {senderName} - - {subject} - {snippet && {snippet}} - - + event.stopPropagation()} + onChange={() => onToggleCheck?.(email)} + /> + + + {unread &&