-
Notifications
You must be signed in to change notification settings - Fork 1
feat: restore live Pandora dashboard #132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,17 @@ | ||
| import { profileSnapshot } from "./mock-data"; | ||
|
|
||
| export function AskPandoraHero() { | ||
| export function AskPandoraHero({ hero, evidence, warnings }: { hero: { title: string; description: string; primaryAction: string; secondaryAction: string }; evidence: string; warnings: string[] }) { | ||
| return ( | ||
| <section className="pd-hero"> | ||
| <div> | ||
| <p className="pd-label">Ask Pandora</p> | ||
| <h2>Pandora dashboard is an authenticated mock shell.</h2> | ||
| <p>This route is for layout review only. It does not present live memory health, retrieval scores, profile state, or queue activity until those claims are backed by implemented routes, database policy, and tests.</p> | ||
| <h2>{hero.title}</h2> | ||
| <p>{hero.description}</p> | ||
| </div> | ||
| <div className="pd-hero-actions"> | ||
| <button type="button" className="pd-primary-btn" disabled>Context pack backend pending</button> | ||
| <button type="button" className="pd-secondary-btn" disabled>Retrieval eval backend pending</button> | ||
| <button type="button" className="pd-primary-btn" disabled>{hero.primaryAction}</button> | ||
| <button type="button" className="pd-secondary-btn" disabled>{hero.secondaryAction}</button> | ||
| </div> | ||
| <div className="pd-evidence">{profileSnapshot.evidence}</div> | ||
| <div className="pd-evidence">{evidence}</div> | ||
| {warnings.length ? <div className="pd-envelope"><strong>Loader warnings</strong><p>{warnings.join(" ")}</p></div> : null} | ||
| </section> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,7 @@ | ||
| import { coreSystems, gatedSystems } from "./mock-data"; | ||
| import type { SystemRow } from "./types"; | ||
| import type { PandoraDashboardData, SystemRow } from "./types"; | ||
|
|
||
| function SystemStatusRow({ row }: { row: SystemRow }) { return <div className="pd-system-row"><span>{row.label}</span><strong className={`pd-state-${row.state}`}>{row.value}</strong></div>; } | ||
|
|
||
| export function DiagnosticsCard({ loading }: { loading: boolean }) { | ||
| return <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Diagnostics</p><h3>Authenticated mock shell only.</h3></div></div>{loading ? <div className="pd-loading" aria-label="Loading diagnostics" /> : <><div className="pd-system-list">{coreSystems.map((row) => <SystemStatusRow row={row} key={row.label} />)}</div><div className="pd-system-list pd-gated-list">{gatedSystems.map((row) => <SystemStatusRow row={row} key={row.label} />)}</div><div className="pd-envelope"><strong>Action Envelope</strong><p>Pending backend proof. Do not treat this UI as live engine evidence.</p></div><button type="button" className="pd-secondary-btn" disabled title="Backend wiring pending">Run Smoke Test</button></>}</section>; | ||
| export function DiagnosticsCard({ diagnostics, loading = false }: { diagnostics: PandoraDashboardData["diagnostics"]; loading?: boolean }) { | ||
| return <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Diagnostics</p><h3>Live RLS Data</h3></div></div>{loading ? <div className="pd-loading" aria-label="Loading diagnostics" /> : <><div className="pd-system-list">{diagnostics.coreSystems.map((row) => <SystemStatusRow row={row} key={row.label} />)}</div><div className="pd-system-list pd-gated-list">{diagnostics.gatedSystems.map((row) => <SystemStatusRow row={row} key={row.label} />)}</div><div className="pd-envelope"><strong>{diagnostics.envelope.title}</strong><p>{diagnostics.envelope.description}</p></div><button type="button" className="pd-secondary-btn" disabled title="Smoke tests run outside the dashboard">Run Smoke Test</button></>}</section>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| import { LockKeyhole } from "lucide-react"; | ||
| import { memorySpaces } from "./mock-data"; | ||
| import type { MemorySpace } from "./types"; | ||
| import { COLORS, cn } from "./theme"; | ||
|
|
||
| export function MemorySpacesCard() { | ||
| return <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Memory Spaces</p><h3>Namespace separation enforced</h3></div><LockKeyhole size={20} aria-hidden="true" /></div><div className="pd-space-grid">{memorySpaces.map((space) => { const color = COLORS[space.color]; return <button type="button" className={cn("pd-space", color.border)} key={space.id} aria-label={`Open ${space.label} namespace shell`}><div className="pd-card-row"><span className={cn("pd-dot", color.dot)} /><span className="pd-pill">{space.status}</span></div><strong>{space.label}</strong><small>{space.type}</small><p>{space.description}</p><div className="pd-space-metrics"><span>{space.memories.toLocaleString()}<small>memories</small></span><span>{space.people}<small>people</small></span><span>{space.projects}<small>projects</small></span></div></button>; })}</div></section>; | ||
| export function MemorySpacesCard({ spaces }: { spaces: MemorySpace[] }) { | ||
| return <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Memory Spaces</p><h3>Namespace separation enforced</h3></div><LockKeyhole size={20} aria-hidden="true" /></div><div className="pd-space-grid">{spaces.map((space) => { const color = COLORS[space.color]; return <button type="button" className={cn("pd-space", color.border)} key={space.id} aria-label={`${space.label} namespace live summary`} disabled><div className="pd-card-row"><span className={cn("pd-dot", color.dot)} /><span className="pd-pill">{space.status}</span></div><strong>{space.label}</strong><small>{space.type}</small><p>{space.description}</p><div className="pd-space-metrics"><span>{space.memories.toLocaleString()}<small>memories</small></span><span>{space.people}<small>people</small></span><span>{space.projects}<small>projects</small></span></div></button>; })}</div></section>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import { timelineEvents } from "./mock-data"; | ||
| import { BadgeCheck } from "lucide-react"; | ||
| import type { TimelineEventData } from "./types"; | ||
| import { COLORS, cn } from "./theme"; | ||
|
|
||
| export function RecentEventsTimeline() { | ||
| return <section className="pd-card pd-timeline-card"><div className="pd-section-head"><div><p className="pd-label">Recent Memory Events</p><h3>Checkpoint timeline</h3></div></div><ol className="pd-timeline">{timelineEvents.map((event) => { const Icon = event.icon; const color = COLORS[event.color]; return <li key={event.id}><div className={cn("pd-timeline-icon", color.iconBg, color.iconText)}><Icon size={16} aria-hidden="true" /></div><div><div className="pd-event-title"><strong>{event.title}</strong><time>{event.time}</time></div><p>{event.desc}</p></div></li>; })}</ol></section>; | ||
| export function RecentEventsTimeline({ events }: { events: TimelineEventData[] }) { | ||
| return <section className="pd-card pd-timeline-card"><div className="pd-section-head"><div><p className="pd-label">Recent Memory Events</p><h3>Checkpoint timeline</h3></div></div><ol className="pd-timeline">{events.length ? events.map((event) => { const color = COLORS[event.color]; return <li key={event.id}><div className={cn("pd-timeline-icon", color.iconBg, color.iconText)}><BadgeCheck size={16} aria-hidden="true" /></div><div><div className="pd-event-title"><strong>{event.title}</strong><time>{event.time}</time></div><p>{event.desc}</p></div></li>; }) : <li><div className="pd-timeline-icon pd-icon-bg-slate pd-icon-text-slate"><BadgeCheck size={16} aria-hidden="true" /></div><div><div className="pd-event-title"><strong>No live events returned</strong><time>Empty state</time></div><p>No timeline rows are fabricated.</p></div></li>}</ol></section>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,10 +6,10 @@ function MiniMetric({ label, value }: { label: string; value: number }) { return | |
| export function WorkQueueCard({ queue }: { queue: WorkQueueData }) { | ||
| const total = Object.values(queue).reduce((sum, value) => sum + value, 0); | ||
| const items = [ | ||
| { label: "Open Loops", value: queue.openLoops, desc: "Backend pending", icon: AlertCircle }, | ||
| { label: "Needs Review", value: queue.needsReview, desc: "Backend pending", icon: ListChecks }, | ||
| { label: "Pack Supersession", value: queue.packSupersessionNeeded, desc: "Backend pending", icon: GitMerge }, | ||
| { label: "People Map Design", value: queue.peopleMapDesignNeeded, desc: "Backend pending", icon: UserRoundSearch }, | ||
| { label: "Open Loops", value: queue.openLoops, desc: "Live open-loop rows needing operator follow-up", icon: AlertCircle }, | ||
| { label: "Needs Review", value: queue.needsReview, desc: "Live capture/review rows awaiting review", icon: ListChecks }, | ||
| { label: "Pack Supersession", value: queue.packSupersessionNeeded, desc: "Duplicate active master packs requiring attention", icon: GitMerge }, | ||
| { label: "People Map Design", value: queue.peopleMapDesignNeeded, desc: "No fabricated people-map work is created", icon: UserRoundSearch }, | ||
| ]; | ||
| return <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Work Queue</p><h3>{total} actionable items</h3></div><span className="pd-pill pd-pill-slate">Mock only</span></div><div className="pd-queue-list">{items.map((item) => { const Icon = item.icon; return <div className="pd-queue-item" key={item.label}><Icon size={18} aria-hidden="true" /><div><strong>{item.label}</strong><p>{item.desc}</p></div><b>{item.value}</b></div>; })}</div><div className="pd-mini-grid"><MiniMetric label="Stale Packs" value={queue.stalePacks} /><MiniMetric label="Refresh Due" value={queue.profileRefreshDue} /><MiniMetric label="Failed Tests" value={queue.failedTests} /></div></section>; | ||
| return <section className="pd-card"><div className="pd-section-head"><div><p className="pd-label">Work Queue</p><h3>{total} actionable items</h3></div><span className="pd-pill pd-pill-emerald">Live RLS Data</span></div><div className="pd-queue-list">{items.map((item) => { const Icon = item.icon; return <div className="pd-queue-item" key={item.label}><Icon size={18} aria-hidden="true" /><div><strong>{item.label}</strong><p>{item.desc}</p></div><b>{item.value}</b></div>; })}</div><div className="pd-mini-grid"><MiniMetric label="Pruning Review" value={queue.stalePacks} /><MiniMetric label="Profile Missing" value={queue.profileRefreshDue} /><MiniMetric label="Failed Tests" value={queue.failedTests} /></div></section>; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a queue backing table such as Useful? React with 👍 / 👎. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,30 +1,21 @@ | ||
| import { BadgeCheck, Package } from "lucide-react"; | ||
| import type { MemorySpace, StatItem, SystemRow, WorkQueueData } from "./types"; | ||
| import { Package } from "lucide-react"; | ||
| import type { MemorySpace, PandoraDashboardData, StatItem, SystemRow, TimelineEventData, WorkQueueData } from "./types"; | ||
|
|
||
| export const profileSnapshot = { name: "Fixture", status: "Fixture", confidencePercent: 0, confidenceLabel: "N/A", summary: "Fixture", lastRefreshed: "Fixture", traits: ["Fixture"], evidence: "Fixture" } as const; | ||
| export const profileSnapshot = { name: "Fixture", status: "Mock only", confidencePercent: 0, confidenceLabel: "No live data", summary: "No live data fixture profile.", lastRefreshed: "No live data", traits: ["Fixture", "Mock only"], evidence: "No live data" }; | ||
|
|
||
| export const mockStats: StatItem[] = [ | ||
| { id: "one", title: "One", value: "0", subtitle: "Fixture", icon: Package, color: "slate", sparklineData: [0] }, | ||
| { id: "fixture", title: "Fixture", value: "No live data", subtitle: "Mock only", icon: Package, color: "slate", sparklineData: [0, 0] }, | ||
| ]; | ||
|
|
||
| export const memorySpaces: MemorySpace[] = [ | ||
| { id: "real_life", label: "real_life", type: "Primary", description: "Fixture", memories: 0, people: 0, projects: 0, status: "Degraded", color: "emerald" }, | ||
| { id: "au", label: "au", type: "Secondary", description: "Fixture", memories: 0, people: 0, projects: 0, status: "Degraded", color: "purple" }, | ||
| { id: "real_life", label: "real_life", type: "Fixture", description: "Mock only: no live data.", memories: 0, people: 0, projects: 0, status: "Degraded", color: "emerald" }, | ||
| { id: "au", label: "au", type: "Fixture", description: "Mock only: no live data.", memories: 0, people: 0, projects: 0, status: "Degraded", color: "purple" }, | ||
| ]; | ||
|
|
||
| export const workQueue: WorkQueueData = { needsReview: 0, openLoops: 0, stalePacks: 0, failedTests: 0, profileRefreshDue: 0, packSupersessionNeeded: 0, peopleMapDesignNeeded: 0 }; | ||
|
|
||
| export const timelineEvents = [ | ||
| { id: "one", icon: BadgeCheck, color: "slate", title: "One", time: "Now", desc: "Fixture" }, | ||
| ] as const; | ||
|
|
||
| export const coreSystems: SystemRow[] = [ | ||
| { label: "One", value: "OK", state: "healthy" }, | ||
| ]; | ||
|
|
||
| export const gatedSystems: SystemRow[] = [ | ||
| { label: "Two", value: "OK", state: "gated" }, | ||
| ]; | ||
|
|
||
| export const timelineEvents: TimelineEventData[] = [{ id: "fixture", color: "slate", title: "Fixture", time: "No live data", desc: "Mock only", namespace: "real_life" }]; | ||
| export const coreSystems: SystemRow[] = [{ label: "Fixture", value: "No live data", state: "idle" }]; | ||
| export const gatedSystems: SystemRow[] = [{ label: "Semantic retrieval", value: "Gated", state: "gated" }]; | ||
| export const fixtureDashboardData: PandoraDashboardData = { generatedAt: "No live data", operatorLabel: "Fixture", live: false, warnings: ["Mock only"], hero: { title: "Fixture dashboard", description: "Mock only: no live data.", primaryAction: "No live data", secondaryAction: "Semantic gated" }, evidence: "No live data", stats: [{ id: "fixture", title: "Fixture", value: "No live data", subtitle: "Mock only", color: "slate", sparklineData: [0, 0] }], memorySpaces, workQueue, profileSnapshot, timelineEvents, diagnostics: { coreSystems, gatedSystems, envelope: { title: "Fixture", description: "Mock only: no live data." } } }; | ||
| export const navItems = ["Dashboard", "Memory Feed", "Context Packs", "Adaptive Profiles", "Open Loops", "People", "Projects", "Retrieval Tests", "Settings"] as const; | ||
| export const mobileNavItems = ["Dashboard", "Memory Feed", "Queue", "Profiles", "More"]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With both namespaces populated, the loader builds
timelineEventsby flattening namespace reads in fixed order (real_lifebeforeau) and then taking the first six. Rendering that array here makes the “Recent Memory Events” card omit newer AU rows whenever six older real-life rows exist, so the live dashboard can show an inaccurate recent timeline; sort the combined events bycreated_atbefore slicing.Useful? React with 👍 / 👎.