519423cb0d
- landing/app/mira/wiki: tag=mira-wiki list now shows all wiki paragraphs (depends on KBDB tag filter exposed in matrix/kbdb commit, separate repo) - landing: app/mira hub + feed split + various WIP from prior sessions - registry/components: claude_api / kbdb_create_block / kbdb_get / km_writer / platform_crypto / auth_oauth2 contracts + main.go (accumulated) - .component-builds: pkg-lock updates + index.ts adjustments (WIP) - .agents/specs/arcrun/frontend-redesign: design notes - docs/test_credentials, docs/user_requirements/arcrun-landing-page: WIP docs - cypher-executor: auth-dispatcher / wasi-shim adjustments (WIP) Includes accumulated work from prior sessions plus the wiki UI tag-filter update that surfaces the AI-generated wiki paragraphs at /mira/wiki. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
127 lines
5.9 KiB
React
127 lines
5.9 KiB
React
const Dashboard = ({ onNav }) => {
|
|
const apps = [
|
|
{ id: 'digest', name: 'Weekly Digest', desc: 'Summarize customer activity into a Monday email for the revenue team.', icon: 'mail', tone: 'indigo' },
|
|
{ id: 'triage', name: 'Support Triage', desc: 'Classify inbound tickets, attach context from the CRM, and route.', icon: 'filter', tone: 'orange' },
|
|
{ id: 'seo', name: 'SEO Brief Generator', desc: 'Turn a keyword into a draft brief with outline, FAQs, and SERP notes.', icon: 'search', tone: 'green' },
|
|
{ id: 'slack', name: 'Standup Bot', desc: 'Collect Linear updates and post a tidy engineering standup to Slack.', icon: 'slack', tone: 'pink' },
|
|
{ id: 'doc', name: 'Docs Sync', desc: 'Keep Notion runbooks in sync with the production API surface.', icon: 'book', tone: 'blue' },
|
|
];
|
|
|
|
const workflows = [
|
|
{ id: 'digest_weekly', name: 'digest/weekly', nodes: 9, modified: '2 hours ago', runs: '147 runs', status: 'healthy' },
|
|
{ id: 'triage_inbound', name: 'triage/inbound-email', nodes: 14, modified: 'Yesterday', runs: '2,318 runs', status: 'healthy' },
|
|
{ id: 'seo_brief', name: 'seo/brief-from-keyword', nodes: 7, modified: '3 days ago', runs: '42 runs', status: 'healthy' },
|
|
{ id: 'standup', name: 'slack/standup-collector', nodes: 6, modified: '1 week ago', runs: '24 runs', status: 'idle' },
|
|
{ id: 'docs_sync', name: 'docs/sync-notion', nodes: 11, modified: '2 weeks ago', runs: '8 runs', status: 'failed' },
|
|
];
|
|
|
|
return (
|
|
<div className="shell">
|
|
<Sidebar current="dashboard" onNav={onNav} />
|
|
<div className="main">
|
|
<div className="main-head">
|
|
<div>
|
|
<div className="crumb">
|
|
<span>Northwind</span>
|
|
<span className="sep"><Icon name="chevron_right" size={11} /></span>
|
|
<span>Dashboard</span>
|
|
</div>
|
|
<h1>Welcome back, Maya</h1>
|
|
<div className="sub">5 apps running · 12 workflows · 2,538 runs this week</div>
|
|
</div>
|
|
<div className="flex gap-8">
|
|
<button className="btn btn-secondary"><Icon name="book" size={14} /> Templates</button>
|
|
<button className="btn btn-primary"><Icon name="plus" size={14} /> New app</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="main-body">
|
|
{/* Apps grid */}
|
|
<div className="section-head">
|
|
<div>
|
|
<h2>My Apps</h2>
|
|
<div className="subtle" style={{marginTop: 2}}>Packaged workflows your team can run from chat or code</div>
|
|
</div>
|
|
<span className="subtle">{apps.length} apps</span>
|
|
</div>
|
|
|
|
<div className="apps-grid">
|
|
{apps.map(a => (
|
|
<div key={a.id} className="app-card">
|
|
<AppIcon tone={a.tone}><Icon name={a.icon} size={17} /></AppIcon>
|
|
<h4>{a.name}</h4>
|
|
<p className="dsc">{a.desc}</p>
|
|
<div className="row">
|
|
<a className="open" onClick={() => onNav('workflow')}>Open app <Icon name="arrow_right" size={12} /></a>
|
|
<button className="chip-btn">
|
|
<Icon name="spark" size={11} /> Edit in Claude
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
<div className="app-card app-empty">
|
|
<div className="plus"><Icon name="plus" size={16} /></div>
|
|
<div style={{fontSize: 13, fontWeight: 500}}>Create new app</div>
|
|
<div style={{fontSize: 12, opacity: 0.75}}>Start from scratch or template</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Workflows */}
|
|
<div className="wf-table">
|
|
<div className="section-head">
|
|
<div>
|
|
<h2>My Workflows</h2>
|
|
<div className="subtle" style={{marginTop: 2}}>The graphs that power your apps</div>
|
|
</div>
|
|
<div className="flex gap-8">
|
|
<button className="btn btn-secondary btn-sm"><Icon name="filter" size={12} /> All workflows</button>
|
|
<button className="btn btn-secondary btn-sm" onClick={() => onNav('workflow')}><Icon name="plus" size={12} /> New</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="table-wrap">
|
|
<table className="table">
|
|
<thead>
|
|
<tr>
|
|
<th style={{width: '34%'}}>Workflow</th>
|
|
<th>Nodes</th>
|
|
<th>Last modified</th>
|
|
<th>Activity</th>
|
|
<th>Status</th>
|
|
<th style={{width: 100, textAlign: 'right'}}></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{workflows.map(w => (
|
|
<tr key={w.id}>
|
|
<td>
|
|
<div className="wf-row-name">
|
|
<span className="dot" />
|
|
<span className="mono" style={{fontSize: 13}}>{w.name}</span>
|
|
</div>
|
|
</td>
|
|
<td className="dim">{w.nodes}</td>
|
|
<td className="dim" style={{fontSize: 12.5}}>{w.modified}</td>
|
|
<td className="dim" style={{fontSize: 12.5}}>{w.runs}</td>
|
|
<td>
|
|
<span className={`pill ${w.status === 'healthy' ? 'active' : w.status === 'failed' ? 'revoked' : 'idle'}`}>
|
|
<span className="pdot" /> {w.status}
|
|
</span>
|
|
</td>
|
|
<td style={{textAlign: 'right'}}>
|
|
<button className="btn btn-secondary btn-sm" onClick={() => onNav('workflow')}>View</button>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
window.Dashboard = Dashboard;
|