mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(panel): overview reorder — quick actions + key cards first, team health second, queues last; PR Reviews + Social share a row
Removes the mobile order hack that hoisted decision queues above the fold; the empty PR-Reviews state drops to the compact house convention.
This commit is contained in:
@@ -97,4 +97,64 @@ describe("CommandCenter", () => {
|
||||
expect(screen.queryByText("XPostQueueStub")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("VideoPostQueueStub")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders every section", () => {
|
||||
render(<CommandCenter />);
|
||||
for (const stub of [
|
||||
"QuickActionsBarStub",
|
||||
"KeyMetricsPanelStub",
|
||||
"AuditorAlertsPanelStub",
|
||||
"UsageOverviewPanelStub",
|
||||
"ScorecardOverviewPanelStub",
|
||||
"TeamHealthCardsStub",
|
||||
"CeoApprovalQueueStub",
|
||||
"StrategySignalsPanelStub",
|
||||
"PrReviewQueueStub",
|
||||
"SocialSummaryCardStub",
|
||||
"ReleaseProposalCardStub",
|
||||
"PlaybookReviewQueueStub",
|
||||
"RoadmapReviewQueueStub",
|
||||
"ActiveBlockersPanelStub",
|
||||
"RecentActivityFeedStub",
|
||||
]) {
|
||||
expect(screen.getByText(stub)).toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
|
||||
it("orders sections top to bottom: quick actions/key cards, team health, then the rest", () => {
|
||||
render(<CommandCenter />);
|
||||
const order = [
|
||||
"QuickActionsBarStub",
|
||||
"KeyMetricsPanelStub",
|
||||
"TeamHealthCardsStub",
|
||||
"CeoApprovalQueueStub",
|
||||
"PrReviewQueueStub",
|
||||
"ReleaseProposalCardStub",
|
||||
"PlaybookReviewQueueStub",
|
||||
"RoadmapReviewQueueStub",
|
||||
"ActiveBlockersPanelStub",
|
||||
];
|
||||
for (let i = 1; i < order.length; i++) {
|
||||
const earlier = screen.getByText(order[i - 1]);
|
||||
const later = screen.getByText(order[i]);
|
||||
// earlier precedes later in the DOM.
|
||||
expect(
|
||||
earlier.compareDocumentPosition(later) &
|
||||
Node.DOCUMENT_POSITION_FOLLOWING,
|
||||
).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it("pairs PR Reviews and Social side by side ahead of the release/playbook/roadmap cards", () => {
|
||||
render(<CommandCenter />);
|
||||
const prReview = screen.getByText("PrReviewQueueStub");
|
||||
const social = screen.getByText("SocialSummaryCardStub");
|
||||
const release = screen.getByText("ReleaseProposalCardStub");
|
||||
// Same grid row: shared immediate parent.
|
||||
expect(prReview.parentElement).toBe(social.parentElement);
|
||||
expect(
|
||||
prReview.compareDocumentPosition(release) &
|
||||
Node.DOCUMENT_POSITION_FOLLOWING,
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,13 +95,9 @@ export function CommandCenter() {
|
||||
const hasError = errorOverview || errorFlags || errorTasks || errorActivity;
|
||||
|
||||
return (
|
||||
// flex-col + explicit `order` (reset via md:order-none): below md the CEO
|
||||
// decision queues and activity move above the fold; at md+ every item
|
||||
// shares order:0 and falls back to plain source order (unchanged desktop
|
||||
// layout).
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Header */}
|
||||
<div className="order-1 flex items-center justify-between md:order-none">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">
|
||||
RoboCo Command Center
|
||||
@@ -133,72 +129,19 @@ export function CommandCenter() {
|
||||
|
||||
{/* Error indicator */}
|
||||
{hasError && (
|
||||
<div className="order-2 flex items-center gap-2 rounded-md border border-destructive/50 bg-destructive/10 px-4 py-2 text-sm text-destructive md:order-none">
|
||||
<div className="flex items-center gap-2 rounded-md border border-destructive/50 bg-destructive/10 px-4 py-2 text-sm text-destructive">
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
Some data failed to load. Use the header refresh button to try again.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* CEO Approval Queue + Strategy Signals - side-by-side on lg+. Ordered
|
||||
first on mobile — the CEO's decisions shouldn't be below the fold. */}
|
||||
<div className="order-3 grid grid-cols-1 gap-6 md:order-none lg:grid-cols-2">
|
||||
<CeoApprovalQueue />
|
||||
<StrategySignalsPanel />
|
||||
</div>
|
||||
|
||||
{/* External-PR review decision queue (hidden when empty) */}
|
||||
<div className="order-4 md:order-none">
|
||||
<PrReviewQueue />
|
||||
</div>
|
||||
|
||||
{/* Gated release proposal (hidden when none open) */}
|
||||
<div className="order-4 md:order-none">
|
||||
<ReleaseProposalCard />
|
||||
</div>
|
||||
|
||||
{/* Playbook review queue (hidden when no drafts) */}
|
||||
<div className="order-4 md:order-none">
|
||||
<PlaybookReviewQueue />
|
||||
</div>
|
||||
|
||||
{/* Social (X + video) summary — the full queues + unified history live
|
||||
on /social, avoiding a duplicated surface here. */}
|
||||
<div className="order-4 md:order-none">
|
||||
<SocialSummaryCard />
|
||||
</div>
|
||||
|
||||
{/* Board roadmap queue (hidden when no cycle authored) */}
|
||||
<div className="order-4 md:order-none">
|
||||
<RoadmapReviewQueue />
|
||||
</div>
|
||||
|
||||
{/* Blockers and Activity Row — activity brought up near the top on
|
||||
mobile too, ahead of the Team Health / Quick Actions filler. */}
|
||||
<div className="order-5 grid grid-cols-1 gap-6 md:order-none lg:grid-cols-2">
|
||||
<ActiveBlockersPanel tasks={tasks} isLoading={loadingTasks} />
|
||||
<RecentActivityFeed
|
||||
activities={activity as Activity[] | undefined}
|
||||
isLoading={loadingActivity}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Team Health */}
|
||||
<section className="order-6 md:order-none">
|
||||
<h2 className="text-lg font-semibold mb-4">Team Health</h2>
|
||||
<TeamHealthCards
|
||||
teams={overview?.health_status}
|
||||
isLoading={loadingOverview}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<section className="order-7 md:order-none">
|
||||
{/* Section 1: Quick Actions + the four key cards */}
|
||||
<section>
|
||||
<h2 className="text-lg font-semibold mb-4">Quick Actions</h2>
|
||||
<QuickActionsBar />
|
||||
</section>
|
||||
|
||||
{/* Metrics, Alerts, Usage, and Performance Row */}
|
||||
<div className="order-8 grid grid-cols-1 gap-6 md:order-none lg:grid-cols-2 xl:grid-cols-4">
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-4">
|
||||
<KeyMetricsPanel
|
||||
metrics={overview?.key_metrics}
|
||||
isLoading={loadingOverview}
|
||||
@@ -207,6 +150,49 @@ export function CommandCenter() {
|
||||
<UsageOverviewPanel />
|
||||
<ScorecardOverviewPanel />
|
||||
</div>
|
||||
|
||||
{/* Section 2: Team Health (team cards + Task Intake + Secretary) */}
|
||||
<section>
|
||||
<h2 className="text-lg font-semibold mb-4">Team Health</h2>
|
||||
<TeamHealthCards
|
||||
teams={overview?.health_status}
|
||||
isLoading={loadingOverview}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Section 3: everything else */}
|
||||
|
||||
{/* CEO Approval Queue + Strategy Signals - side-by-side on lg+ */}
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<CeoApprovalQueue />
|
||||
<StrategySignalsPanel />
|
||||
</div>
|
||||
|
||||
{/* External-PR review decision queue + Social summary - side-by-side
|
||||
on lg+ (both hidden-when-empty / compact, so pairing them avoids
|
||||
two near-empty full-width rows) */}
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<PrReviewQueue />
|
||||
<SocialSummaryCard />
|
||||
</div>
|
||||
|
||||
{/* Gated release proposal (hidden when none open) */}
|
||||
<ReleaseProposalCard />
|
||||
|
||||
{/* Playbook review queue (hidden when no drafts) */}
|
||||
<PlaybookReviewQueue />
|
||||
|
||||
{/* Board roadmap queue (hidden when no cycle authored) */}
|
||||
<RoadmapReviewQueue />
|
||||
|
||||
{/* Blockers and Activity Row */}
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<ActiveBlockersPanel tasks={tasks} isLoading={loadingTasks} />
|
||||
<RecentActivityFeed
|
||||
activities={activity as Activity[] | undefined}
|
||||
isLoading={loadingActivity}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -151,13 +151,9 @@ export function PrReviewQueue({ className }: PrReviewQueueProps) {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{items.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center py-10 text-center text-muted-foreground">
|
||||
<GitPullRequest className="mb-3 h-8 w-8 opacity-50" />
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
<GitPullRequest className="h-8 w-8 mx-auto mb-2 opacity-50" />
|
||||
<p className="text-sm">No external PRs under review right now.</p>
|
||||
<p className="mt-1 text-xs">
|
||||
Inbound external/fork PRs the reviewer has looked at show up
|
||||
here for your supersede / dismiss call.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-3">
|
||||
|
||||
Reference in New Issue
Block a user