first commit
This commit is contained in:
commit
cce9a8c9ba
73 changed files with 8665 additions and 0 deletions
213
frontend/src/components/DashboardPage.jsx
Normal file
213
frontend/src/components/DashboardPage.jsx
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
import {
|
||||
Bot,
|
||||
Clock3,
|
||||
Inbox,
|
||||
ListFilter,
|
||||
LogOut,
|
||||
Mail,
|
||||
Menu,
|
||||
Plug,
|
||||
Send,
|
||||
Settings,
|
||||
ShieldAlert,
|
||||
Sparkles,
|
||||
Tags,
|
||||
Trash2,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getCurrentAccount, getMyCompanyDomain } from "../api";
|
||||
import Brand from "./Brand";
|
||||
import CompanyDnsSetup from "./CompanyDnsSetup";
|
||||
import ComposeModal from "./ComposeModal";
|
||||
import MailboxView from "./MailboxView";
|
||||
|
||||
const mailItems = [
|
||||
[Inbox, "Inbox"],
|
||||
[Send, "Outbox"],
|
||||
[ShieldAlert, "Spam"],
|
||||
[Trash2, "Trash"],
|
||||
[Clock3, "Scheduled"],
|
||||
[Settings, "Settings"],
|
||||
];
|
||||
|
||||
const agentItems = [
|
||||
[Tags, "Tags"],
|
||||
[ListFilter, "Filters"],
|
||||
[Sparkles, "Summary"],
|
||||
[Plug, "Integration"],
|
||||
];
|
||||
|
||||
function clearSession() {
|
||||
sessionStorage.removeItem("agmail_access_token");
|
||||
sessionStorage.removeItem("agmail_account");
|
||||
}
|
||||
|
||||
function NavGroup({ icon: Icon, title, items, active, onSelect }) {
|
||||
return (
|
||||
<section className="dashboard-nav-group">
|
||||
<h2><Icon size={15} />{title}</h2>
|
||||
<nav aria-label={title}>
|
||||
{items.map(([ItemIcon, label]) => (
|
||||
<button
|
||||
className={active === label ? "active" : ""}
|
||||
key={label}
|
||||
onClick={() => onSelect(label)}
|
||||
type="button"
|
||||
>
|
||||
<ItemIcon size={17} />
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DashboardPage() {
|
||||
const [account, setAccount] = useState(null);
|
||||
const [companyDomain, setCompanyDomain] = useState(null);
|
||||
const [needsDnsSetup, setNeedsDnsSetup] = useState(false);
|
||||
const [domainError, setDomainError] = useState("");
|
||||
const [active, setActive] = useState("Inbox");
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [composeOpen, setComposeOpen] = useState(false);
|
||||
const [notice, setNotice] = useState("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [mailRefreshKey, setMailRefreshKey] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const token = sessionStorage.getItem("agmail_access_token");
|
||||
if (!token) {
|
||||
window.location.replace("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
getCurrentAccount(token)
|
||||
.then(async (currentAccount) => {
|
||||
sessionStorage.setItem("agmail_account", JSON.stringify(currentAccount));
|
||||
setAccount(currentAccount);
|
||||
if (currentAccount.account_type === "company") {
|
||||
try {
|
||||
const domain = await getMyCompanyDomain(token);
|
||||
setCompanyDomain(domain);
|
||||
setNeedsDnsSetup(domain.status === "pending_dns");
|
||||
} catch (error) {
|
||||
setDomainError(error.message || "Could not load your company domain.");
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
clearSession();
|
||||
window.location.replace("/login");
|
||||
});
|
||||
}, []);
|
||||
|
||||
function logout() {
|
||||
clearSession();
|
||||
window.location.assign("/login");
|
||||
}
|
||||
|
||||
function selectItem(item) {
|
||||
setActive(item);
|
||||
setMenuOpen(false);
|
||||
}
|
||||
|
||||
function emailSent() {
|
||||
setComposeOpen(false);
|
||||
setActive("Outbox");
|
||||
setMailRefreshKey((key) => key + 1);
|
||||
setNotice("Message accepted by the mail server. Delivery can take a few minutes — check spam if it does not arrive.");
|
||||
window.setTimeout(() => setNotice(""), 4000);
|
||||
}
|
||||
|
||||
if (loading || !account) {
|
||||
return <main className="dashboard-loading"><Brand /><span>Opening your workspace…</span></main>;
|
||||
}
|
||||
|
||||
if (account.account_type === "company" && domainError) {
|
||||
return (
|
||||
<main className="company-dns-setup">
|
||||
<header className="company-dns-header">
|
||||
<Brand />
|
||||
<button type="button" onClick={logout}>Log out</button>
|
||||
</header>
|
||||
<section className="company-dns-card">
|
||||
<p className="eyebrow">Company setup</p>
|
||||
<h1>Domain not ready</h1>
|
||||
<p className="form-error" role="alert">{domainError}</p>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
if (needsDnsSetup && companyDomain) {
|
||||
return (
|
||||
<CompanyDnsSetup
|
||||
account={account}
|
||||
domain={companyDomain}
|
||||
onComplete={() => {
|
||||
setCompanyDomain({ ...companyDomain, status: "active" });
|
||||
setNeedsDnsSetup(false);
|
||||
}}
|
||||
onLogout={logout}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const workspaceLabel = account.account_type === "company" ? "Company" : "Individual";
|
||||
const workspaceTitle = account.account_type === "company" && companyDomain
|
||||
? companyDomain.domain_name
|
||||
: "Personal workspace";
|
||||
|
||||
return (
|
||||
<div className="dashboard">
|
||||
<header className="dashboard-mobile-header">
|
||||
<Brand />
|
||||
<button aria-label="Open navigation" type="button" onClick={() => setMenuOpen(true)}>
|
||||
<Menu size={21} />
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{menuOpen && <button className="dashboard-scrim" aria-label="Close navigation" onClick={() => setMenuOpen(false)} />}
|
||||
<aside className={menuOpen ? "dashboard-sidebar open" : "dashboard-sidebar"}>
|
||||
<div className="dashboard-brand-row">
|
||||
<Brand />
|
||||
<button aria-label="Close navigation" type="button" onClick={() => setMenuOpen(false)}><X size={19} /></button>
|
||||
</div>
|
||||
<button className="dashboard-compose" type="button" onClick={() => setComposeOpen(true)}>
|
||||
<Mail size={17} />Compose
|
||||
</button>
|
||||
<NavGroup icon={Mail} title="Mail" items={mailItems} active={active} onSelect={selectItem} />
|
||||
<NavGroup icon={Bot} title="Agent" items={agentItems} active={active} onSelect={selectItem} />
|
||||
<div className="dashboard-profile">
|
||||
<span>{account.first_name[0]}{account.last_name[0]}</span>
|
||||
<div><strong>{account.first_name} {account.last_name}</strong><small>{account.email}</small></div>
|
||||
<button aria-label="Log out" title="Log out" type="button" onClick={logout}><LogOut size={17} /></button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="dashboard-content">
|
||||
{!["Inbox", "Outbox", "Trash"].includes(active) && (
|
||||
<div className="dashboard-content-heading">
|
||||
<div><p>{workspaceTitle}</p><h1>{active}</h1></div>
|
||||
<span className="dashboard-account-pill">{workspaceLabel}</span>
|
||||
</div>
|
||||
)}
|
||||
{active === "Inbox" && <MailboxView mailbox="inbox" title="Inbox" refreshKey={mailRefreshKey} />}
|
||||
{active === "Outbox" && <MailboxView mailbox="sent" title="Sent" refreshKey={mailRefreshKey} />}
|
||||
{active === "Trash" && <MailboxView mailbox="trash" title="Trash" refreshKey={mailRefreshKey} />}
|
||||
{!["Inbox", "Outbox", "Trash"].includes(active) && (
|
||||
<section className="dashboard-empty">
|
||||
<span><Sparkles size={24} /></span>
|
||||
<h2>{active} is ready for you</h2>
|
||||
<p>Your personal mail and agent tools will appear here.</p>
|
||||
</section>
|
||||
)}
|
||||
</main>
|
||||
{notice && <div className="dashboard-notice" role="status">{notice}</div>}
|
||||
{composeOpen && <ComposeModal account={account} onClose={() => setComposeOpen(false)} onSent={emailSent} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue