responsiveness fix
This commit is contained in:
parent
962f991580
commit
4ad05bf137
7 changed files with 671 additions and 5 deletions
|
|
@ -25,7 +25,7 @@ export default function ComposeDock({ account, onSent, agentOpen = true }) {
|
||||||
const focusRef = useRef(null);
|
const focusRef = useRef(null);
|
||||||
const viewport = useViewport();
|
const viewport = useViewport();
|
||||||
|
|
||||||
const agentReserve = agentOpen && viewport.width >= 960 ? AGENT_PANEL_WIDTH + MAIL_GAP : 0;
|
const agentReserve = agentOpen && viewport.width >= 1200 ? AGENT_PANEL_WIDTH + MAIL_GAP : 0;
|
||||||
const dockRight = agentReserve + 16;
|
const dockRight = agentReserve + 16;
|
||||||
|
|
||||||
const expandedWindows = useMemo(
|
const expandedWindows = useMemo(
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ import {
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { getCurrentAccount, getMyCompanyDomain } from "../api";
|
import { getCurrentAccount, getMyCompanyDomain } from "../api";
|
||||||
import Brand from "./Brand";
|
import Brand from "./Brand";
|
||||||
|
import MobileChrome from "./MobileChrome";
|
||||||
|
import { ShellModeContext } from "../shellMode";
|
||||||
import CompanyDnsSetup from "./CompanyDnsSetup";
|
import CompanyDnsSetup from "./CompanyDnsSetup";
|
||||||
import CompanyDomains from "./CompanyDomains";
|
import CompanyDomains from "./CompanyDomains";
|
||||||
import CompanyAdministrators from "./CompanyAdministrators";
|
import CompanyAdministrators from "./CompanyAdministrators";
|
||||||
|
|
@ -110,6 +112,7 @@ export default function DashboardPage() {
|
||||||
const [domainError, setDomainError] = useState("");
|
const [domainError, setDomainError] = useState("");
|
||||||
const [active, setActive] = useState("Inbox");
|
const [active, setActive] = useState("Inbox");
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
const [sheet, setSheet] = useState(null);
|
||||||
const [notice, setNotice] = useState("");
|
const [notice, setNotice] = useState("");
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [mailRefreshKey, setMailRefreshKey] = useState(0);
|
const [mailRefreshKey, setMailRefreshKey] = useState(0);
|
||||||
|
|
@ -174,6 +177,7 @@ export default function DashboardPage() {
|
||||||
setCompanyActive(label);
|
setCompanyActive(label);
|
||||||
setAgentNav(null);
|
setAgentNav(null);
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
|
setSheet(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectMailItem(item) {
|
function selectMailItem(item) {
|
||||||
|
|
@ -190,6 +194,7 @@ export default function DashboardPage() {
|
||||||
setLens("all");
|
setLens("all");
|
||||||
}
|
}
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
|
setSheet(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectLabel(name) {
|
function selectLabel(name) {
|
||||||
|
|
@ -198,6 +203,7 @@ export default function DashboardPage() {
|
||||||
setActive("Inbox");
|
setActive("Inbox");
|
||||||
setLens(name);
|
setLens(name);
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
|
setSheet(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectAgentItem(item, actionKey) {
|
function selectAgentItem(item, actionKey) {
|
||||||
|
|
@ -206,6 +212,32 @@ export default function DashboardPage() {
|
||||||
setLens("all");
|
setLens("all");
|
||||||
setAgentNav(actionKey);
|
setAgentNav(actionKey);
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
|
const compact = window.matchMedia("(max-width: 1199px)").matches;
|
||||||
|
setSheet(actionKey === "handoff" || !compact ? null : "agent");
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAgentDestination(actionKey) {
|
||||||
|
setCompanyActive(null);
|
||||||
|
setActive("Inbox");
|
||||||
|
setLens("all");
|
||||||
|
setAgentNav(actionKey);
|
||||||
|
setMenuOpen(false);
|
||||||
|
setSheet("agent");
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMailboxForSheet() {
|
||||||
|
setCompanyActive(null);
|
||||||
|
setActive("Inbox");
|
||||||
|
setLens("all");
|
||||||
|
setMenuOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSheet(next) {
|
||||||
|
if (next === "agent" || next === "calendar" || next === "notes") {
|
||||||
|
showMailboxForSheet();
|
||||||
|
setAgentNav(next === "agent" ? "ask" : null);
|
||||||
|
}
|
||||||
|
setSheet(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCompose(context = null) {
|
function openCompose(context = null) {
|
||||||
|
|
@ -277,10 +309,15 @@ export default function DashboardPage() {
|
||||||
: active;
|
: active;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dashboard">
|
<ShellModeContext.Provider value={{ sheet, setSheet }}>
|
||||||
|
<div
|
||||||
|
className="dashboard"
|
||||||
|
data-sheet={sheet || undefined}
|
||||||
|
data-onboarding={isOrgOnboarding ? "true" : undefined}
|
||||||
|
>
|
||||||
<header className="dashboard-mobile-header">
|
<header className="dashboard-mobile-header">
|
||||||
<Brand />
|
<Brand />
|
||||||
<button aria-label="Open navigation" type="button" onClick={() => setMenuOpen(true)}>
|
<button aria-label="Open navigation" type="button" onClick={() => { setSheet(null); setMenuOpen(true); }}>
|
||||||
<Menu size={21} />
|
<Menu size={21} />
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
@ -525,7 +562,31 @@ export default function DashboardPage() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{sheet && sheet !== "more" && (
|
||||||
|
<button className="shell-drawer-scrim" type="button" aria-label="Close panel" onClick={() => setSheet(null)} />
|
||||||
|
)}
|
||||||
|
{!isOrgOnboarding && (
|
||||||
|
<MobileChrome
|
||||||
|
sheet={sheet}
|
||||||
|
mailActive={!sheet && !companyActive && agentNav !== "handoff"}
|
||||||
|
account={account}
|
||||||
|
onMail={() => selectMailItem("Inbox")}
|
||||||
|
onAgent={() => openSheet("agent")}
|
||||||
|
onCompose={() => openCompose()}
|
||||||
|
onCalendar={() => openSheet("calendar")}
|
||||||
|
onMore={() => setSheet((current) => (current === "more" ? null : "more"))}
|
||||||
|
onClose={() => setSheet(null)}
|
||||||
|
onSettings={() => selectMailItem("Settings")}
|
||||||
|
onNotes={() => openSheet("notes")}
|
||||||
|
onHandoff={() => selectAgentItem("Human Handoff", "handoff")}
|
||||||
|
onActionItems={() => openAgentDestination("actions")}
|
||||||
|
onSummaries={() => openAgentDestination("summaries")}
|
||||||
|
onIntegrations={() => openAgentDestination("integrations")}
|
||||||
|
onLogout={logout}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<ComposeDock account={account} onSent={emailSent} agentOpen={!companyActive} />
|
<ComposeDock account={account} onSent={emailSent} agentOpen={!companyActive} />
|
||||||
</div>
|
</div>
|
||||||
|
</ShellModeContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
Search,
|
Search,
|
||||||
Settings,
|
Settings,
|
||||||
|
Sparkles,
|
||||||
Sun,
|
Sun,
|
||||||
Tag,
|
Tag,
|
||||||
Users,
|
Users,
|
||||||
|
|
@ -37,6 +38,8 @@ import MailRow from "./MailRow";
|
||||||
import MailboxEmptyState from "./MailboxEmptyState";
|
import MailboxEmptyState from "./MailboxEmptyState";
|
||||||
import MailboxSelectionBar from "./MailboxSelectionBar";
|
import MailboxSelectionBar from "./MailboxSelectionBar";
|
||||||
import StickyNotesPanel from "./StickyNotesPanel";
|
import StickyNotesPanel from "./StickyNotesPanel";
|
||||||
|
import { useShellMode } from "../shellMode";
|
||||||
|
import { shellTitle } from "./MobileChrome";
|
||||||
import {
|
import {
|
||||||
categoryCounts,
|
categoryCounts,
|
||||||
emailCategory,
|
emailCategory,
|
||||||
|
|
@ -118,6 +121,7 @@ export default function MailboxView({
|
||||||
onStats,
|
onStats,
|
||||||
}) {
|
}) {
|
||||||
const token = sessionStorage.getItem("agmail_access_token");
|
const token = sessionStorage.getItem("agmail_access_token");
|
||||||
|
const { sheet, setSheet } = useShellMode();
|
||||||
const [emails, setEmails] = useState([]);
|
const [emails, setEmails] = useState([]);
|
||||||
const [nextCursor, setNextCursor] = useState(null);
|
const [nextCursor, setNextCursor] = useState(null);
|
||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
|
|
@ -679,6 +683,14 @@ export default function MailboxView({
|
||||||
<button className="workspace-ask agent-action" type="button" onClick={() => runQuickAction("summarize")}>
|
<button className="workspace-ask agent-action" type="button" onClick={() => runQuickAction("summarize")}>
|
||||||
<AgentSpark /> Ask Agent
|
<AgentSpark /> Ask Agent
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
className="shell-agent-toggle"
|
||||||
|
type="button"
|
||||||
|
aria-label="Open AG Agent"
|
||||||
|
onClick={() => setSheet(sheet === "agent" ? null : "agent")}
|
||||||
|
>
|
||||||
|
<Sparkles size={16} />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="workspace-zone workspace-zone-account">
|
<div className="workspace-zone workspace-zone-account">
|
||||||
<button type="button" className="workspace-icon" aria-label="Refresh" onClick={load}><RefreshCw size={16} /></button>
|
<button type="button" className="workspace-icon" aria-label="Refresh" onClick={load}><RefreshCw size={16} /></button>
|
||||||
|
|
@ -840,7 +852,7 @@ export default function MailboxView({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={`mailbox-rows${isEmpty ? " is-empty" : ""}`} aria-busy={status.loading}>
|
<div className={`mailbox-rows${isEmpty ? " is-empty" : ""}${selectedCount > 0 ? " selecting" : ""}`} aria-busy={status.loading}>
|
||||||
{status.loading && <div className="mailbox-state loading">Loading messages…</div>}
|
{status.loading && <div className="mailbox-state loading">Loading messages…</div>}
|
||||||
{status.error && (
|
{status.error && (
|
||||||
<div className="mailbox-state error" role="alert">
|
<div className="mailbox-state error" role="alert">
|
||||||
|
|
@ -916,7 +928,15 @@ export default function MailboxView({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside className="mailbox-rail">
|
<aside className={`mailbox-rail${sheet ? ` shell-${sheet}` : ""}`}>
|
||||||
|
{(sheet === "agent" || sheet === "calendar" || sheet === "notes") && (
|
||||||
|
<div className="shell-sheet-bar">
|
||||||
|
<button type="button" aria-label="Back" onClick={() => setSheet(null)}>
|
||||||
|
<ChevronLeft size={18} strokeWidth={1.75} />
|
||||||
|
</button>
|
||||||
|
<strong>{shellTitle(sheet)}</strong>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<AgAgentPanel
|
<AgAgentPanel
|
||||||
account={account}
|
account={account}
|
||||||
emails={listedEmails}
|
emails={listedEmails}
|
||||||
|
|
|
||||||
101
frontend/src/components/MobileChrome.jsx
Normal file
101
frontend/src/components/MobileChrome.jsx
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
import {
|
||||||
|
CalendarDays,
|
||||||
|
CircleHelp,
|
||||||
|
HandHelping,
|
||||||
|
Inbox,
|
||||||
|
ListChecks,
|
||||||
|
LogOut,
|
||||||
|
MoreHorizontal,
|
||||||
|
Plug,
|
||||||
|
Plus,
|
||||||
|
Settings,
|
||||||
|
Sparkles,
|
||||||
|
StickyNote,
|
||||||
|
Sun,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
|
const SHEET_LABEL = {
|
||||||
|
agent: "AG Agent",
|
||||||
|
calendar: "Calendar",
|
||||||
|
notes: "Notes",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function shellTitle(sheet) {
|
||||||
|
return SHEET_LABEL[sheet] || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MobileChrome({
|
||||||
|
sheet,
|
||||||
|
mailActive,
|
||||||
|
account,
|
||||||
|
onMail,
|
||||||
|
onAgent,
|
||||||
|
onCompose,
|
||||||
|
onCalendar,
|
||||||
|
onMore,
|
||||||
|
onClose,
|
||||||
|
onSettings,
|
||||||
|
onNotes,
|
||||||
|
onHandoff,
|
||||||
|
onActionItems,
|
||||||
|
onSummaries,
|
||||||
|
onIntegrations,
|
||||||
|
onLogout,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<nav className="mobile-bottom-nav" aria-label="App">
|
||||||
|
<button type="button" className={mailActive ? "active" : ""} onClick={onMail}>
|
||||||
|
<Inbox size={18} strokeWidth={1.75} />
|
||||||
|
<span>Mail</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" className={sheet === "agent" ? "active" : ""} onClick={onAgent}>
|
||||||
|
<Sparkles size={18} strokeWidth={1.75} />
|
||||||
|
<span>Agent</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" className="mobile-bottom-compose" onClick={onCompose}>
|
||||||
|
<span className="mobile-bottom-plus" aria-hidden="true"><Plus size={20} strokeWidth={2} /></span>
|
||||||
|
<span>Compose</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" className={sheet === "calendar" ? "active" : ""} onClick={onCalendar}>
|
||||||
|
<CalendarDays size={18} strokeWidth={1.75} />
|
||||||
|
<span>Calendar</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" className={sheet === "more" || sheet === "notes" ? "active" : ""} onClick={onMore}>
|
||||||
|
<MoreHorizontal size={18} strokeWidth={1.75} />
|
||||||
|
<span>More</span>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{sheet === "more" && (
|
||||||
|
<div className="mobile-more-layer">
|
||||||
|
<button type="button" className="mobile-more-scrim" aria-label="Close more" onClick={onClose} />
|
||||||
|
<section className="mobile-more-sheet" role="dialog" aria-label="More">
|
||||||
|
<header className="mobile-more-head">
|
||||||
|
<strong>More</strong>
|
||||||
|
<button type="button" onClick={onClose} aria-label="Close">Close</button>
|
||||||
|
</header>
|
||||||
|
<div className="mobile-more-account">
|
||||||
|
<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 type="button" aria-label="Log out" onClick={onLogout}><LogOut size={16} /></button>
|
||||||
|
</div>
|
||||||
|
<p>Workspace</p>
|
||||||
|
<button type="button" onClick={onSettings}><Settings size={16} /> Settings</button>
|
||||||
|
<button type="button" onClick={onNotes}><StickyNote size={16} /> Notes</button>
|
||||||
|
<button type="button" onClick={onClose}><CircleHelp size={16} /> Help</button>
|
||||||
|
<button type="button" onClick={onIntegrations}><Plug size={16} /> Integrations</button>
|
||||||
|
<button type="button" onClick={onClose}><Sun size={16} /> Appearance</button>
|
||||||
|
<p>Agent</p>
|
||||||
|
<button type="button" onClick={onHandoff}><HandHelping size={16} /> Human Handoff</button>
|
||||||
|
<button type="button" onClick={onActionItems}><ListChecks size={16} /> Action Items</button>
|
||||||
|
<button type="button" onClick={onSummaries}><Sparkles size={16} /> Summaries</button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
import "./responsive.css";
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
|
|
||||||
473
frontend/src/responsive.css
Normal file
473
frontend/src/responsive.css
Normal file
|
|
@ -0,0 +1,473 @@
|
||||||
|
/* Responsive presentation only. Desktop rules at min-width 1200px stay in styles.css. */
|
||||||
|
|
||||||
|
.mobile-bottom-nav,
|
||||||
|
.shell-agent-toggle,
|
||||||
|
.shell-drawer-scrim,
|
||||||
|
.shell-sheet-bar,
|
||||||
|
.mobile-more-layer { display:none; }
|
||||||
|
|
||||||
|
@media (min-width:768px) and (max-width:1199px) {
|
||||||
|
html,body { overflow-x:clip; }
|
||||||
|
.dashboard { display:grid; grid-template-columns:72px minmax(0,1fr); overflow-x:clip; }
|
||||||
|
.dashboard-mobile-header { display:none; }
|
||||||
|
.dashboard-sidebar {
|
||||||
|
width:72px;
|
||||||
|
position:sticky;
|
||||||
|
top:0;
|
||||||
|
left:auto;
|
||||||
|
transform:none;
|
||||||
|
padding:16px 8px 12px;
|
||||||
|
z-index:12;
|
||||||
|
}
|
||||||
|
.dashboard-brand-row { justify-content:center; padding:0; }
|
||||||
|
.dashboard-brand-row .brand > span:not(.brand-mark),
|
||||||
|
.dashboard-compose span,
|
||||||
|
.dashboard-sidebar-section h2,
|
||||||
|
.dashboard-nav-item em,
|
||||||
|
.dashboard-profile > div { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }
|
||||||
|
.dashboard-brand-row > button,
|
||||||
|
.dashboard-profile > button { display:none; }
|
||||||
|
.dashboard-compose { width:44px; min-height:40px; margin:16px auto 0; padding:0; }
|
||||||
|
.dashboard-nav-item { justify-content:center; padding:0; gap:0; }
|
||||||
|
.dashboard-nav-item span { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0,0,0,0); }
|
||||||
|
.dashboard-profile { grid-template-columns:34px; justify-content:center; padding-left:0; padding-right:0; }
|
||||||
|
.dashboard-content { min-width:0; padding:12px 12px 16px; }
|
||||||
|
.workspace-header { display:flex; flex-wrap:wrap; height:auto; min-height:56px; }
|
||||||
|
.workspace-zone-command { flex:1 1 220px; }
|
||||||
|
.workspace-zone-command .workspace-search { width:100%; max-width:none; }
|
||||||
|
.workspace-account div,
|
||||||
|
.workspace-account > svg { display:none; }
|
||||||
|
.mailbox-stage { grid-template-columns:minmax(0,1fr); }
|
||||||
|
.mailbox-rail {
|
||||||
|
display:flex;
|
||||||
|
position:fixed;
|
||||||
|
top:0;
|
||||||
|
right:0;
|
||||||
|
bottom:0;
|
||||||
|
width:min(320px,86vw);
|
||||||
|
min-width:0;
|
||||||
|
max-width:320px;
|
||||||
|
z-index:45;
|
||||||
|
transform:translateX(105%);
|
||||||
|
transition:transform .25s ease;
|
||||||
|
overflow:auto;
|
||||||
|
background:var(--canvas);
|
||||||
|
padding:12px;
|
||||||
|
box-shadow:-12px 0 40px rgba(8,8,33,.16);
|
||||||
|
}
|
||||||
|
.dashboard[data-sheet="agent"] .mailbox-rail,
|
||||||
|
.dashboard[data-sheet="calendar"] .mailbox-rail,
|
||||||
|
.dashboard[data-sheet="notes"] .mailbox-rail { transform:none; }
|
||||||
|
.mailbox-notes-pane { display:none; }
|
||||||
|
.mailbox-rail.shell-notes .mailbox-notes-pane { display:flex; min-height:0; max-height:none; }
|
||||||
|
.shell-agent-toggle {
|
||||||
|
width:36px;
|
||||||
|
height:36px;
|
||||||
|
display:grid;
|
||||||
|
place-items:center;
|
||||||
|
border:1px solid var(--agent-border);
|
||||||
|
border-radius:var(--radius-sm);
|
||||||
|
background:var(--agent-soft);
|
||||||
|
color:var(--agent);
|
||||||
|
}
|
||||||
|
.dashboard[data-sheet="agent"] .shell-drawer-scrim,
|
||||||
|
.dashboard[data-sheet="calendar"] .shell-drawer-scrim,
|
||||||
|
.dashboard[data-sheet="notes"] .shell-drawer-scrim {
|
||||||
|
display:block;
|
||||||
|
position:fixed;
|
||||||
|
inset:0;
|
||||||
|
z-index:44;
|
||||||
|
border:0;
|
||||||
|
background:#08082155;
|
||||||
|
}
|
||||||
|
.shell-sheet-bar {
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
gap:8px;
|
||||||
|
min-height:44px;
|
||||||
|
margin-bottom:8px;
|
||||||
|
}
|
||||||
|
.shell-sheet-bar button {
|
||||||
|
width:32px;
|
||||||
|
height:32px;
|
||||||
|
display:grid;
|
||||||
|
place-items:center;
|
||||||
|
border:0;
|
||||||
|
border-radius:8px;
|
||||||
|
background:transparent;
|
||||||
|
color:#4a4652;
|
||||||
|
}
|
||||||
|
.shell-sheet-bar strong { font-size:14px; color:#171424; }
|
||||||
|
.compose-dock { right:12px !important; }
|
||||||
|
.compose-window {
|
||||||
|
width:min(640px,calc(100vw - 96px));
|
||||||
|
min-width:0;
|
||||||
|
max-width:calc(100vw - 96px);
|
||||||
|
height:min(640px,calc(100vh - 32px));
|
||||||
|
}
|
||||||
|
.compose-window.expanded {
|
||||||
|
width:min(900px,calc(100vw - 48px));
|
||||||
|
min-width:0;
|
||||||
|
max-width:calc(100vw - 48px);
|
||||||
|
height:min(78vh,calc(100vh - 32px));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:767px) {
|
||||||
|
html,body { overflow-x:clip; }
|
||||||
|
.dashboard { display:block; min-height:100vh; overflow-x:clip; }
|
||||||
|
.dashboard-mobile-header {
|
||||||
|
height:56px;
|
||||||
|
padding:0 16px;
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:space-between;
|
||||||
|
position:sticky;
|
||||||
|
top:0;
|
||||||
|
z-index:40;
|
||||||
|
border-bottom:1px solid var(--line);
|
||||||
|
background:#fffefd;
|
||||||
|
}
|
||||||
|
.dashboard-mobile-header .brand { font-size:18px; gap:8px; }
|
||||||
|
.dashboard-mobile-header .brand-mark { width:30px; height:28px; }
|
||||||
|
.dashboard-mobile-header > button { width:32px; height:32px; }
|
||||||
|
.dashboard-sidebar {
|
||||||
|
width:min(320px,86vw);
|
||||||
|
height:100vh;
|
||||||
|
max-height:100vh;
|
||||||
|
position:fixed;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
z-index:70;
|
||||||
|
transform:translateX(-105%);
|
||||||
|
transition:transform .25s ease;
|
||||||
|
}
|
||||||
|
.dashboard-sidebar.open { transform:translateX(0); }
|
||||||
|
.dashboard-brand-row > button { display:grid; }
|
||||||
|
.dashboard-scrim { display:block; position:fixed; inset:0; z-index:65; border:0; background:#08082155; }
|
||||||
|
.dashboard-content {
|
||||||
|
min-width:0;
|
||||||
|
padding:0 0 calc(76px + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
.dashboard-content > *:not(.mailbox-workspace) { padding:16px 16px 0; }
|
||||||
|
.dashboard-content h1 { font-size:28px; letter-spacing:-0.03em; overflow-wrap:anywhere; }
|
||||||
|
.human-handoff-page { max-width:none; margin:0; padding:0; }
|
||||||
|
.handoff-page-title,
|
||||||
|
.human-handoff-head h1.handoff-page-title { white-space:normal; font-size:22px; }
|
||||||
|
.human-handoff-card-body {
|
||||||
|
grid-template-areas:"avatar name status" "avatar email email" "actions actions actions";
|
||||||
|
grid-template-columns:36px minmax(0,1fr) auto;
|
||||||
|
}
|
||||||
|
.human-handoff-card-actions { margin-top:8px; flex-wrap:wrap; justify-content:flex-end; }
|
||||||
|
.human-handoff-card-chips { padding-left:0; }
|
||||||
|
.handoff-drawer,.handoff-test-modal,.people-drawer { width:100%; max-width:100%; }
|
||||||
|
|
||||||
|
.mailbox-workspace { gap:0; }
|
||||||
|
.workspace-header {
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
align-items:stretch;
|
||||||
|
gap:8px;
|
||||||
|
min-height:0;
|
||||||
|
margin:12px 16px 8px;
|
||||||
|
padding:0;
|
||||||
|
background:transparent;
|
||||||
|
border-radius:0;
|
||||||
|
}
|
||||||
|
.workspace-zone-command { padding:0; }
|
||||||
|
.workspace-zone-command .workspace-search { width:100%; max-width:none; height:42px; }
|
||||||
|
.workspace-search-kbd,.workspace-search kbd { display:none; }
|
||||||
|
.workspace-zone-account { display:none; }
|
||||||
|
.workspace-zone-actions { width:100%; padding:0; justify-content:space-between; }
|
||||||
|
.mailbox-shell { margin-top:0; border-radius:0; }
|
||||||
|
.mailbox-stage { display:block; }
|
||||||
|
.mailbox-main-column { min-width:0; }
|
||||||
|
.mailbox-list-bar { min-height:40px; padding:0 8px; }
|
||||||
|
.mailbox-pagination-range { font-size:11px; }
|
||||||
|
.mailbox-categories {
|
||||||
|
display:flex;
|
||||||
|
flex-wrap:nowrap;
|
||||||
|
overflow-x:auto;
|
||||||
|
white-space:nowrap;
|
||||||
|
scrollbar-width:none;
|
||||||
|
}
|
||||||
|
.mailbox-categories::-webkit-scrollbar { display:none; }
|
||||||
|
.mailbox-categories button { flex:0 0 auto; }
|
||||||
|
.mailbox-filters { grid-template-columns:1fr; }
|
||||||
|
.mailbox-selection-bar { display:flex; flex-wrap:wrap; }
|
||||||
|
.mailbox-selection-actions { overflow-x:auto; flex-wrap:nowrap; max-width:100%; }
|
||||||
|
.mailbox-rows { max-height:none; }
|
||||||
|
.mailbox-row {
|
||||||
|
height:auto;
|
||||||
|
min-height:66px;
|
||||||
|
max-height:none;
|
||||||
|
padding:9px 12px;
|
||||||
|
grid-template-columns:30px minmax(0,1fr) auto;
|
||||||
|
grid-template-rows:auto auto auto;
|
||||||
|
column-gap:10px;
|
||||||
|
row-gap:1px;
|
||||||
|
align-items:center;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
.mailbox-check { position:absolute; width:1px; height:1px; margin:0; overflow:hidden; opacity:0; }
|
||||||
|
.mailbox-rows.selecting .mailbox-row { grid-template-columns:16px 30px minmax(0,1fr) auto; }
|
||||||
|
.mailbox-rows.selecting .mailbox-check {
|
||||||
|
position:static;
|
||||||
|
width:16px;
|
||||||
|
height:16px;
|
||||||
|
opacity:1;
|
||||||
|
grid-column:1;
|
||||||
|
grid-row:1 / span 3;
|
||||||
|
}
|
||||||
|
.mailbox-avatar { grid-column:1; grid-row:1 / span 3; align-self:center; width:30px; height:30px; }
|
||||||
|
.mailbox-rows.selecting .mailbox-avatar { grid-column:2; }
|
||||||
|
.mailbox-sender-wrap { grid-column:2; grid-row:1; align-self:end; }
|
||||||
|
.mailbox-rows.selecting .mailbox-sender-wrap { grid-column:3; }
|
||||||
|
.mailbox-sender { font-size:12px; font-weight:600; }
|
||||||
|
.mailbox-subject-line {
|
||||||
|
grid-column:2;
|
||||||
|
grid-row:2;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
align-items:flex-start;
|
||||||
|
white-space:normal;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
.mailbox-rows.selecting .mailbox-subject-line { grid-column:3; }
|
||||||
|
.mailbox-subject { font-size:11.5px; font-weight:550; }
|
||||||
|
.mailbox-preview { display:block; max-width:100%; font-size:10.5px; white-space:nowrap; }
|
||||||
|
.mailbox-preview-sep { display:none; }
|
||||||
|
.mailbox-row-labels {
|
||||||
|
grid-column:2;
|
||||||
|
grid-row:3;
|
||||||
|
min-width:0;
|
||||||
|
justify-content:flex-start;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
.mailbox-rows.selecting .mailbox-row-labels { grid-column:3; }
|
||||||
|
.mailbox-row-labels:empty { display:none; }
|
||||||
|
.mailbox-attachment-icon.empty { display:none; }
|
||||||
|
.mailbox-attachment-icon { grid-column:3; grid-row:2; width:16px; height:16px; }
|
||||||
|
.mailbox-rows.selecting .mailbox-attachment-icon { grid-column:4; }
|
||||||
|
.mailbox-row-rail { grid-column:3; grid-row:1; height:auto; align-self:start; }
|
||||||
|
.mailbox-rows.selecting .mailbox-row-rail { grid-column:4; }
|
||||||
|
.mailbox-time { font-size:9.5px; opacity:1; }
|
||||||
|
.mailbox-row:hover .mailbox-time,
|
||||||
|
.mailbox-row:focus-within .mailbox-time,
|
||||||
|
.mailbox-row.selected .mailbox-time { opacity:1; }
|
||||||
|
.mailbox-hover-actions { display:none; }
|
||||||
|
.mailbox-star { grid-column:3; grid-row:3; width:28px; height:28px; align-self:end; }
|
||||||
|
.mailbox-rows.selecting .mailbox-star { grid-column:4; }
|
||||||
|
|
||||||
|
.mailbox-rail { display:none; }
|
||||||
|
.dashboard[data-sheet="agent"] .mailbox-rail,
|
||||||
|
.dashboard[data-sheet="calendar"] .mailbox-rail,
|
||||||
|
.dashboard[data-sheet="notes"] .mailbox-rail {
|
||||||
|
display:flex;
|
||||||
|
position:fixed;
|
||||||
|
top:56px;
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
bottom:calc(64px + env(safe-area-inset-bottom));
|
||||||
|
width:auto;
|
||||||
|
min-width:0;
|
||||||
|
max-width:none;
|
||||||
|
z-index:35;
|
||||||
|
transform:none;
|
||||||
|
overflow:auto;
|
||||||
|
background:var(--canvas);
|
||||||
|
padding:12px 16px 16px;
|
||||||
|
}
|
||||||
|
.dashboard[data-sheet="agent"] .mailbox-main-column,
|
||||||
|
.dashboard[data-sheet="agent"] .workspace-header,
|
||||||
|
.dashboard[data-sheet="calendar"] .mailbox-main-column,
|
||||||
|
.dashboard[data-sheet="calendar"] .workspace-header,
|
||||||
|
.dashboard[data-sheet="notes"] .mailbox-main-column,
|
||||||
|
.dashboard[data-sheet="notes"] .workspace-header { display:none; }
|
||||||
|
.mailbox-notes-pane { display:none; min-height:0; max-height:none; }
|
||||||
|
.mailbox-rail.shell-notes .agent-panel { display:none; }
|
||||||
|
.mailbox-rail.shell-notes .mailbox-notes-pane { display:flex; width:100%; }
|
||||||
|
.shell-sheet-bar {
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
gap:8px;
|
||||||
|
min-height:40px;
|
||||||
|
margin:0 0 8px;
|
||||||
|
}
|
||||||
|
.shell-sheet-bar button {
|
||||||
|
width:32px;
|
||||||
|
height:32px;
|
||||||
|
display:grid;
|
||||||
|
place-items:center;
|
||||||
|
border:0;
|
||||||
|
border-radius:8px;
|
||||||
|
background:transparent;
|
||||||
|
color:#4a4652;
|
||||||
|
}
|
||||||
|
.shell-sheet-bar strong { font:650 16px/1.2 "DM Sans"; color:#171424; }
|
||||||
|
.agent-panel { overflow:visible; }
|
||||||
|
.mail-detail-toolbar { overflow-x:auto; }
|
||||||
|
|
||||||
|
.mobile-bottom-nav {
|
||||||
|
position:fixed;
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
bottom:0;
|
||||||
|
z-index:50;
|
||||||
|
height:calc(64px + env(safe-area-inset-bottom));
|
||||||
|
padding:4px 6px env(safe-area-inset-bottom);
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:repeat(5,minmax(0,1fr));
|
||||||
|
align-items:center;
|
||||||
|
background:#fff;
|
||||||
|
border-top:1px solid var(--line);
|
||||||
|
}
|
||||||
|
.mobile-bottom-nav button {
|
||||||
|
min-width:0;
|
||||||
|
min-height:44px;
|
||||||
|
padding:0;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:center;
|
||||||
|
gap:2px;
|
||||||
|
border:0;
|
||||||
|
background:transparent;
|
||||||
|
color:#6f6b76;
|
||||||
|
font-size:10px;
|
||||||
|
font-weight:600;
|
||||||
|
}
|
||||||
|
.mobile-bottom-nav button.active { color:var(--coral); }
|
||||||
|
.mobile-bottom-plus {
|
||||||
|
width:36px;
|
||||||
|
height:28px;
|
||||||
|
display:grid;
|
||||||
|
place-items:center;
|
||||||
|
border-radius:10px;
|
||||||
|
color:#fff;
|
||||||
|
background:linear-gradient(180deg,#ff806d 0%,#ff5c55 100%);
|
||||||
|
box-shadow:0 4px 14px rgba(255,88,72,.24);
|
||||||
|
}
|
||||||
|
.mobile-bottom-compose { color:#c95945; }
|
||||||
|
body.compose-open .mobile-bottom-nav { display:none; }
|
||||||
|
|
||||||
|
.mobile-more-layer {
|
||||||
|
position:fixed;
|
||||||
|
inset:0;
|
||||||
|
z-index:60;
|
||||||
|
display:flex;
|
||||||
|
align-items:flex-end;
|
||||||
|
}
|
||||||
|
.mobile-more-scrim { position:absolute; inset:0; border:0; background:#08082155; }
|
||||||
|
.mobile-more-sheet {
|
||||||
|
position:relative;
|
||||||
|
width:100%;
|
||||||
|
max-height:min(78vh,640px);
|
||||||
|
overflow:auto;
|
||||||
|
padding:8px 16px calc(16px + env(safe-area-inset-bottom));
|
||||||
|
border-radius:16px 16px 0 0;
|
||||||
|
background:#fff;
|
||||||
|
box-shadow:0 -12px 40px rgba(8,8,33,.16);
|
||||||
|
}
|
||||||
|
.mobile-more-head,.mobile-more-account,.mobile-more-sheet > button {
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
}
|
||||||
|
.mobile-more-head { justify-content:space-between; min-height:44px; }
|
||||||
|
.mobile-more-head strong { font-size:16px; color:#171424; }
|
||||||
|
.mobile-more-head button,.mobile-more-account button {
|
||||||
|
border:0;
|
||||||
|
background:transparent;
|
||||||
|
color:#6f6b76;
|
||||||
|
font-size:12px;
|
||||||
|
font-weight:600;
|
||||||
|
}
|
||||||
|
.mobile-more-account {
|
||||||
|
gap:10px;
|
||||||
|
margin-bottom:8px;
|
||||||
|
padding-bottom:10px;
|
||||||
|
border-bottom:1px solid var(--line);
|
||||||
|
}
|
||||||
|
.mobile-more-account > span {
|
||||||
|
width:34px;
|
||||||
|
height:34px;
|
||||||
|
display:grid;
|
||||||
|
place-items:center;
|
||||||
|
border-radius:11px;
|
||||||
|
background:linear-gradient(180deg,#ff9a82,#ff6f57);
|
||||||
|
color:#2a2038;
|
||||||
|
font-size:11px;
|
||||||
|
font-weight:700;
|
||||||
|
}
|
||||||
|
.mobile-more-account div { min-width:0; flex:1; display:grid; }
|
||||||
|
.mobile-more-account strong,.mobile-more-account small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||||
|
.mobile-more-account strong { font-size:13px; color:#171424; }
|
||||||
|
.mobile-more-account small { color:#8d8792; font-size:11px; }
|
||||||
|
.mobile-more-sheet > p {
|
||||||
|
margin:12px 0 4px;
|
||||||
|
color:#97929a;
|
||||||
|
font-size:10px;
|
||||||
|
font-weight:700;
|
||||||
|
letter-spacing:.08em;
|
||||||
|
text-transform:uppercase;
|
||||||
|
}
|
||||||
|
.mobile-more-sheet > button {
|
||||||
|
width:100%;
|
||||||
|
min-height:42px;
|
||||||
|
gap:10px;
|
||||||
|
border:0;
|
||||||
|
border-radius:10px;
|
||||||
|
background:transparent;
|
||||||
|
color:#3d3948;
|
||||||
|
font-size:14px;
|
||||||
|
font-weight:600;
|
||||||
|
text-align:left;
|
||||||
|
}
|
||||||
|
.mobile-more-sheet > button:hover { background:#faf5f2; }
|
||||||
|
|
||||||
|
.compose-dock {
|
||||||
|
position:fixed;
|
||||||
|
inset:0;
|
||||||
|
right:0 !important;
|
||||||
|
bottom:0;
|
||||||
|
width:100%;
|
||||||
|
z-index:80;
|
||||||
|
align-items:stretch;
|
||||||
|
}
|
||||||
|
.compose-dock-expanded { width:100%; height:100%; }
|
||||||
|
.compose-window,
|
||||||
|
.compose-window.docked,
|
||||||
|
.compose-window.expanded {
|
||||||
|
position:fixed;
|
||||||
|
inset:0;
|
||||||
|
left:0;
|
||||||
|
top:0;
|
||||||
|
right:0;
|
||||||
|
bottom:0;
|
||||||
|
transform:none;
|
||||||
|
width:100%;
|
||||||
|
min-width:0;
|
||||||
|
max-width:none;
|
||||||
|
height:100dvh;
|
||||||
|
border-radius:0;
|
||||||
|
z-index:80;
|
||||||
|
}
|
||||||
|
.dashboard-notice { left:16px; right:16px; bottom:calc(76px + env(safe-area-inset-bottom)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:1199px) {
|
||||||
|
.mailbox-rail.shell-calendar .agent-panel-hello,
|
||||||
|
.mailbox-rail.shell-calendar .agent-stat-grid,
|
||||||
|
.mailbox-rail.shell-calendar .agent-quick,
|
||||||
|
.mailbox-rail.shell-calendar .agent-attention-block,
|
||||||
|
.mailbox-rail.shell-calendar .agent-deadlines,
|
||||||
|
.mailbox-rail.shell-calendar .agent-panel-note,
|
||||||
|
.mailbox-rail.shell-calendar .agent-loading-block,
|
||||||
|
.mailbox-rail.shell-calendar .agent-caught-up,
|
||||||
|
.mailbox-rail.shell-calendar .agent-ready-card,
|
||||||
|
.mailbox-rail.shell-calendar .agent-email-context,
|
||||||
|
.mailbox-rail.shell-calendar .agent-insight,
|
||||||
|
.mailbox-rail.shell-calendar .mailbox-notes-pane,
|
||||||
|
.mailbox-rail.shell-agent .mailbox-notes-pane { display:none; }
|
||||||
|
.mailbox-rail.shell-notes .agent-panel { display:none; }
|
||||||
|
}
|
||||||
10
frontend/src/shellMode.js
Normal file
10
frontend/src/shellMode.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
|
export const ShellModeContext = createContext({
|
||||||
|
sheet: null,
|
||||||
|
setSheet: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function useShellMode() {
|
||||||
|
return useContext(ShellModeContext);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue