fix(rinbox): clean up sub-tab header — mailboxes + approvals only, info icon, space mailbox primary
Remove Personal, Agents, Tour from nav header. Move Guide/About behind info icon (ⓘ). Space mailbox ({space}@rspace.online) shown first with highlighted card spanning full width. Add demo threads for space mailbox. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
696ab3cfd9
commit
8f9d507440
|
|
@ -78,6 +78,12 @@ class FolkInboxClient extends HTMLElement {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
private demoThreads: Record<string, any[]> = {
|
private demoThreads: Record<string, any[]> = {
|
||||||
|
space: [
|
||||||
|
{ id: "s1", from_name: "Alice Chen", from_address: "alice@example.com", subject: "Welcome to the space mailbox", status: "open", is_read: true, is_starred: true, replied: false, forwarded: false, needsAttention: false, comment_count: 1, received_at: new Date(Date.now() - 1 * 3600000).toISOString(), body_text: "This is the shared space mailbox. All space members can read and triage messages here.", direction: 'inbound', comments: [{ username: "Bob Martinez", body: "Great, all set up!", created_at: new Date(Date.now() - 0.5 * 3600000).toISOString() }] },
|
||||||
|
{ id: "s2", from_name: "External Contact", from_address: "contact@example.org", subject: "Partnership inquiry", status: "open", is_read: false, is_starred: false, replied: false, forwarded: false, needsAttention: true, comment_count: 0, received_at: new Date(Date.now() - 3 * 3600000).toISOString(), body_text: "We'd love to discuss a potential collaboration with your community. Could we schedule a call?", direction: 'inbound', comments: [] },
|
||||||
|
{ id: "s3", from_name: "Community Bot", from_address: "bot@rspace.online", subject: "Weekly digest: 12 new members, 3 proposals", status: "open", is_read: true, is_starred: false, replied: false, forwarded: false, needsAttention: false, comment_count: 0, received_at: new Date(Date.now() - 12 * 3600000).toISOString(), body_text: "Here's your weekly summary:\n\n- 12 new members joined\n- 3 governance proposals submitted\n- 2 events scheduled\n\nVisit the dashboard for details.", direction: 'inbound', comments: [] },
|
||||||
|
{ id: "s4", from_name: "Dave Park", from_address: "dave@example.com", subject: "Governance proposal feedback", status: "closed", is_read: true, is_starred: false, replied: true, forwarded: false, needsAttention: false, comment_count: 2, received_at: new Date(Date.now() - 48 * 3600000).toISOString(), body_text: "I've reviewed the treasury allocation proposal. My feedback is inline below.", direction: 'inbound', comments: [{ username: "Alice Chen", body: "Good points, incorporated into v2.", created_at: new Date(Date.now() - 46 * 3600000).toISOString() }, { username: "Dave Park", body: "Looks great now, thanks!", created_at: new Date(Date.now() - 44 * 3600000).toISOString() }] },
|
||||||
|
],
|
||||||
team: [
|
team: [
|
||||||
{ id: "t1", from_name: "Alice Chen", from_address: "alice@example.com", subject: "Sprint planning notes", status: "open", is_read: true, is_starred: true, replied: true, forwarded: false, needsAttention: false, comment_count: 3, received_at: new Date(Date.now() - 2 * 3600000).toISOString(), body_text: "Here are the sprint planning notes from today's session. We agreed on the following priorities for the next two weeks:\n\n1. Ship local-first sync for notes module\n2. Polish the calendar demo mode\n3. Review provider registry API\n\nLet me know if I missed anything.", direction: 'inbound', comments: [{ username: "Bob Martinez", body: "Looks good! I'd add the inbox overhaul too.", created_at: new Date(Date.now() - 1.5 * 3600000).toISOString() }, { username: "Carol Wu", body: "Agreed, calendar polish is top priority.", created_at: new Date(Date.now() - 1 * 3600000).toISOString() }, { username: "Alice Chen", body: "Updated the list. Thanks!", created_at: new Date(Date.now() - 0.5 * 3600000).toISOString() }] },
|
{ id: "t1", from_name: "Alice Chen", from_address: "alice@example.com", subject: "Sprint planning notes", status: "open", is_read: true, is_starred: true, replied: true, forwarded: false, needsAttention: false, comment_count: 3, received_at: new Date(Date.now() - 2 * 3600000).toISOString(), body_text: "Here are the sprint planning notes from today's session. We agreed on the following priorities for the next two weeks:\n\n1. Ship local-first sync for notes module\n2. Polish the calendar demo mode\n3. Review provider registry API\n\nLet me know if I missed anything.", direction: 'inbound', comments: [{ username: "Bob Martinez", body: "Looks good! I'd add the inbox overhaul too.", created_at: new Date(Date.now() - 1.5 * 3600000).toISOString() }, { username: "Carol Wu", body: "Agreed, calendar polish is top priority.", created_at: new Date(Date.now() - 1 * 3600000).toISOString() }, { username: "Alice Chen", body: "Updated the list. Thanks!", created_at: new Date(Date.now() - 0.5 * 3600000).toISOString() }] },
|
||||||
{ id: "t2", from_name: "Bob Martinez", from_address: "bob@example.com", subject: "Deploy checklist for v2.1", status: "open", is_read: false, is_starred: false, replied: false, forwarded: false, needsAttention: true, comment_count: 1, received_at: new Date(Date.now() - 5 * 3600000).toISOString(), body_text: "Here is the deploy checklist for v2.1. Please review before we cut the release.\n\n- [ ] Run full test suite\n- [ ] Update changelog\n- [ ] Tag release in Gitea\n- [ ] Deploy to staging\n- [ ] Smoke test all modules", direction: 'inbound', comments: [{ username: "Alice Chen", body: "I can handle the changelog update.", created_at: new Date(Date.now() - 4 * 3600000).toISOString() }] },
|
{ id: "t2", from_name: "Bob Martinez", from_address: "bob@example.com", subject: "Deploy checklist for v2.1", status: "open", is_read: false, is_starred: false, replied: false, forwarded: false, needsAttention: true, comment_count: 1, received_at: new Date(Date.now() - 5 * 3600000).toISOString(), body_text: "Here is the deploy checklist for v2.1. Please review before we cut the release.\n\n- [ ] Run full test suite\n- [ ] Update changelog\n- [ ] Tag release in Gitea\n- [ ] Deploy to staging\n- [ ] Smoke test all modules", direction: 'inbound', comments: [{ username: "Alice Chen", body: "I can handle the changelog update.", created_at: new Date(Date.now() - 4 * 3600000).toISOString() }] },
|
||||||
|
|
@ -96,9 +102,7 @@ class FolkInboxClient extends HTMLElement {
|
||||||
private static readonly TOUR_STEPS = [
|
private static readonly TOUR_STEPS = [
|
||||||
{ target: '[data-nav="mailboxes"]', title: "Mailboxes", message: "Shared team mailboxes — everyone sees the same inbox, threads, and comments.", advanceOnClick: true },
|
{ target: '[data-nav="mailboxes"]', title: "Mailboxes", message: "Shared team mailboxes — everyone sees the same inbox, threads, and comments.", advanceOnClick: true },
|
||||||
{ target: '[data-nav="approvals"]', title: "Approvals", message: "Outbound emails require co-signer approval before sending. Review and sign here.", advanceOnClick: true },
|
{ target: '[data-nav="approvals"]', title: "Approvals", message: "Outbound emails require co-signer approval before sending. Review and sign here.", advanceOnClick: true },
|
||||||
{ target: '[data-nav="agents"]', title: "Agents", message: "AI agents monitor inboxes and draft replies automatically. Create and manage them here.", advanceOnClick: true },
|
{ target: '[data-action="help"]', title: "Info", message: "Open the guide anytime to learn how collaborative email works in rInbox.", advanceOnClick: true },
|
||||||
{ target: '[data-nav="personal"]', title: "Personal Inbox", message: "Connect your own IMAP account for a personal inbox alongside shared ones.", advanceOnClick: true },
|
|
||||||
{ target: '[data-action="help"]', title: "Help Guide", message: "Open the full guide anytime to learn how collaborative email works in rInbox.", advanceOnClick: true },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -118,10 +122,6 @@ class FolkInboxClient extends HTMLElement {
|
||||||
this._checkForwardStatus();
|
this._checkForwardStatus();
|
||||||
if (this.space === "demo") { this.loadDemoData(); }
|
if (this.space === "demo") { this.loadDemoData(); }
|
||||||
else { this.subscribeOffline(); this.loadMailboxes(); }
|
else { this.subscribeOffline(); this.loadMailboxes(); }
|
||||||
// Auto-start tour on first visit
|
|
||||||
if (!localStorage.getItem("rinbox_tour_done")) {
|
|
||||||
setTimeout(() => this._tour.start(), 1200);
|
|
||||||
}
|
|
||||||
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rinbox', context: this.currentThread?.subject || 'Inbox' }));
|
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rinbox', context: this.currentThread?.subject || 'Inbox' }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,8 +173,9 @@ class FolkInboxClient extends HTMLElement {
|
||||||
|
|
||||||
private loadDemoData() {
|
private loadDemoData() {
|
||||||
this.mailboxes = [
|
this.mailboxes = [
|
||||||
{ slug: "team", name: "Team Inbox", email: "team@rspace.online", description: "Shared workspace inbox for internal team communications", thread_count: 4, unread_count: 1 },
|
{ slug: "space", name: `${this.space}@rspace.online`, email: `${this.space}@rspace.online`, description: "Shared space mailbox — all members can read and triage", thread_count: 4, unread_count: 1, primary: true },
|
||||||
{ slug: "support", name: "Support", email: "support@rspace.online", description: "Community support requests with multi-sig approval workflows", thread_count: 3, unread_count: 1 },
|
{ slug: "team", name: "Team Inbox", email: "team@rspace.online", description: "Internal team communications", thread_count: 4, unread_count: 1 },
|
||||||
|
{ slug: "support", name: "Support", email: "support@rspace.online", description: "Community support requests with multi-sig approval", thread_count: 3, unread_count: 1 },
|
||||||
];
|
];
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +189,13 @@ class FolkInboxClient extends HTMLElement {
|
||||||
});
|
});
|
||||||
if (resp.ok) {
|
if (resp.ok) {
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
this.mailboxes = data.mailboxes || [];
|
const list = data.mailboxes || [];
|
||||||
|
// Mark space mailbox as primary
|
||||||
|
const spaceEmail = `${this.space}@rspace.online`;
|
||||||
|
for (const m of list) {
|
||||||
|
if (m.email === spaceEmail) m.primary = true;
|
||||||
|
}
|
||||||
|
this.mailboxes = list;
|
||||||
}
|
}
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
|
|
||||||
|
|
@ -559,6 +566,8 @@ class FolkInboxClient extends HTMLElement {
|
||||||
.mailbox-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 0.75rem; }
|
.mailbox-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 0.75rem; }
|
||||||
.mailbox-card { background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 12px; padding: 1.25rem; cursor: pointer; transition: all 0.2s; }
|
.mailbox-card { background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 12px; padding: 1.25rem; cursor: pointer; transition: all 0.2s; }
|
||||||
.mailbox-card:hover { border-color: var(--rs-primary); transform: translateY(-1px); box-shadow: 0 4px 16px rgba(99,102,241,0.15); }
|
.mailbox-card:hover { border-color: var(--rs-primary); transform: translateY(-1px); box-shadow: 0 4px 16px rgba(99,102,241,0.15); }
|
||||||
|
.mailbox-primary { border-color: #6366f1; background: linear-gradient(135deg, var(--rs-bg-surface), rgba(99,102,241,0.08)); grid-column: 1 / -1; }
|
||||||
|
.mailbox-primary .mailbox-name { font-size: 1.1rem; color: #818cf8; }
|
||||||
.mailbox-icon { font-size: 1.5rem; margin-bottom: 0.75rem; }
|
.mailbox-icon { font-size: 1.5rem; margin-bottom: 0.75rem; }
|
||||||
.mailbox-name { font-weight: 600; font-size: 1rem; margin-bottom: 0.25rem; color: var(--rs-text-primary); }
|
.mailbox-name { font-weight: 600; font-size: 1rem; margin-bottom: 0.25rem; color: var(--rs-text-primary); }
|
||||||
.mailbox-email { font-size: 0.8rem; color: #6366f1; margin-bottom: 0.5rem; font-family: monospace; }
|
.mailbox-email { font-size: 0.8rem; color: #6366f1; margin-bottom: 0.5rem; font-family: monospace; }
|
||||||
|
|
@ -815,8 +824,6 @@ class FolkInboxClient extends HTMLElement {
|
||||||
const items: { id: string; label: string }[] = [
|
const items: { id: string; label: string }[] = [
|
||||||
{ id: "mailboxes", label: "Mailboxes" },
|
{ id: "mailboxes", label: "Mailboxes" },
|
||||||
{ id: "approvals", label: "Approvals" },
|
{ id: "approvals", label: "Approvals" },
|
||||||
{ id: "personal", label: "Personal" },
|
|
||||||
{ id: "agents", label: "Agents" },
|
|
||||||
];
|
];
|
||||||
if (this.currentMailbox) {
|
if (this.currentMailbox) {
|
||||||
items.unshift({ id: "threads", label: this.currentMailbox.name });
|
items.unshift({ id: "threads", label: this.currentMailbox.name });
|
||||||
|
|
@ -826,9 +833,7 @@ class FolkInboxClient extends HTMLElement {
|
||||||
${this.view !== "mailboxes" && this._history.canGoBack ? `<button class="rapp-nav__back" data-action="back">←</button>` : ""}
|
${this.view !== "mailboxes" && this._history.canGoBack ? `<button class="rapp-nav__back" data-action="back">←</button>` : ""}
|
||||||
${items.map((i) => `<button class="nav-btn ${this.view === i.id ? "active" : ""}" data-nav="${i.id}">${i.label}</button>`).join("")}
|
${items.map((i) => `<button class="nav-btn ${this.view === i.id ? "active" : ""}" data-nav="${i.id}">${i.label}</button>`).join("")}
|
||||||
<span class="nav-spacer"></span>
|
<span class="nav-spacer"></span>
|
||||||
${this._currentUsername ? `<span style="font-size:0.75rem;color:#818cf8;margin-right:0.5rem">${this._currentUsername}</span>` : ''}
|
<button class="help-btn" data-action="help" title="About rInbox" style="font-size:1rem;padding:2px 6px">ⓘ</button>
|
||||||
<button class="help-btn" data-action="help">? Guide</button>
|
|
||||||
<button class="help-btn" id="btn-tour">Tour</button>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
@ -857,12 +862,15 @@ class FolkInboxClient extends HTMLElement {
|
||||||
<button class="help-btn" data-action="help" style="font-size:0.85rem;padding:0.5rem 1.25rem">Learn how rInbox works</button>
|
<button class="help-btn" data-action="help" style="font-size:0.85rem;padding:0.5rem 1.25rem">Learn how rInbox works</button>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
return `<div class="mailbox-grid">${this.mailboxes.map((m) => {
|
// Sort: primary (space) mailbox first
|
||||||
|
const sorted = [...this.mailboxes].sort((a, b) => (b.primary ? 1 : 0) - (a.primary ? 1 : 0));
|
||||||
|
return `<div class="mailbox-grid">${sorted.map((m) => {
|
||||||
const demoThreads = this.demoThreads[m.slug] || [];
|
const demoThreads = this.demoThreads[m.slug] || [];
|
||||||
const threadCount = m.thread_count ?? demoThreads.length;
|
const threadCount = m.thread_count ?? demoThreads.length;
|
||||||
const unreadCount = m.unread_count ?? demoThreads.filter((t: any) => !t.is_read).length;
|
const unreadCount = m.unread_count ?? demoThreads.filter((t: any) => !t.is_read).length;
|
||||||
|
const isPrimary = m.primary || m.email === `${this.space}@rspace.online`;
|
||||||
return `
|
return `
|
||||||
<div class="mailbox-card" data-mailbox="${m.slug}">
|
<div class="mailbox-card${isPrimary ? " mailbox-primary" : ""}" data-mailbox="${m.slug}">
|
||||||
<div class="mailbox-icon">📨</div>
|
<div class="mailbox-icon">📨</div>
|
||||||
<div class="mailbox-name">${m.name}</div>
|
<div class="mailbox-name">${m.name}</div>
|
||||||
<div class="mailbox-email">${m.email}</div>
|
<div class="mailbox-email">${m.email}</div>
|
||||||
|
|
@ -1367,9 +1375,6 @@ class FolkInboxClient extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private bindEvents() {
|
private bindEvents() {
|
||||||
// Tour button
|
|
||||||
this.shadow.getElementById("btn-tour")?.addEventListener("click", () => this.startTour());
|
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
this.shadow.querySelectorAll("[data-nav]").forEach((btn) => {
|
this.shadow.querySelectorAll("[data-nav]").forEach((btn) => {
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue