mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
[87c7f3ec] Responsive grids: xl/2xl breakpoints + mobile overflow fix across all pages (#158) (#162) (#163)
* [87c7f3ec] feat(responsive): add xl/2xl breakpoint classes to dashboard grid layouts Adds xl: and 2xl: Tailwind breakpoint classes to all grid layouts in command-center.tsx, auditor-dashboard.tsx, team-health-cards.tsx, agent-grid.tsx, and metrics/page.tsx so content can expand denser beyond the current lg cap at ultrawide (2560px) viewports. Key changes: - team-health-cards.tsx: xl:grid-cols-4 2xl:grid-cols-6 (up to 6 teams) - metrics/page.tsx Team Health: xl:grid-cols-5 2xl:grid-cols-6 - agent-grid.tsx: xl/2xl classes for all 3 column variants - TokenUsageCostsSection: 2xl:grid-cols-6 added to row 1 (already had xl) - Fixed-size grids (2-4 items): xl matches lg, 2xl same as xl * [87c7f3ec] fix(mobile): stack grid-cols-12 layouts on mobile and fix git-browser header overflow communications/page.tsx: change col-span-3/6 to col-span-12 lg:col-span-3/6 so the three panels stack vertically on 375px viewports instead of being squished to 25%/50% widths. Gate the viewport-height constraint (h-[calc(100vh-7rem)]) and flex-1/min-h-0 to lg: so mobile can scroll naturally. Also fixes the Suspense fallback skeleton with the same responsive col-spans. git-browser.tsx: header flex row now goes flex-col on mobile and sm:flex-row on sm+ (640px), eliminating the overflow from the w-64 SelectTrigger plus Refresh button (346px combined) at 375px (327px content width). SelectTrigger is w-full on mobile and sm:w-64 on sm+. journals/page.tsx and git-browser.tsx grids already use col-span-12 lg:col-span-X so no grid changes needed there. --------- Co-authored-by: Frontend Developer 2 <fe-dev-2@agents.roboco.dev>
This commit is contained in:
co-authored by
Frontend Developer 2
parent
ddd9c7a38f
commit
bb9d4ff12a
@@ -297,7 +297,7 @@ function CommunicationsPageContent() {
|
|||||||
const selectedChannel = channels?.find(c => c.id === channelId);
|
const selectedChannel = channels?.find(c => c.id === channelId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-[calc(100vh-7rem)]">
|
<div className="flex flex-col lg:h-[calc(100vh-7rem)]">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<div>
|
<div>
|
||||||
@@ -319,9 +319,9 @@ function CommunicationsPageContent() {
|
|||||||
onRetry={() => refetch()}
|
onRetry={() => refetch()}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-12 gap-6 flex-1 min-h-0">
|
<div className="grid grid-cols-12 gap-4 lg:gap-6 lg:flex-1 lg:min-h-0">
|
||||||
{/* Panel 1: Channels */}
|
{/* Panel 1: Channels */}
|
||||||
<Card className="col-span-3 flex flex-col overflow-hidden">
|
<Card className="col-span-12 lg:col-span-3 flex flex-col overflow-hidden">
|
||||||
<CardContent className="p-3 flex flex-col h-full">
|
<CardContent className="p-3 flex flex-col h-full">
|
||||||
<div className="flex items-center gap-2 mb-3 pb-2 border-b">
|
<div className="flex items-center gap-2 mb-3 pb-2 border-b">
|
||||||
<Hash className="h-4 w-4 text-muted-foreground" />
|
<Hash className="h-4 w-4 text-muted-foreground" />
|
||||||
@@ -339,7 +339,7 @@ function CommunicationsPageContent() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Panel 2: Groups */}
|
{/* Panel 2: Groups */}
|
||||||
<Card className="col-span-3 flex flex-col overflow-hidden">
|
<Card className="col-span-12 lg:col-span-3 flex flex-col overflow-hidden">
|
||||||
<CardContent className="p-3 flex flex-col h-full">
|
<CardContent className="p-3 flex flex-col h-full">
|
||||||
<div className="flex items-center gap-2 mb-3 pb-2 border-b">
|
<div className="flex items-center gap-2 mb-3 pb-2 border-b">
|
||||||
<Users className="h-4 w-4 text-muted-foreground" />
|
<Users className="h-4 w-4 text-muted-foreground" />
|
||||||
@@ -365,7 +365,7 @@ function CommunicationsPageContent() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Panel 3: Sessions */}
|
{/* Panel 3: Sessions */}
|
||||||
<Card className="col-span-6 flex flex-col overflow-hidden">
|
<Card className="col-span-12 lg:col-span-6 flex flex-col overflow-hidden">
|
||||||
<CardContent className="p-3 flex flex-col h-full">
|
<CardContent className="p-3 flex flex-col h-full">
|
||||||
<div className="flex items-center gap-2 mb-3 pb-2 border-b">
|
<div className="flex items-center gap-2 mb-3 pb-2 border-b">
|
||||||
<MessageCircle className="h-4 w-4 text-muted-foreground" />
|
<MessageCircle className="h-4 w-4 text-muted-foreground" />
|
||||||
@@ -393,29 +393,29 @@ function CommunicationsPageContent() {
|
|||||||
export default function CommunicationsPage() {
|
export default function CommunicationsPage() {
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={
|
<Suspense fallback={
|
||||||
<div className="flex flex-col h-[calc(100vh-7rem)]">
|
<div className="flex flex-col lg:h-[calc(100vh-7rem)]">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<div>
|
<div>
|
||||||
<Skeleton className="h-9 w-48 mb-2" />
|
<Skeleton className="h-9 w-48 mb-2" />
|
||||||
<Skeleton className="h-5 w-64" />
|
<Skeleton className="h-5 w-64" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-12 gap-6 flex-1">
|
<div className="grid grid-cols-12 gap-4 lg:gap-6">
|
||||||
<Card className="col-span-3">
|
<Card className="col-span-12 lg:col-span-3">
|
||||||
<CardContent className="p-3 space-y-2">
|
<CardContent className="p-3 space-y-2">
|
||||||
{Array.from({ length: 6 }).map((_, i) => (
|
{Array.from({ length: 6 }).map((_, i) => (
|
||||||
<Skeleton key={i} className="h-8 w-full" />
|
<Skeleton key={i} className="h-8 w-full" />
|
||||||
))}
|
))}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className="col-span-3">
|
<Card className="col-span-12 lg:col-span-3">
|
||||||
<CardContent className="p-3 space-y-2">
|
<CardContent className="p-3 space-y-2">
|
||||||
{Array.from({ length: 4 }).map((_, i) => (
|
{Array.from({ length: 4 }).map((_, i) => (
|
||||||
<Skeleton key={i} className="h-10 w-full" />
|
<Skeleton key={i} className="h-10 w-full" />
|
||||||
))}
|
))}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card className="col-span-6" />
|
<Card className="col-span-12 lg:col-span-6" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}>
|
}>
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ export default function MetricsPage() {
|
|||||||
{/* Velocity Metrics */}
|
{/* Velocity Metrics */}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold mb-3">Velocity</h2>
|
<h2 className="text-lg font-semibold mb-3">Velocity</h2>
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-4">
|
||||||
<MetricCard
|
<MetricCard
|
||||||
title="Completed Today"
|
title="Completed Today"
|
||||||
value={completedToday}
|
value={completedToday}
|
||||||
@@ -241,7 +241,7 @@ export default function MetricsPage() {
|
|||||||
{/* Task Status */}
|
{/* Task Status */}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold mb-3">Task Status</h2>
|
<h2 className="text-lg font-semibold mb-3">Task Status</h2>
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5">
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5 xl:grid-cols-5 2xl:grid-cols-5">
|
||||||
<MetricCard
|
<MetricCard
|
||||||
title="Pending"
|
title="Pending"
|
||||||
value={pending}
|
value={pending}
|
||||||
@@ -273,7 +273,7 @@ export default function MetricsPage() {
|
|||||||
{/* Agent Status */}
|
{/* Agent Status */}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold mb-3">Agent Status</h2>
|
<h2 className="text-lg font-semibold mb-3">Agent Status</h2>
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-4">
|
||||||
<MetricCard
|
<MetricCard
|
||||||
title="Running"
|
title="Running"
|
||||||
value={runningAgents}
|
value={runningAgents}
|
||||||
@@ -304,7 +304,7 @@ export default function MetricsPage() {
|
|||||||
{/* Team Health */}
|
{/* Team Health */}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold mb-3">Team Health</h2>
|
<h2 className="text-lg font-semibold mb-3">Team Health</h2>
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5">
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5 xl:grid-cols-5 2xl:grid-cols-6">
|
||||||
{teamMetrics.map((tm) => (
|
{teamMetrics.map((tm) => (
|
||||||
<TeamHealthCard
|
<TeamHealthCard
|
||||||
key={tm.team}
|
key={tm.team}
|
||||||
@@ -346,7 +346,7 @@ function TokenUsageCostsSection() {
|
|||||||
<h2 className="text-lg font-semibold">Token Usage & Costs</h2>
|
<h2 className="text-lg font-semibold">Token Usage & Costs</h2>
|
||||||
|
|
||||||
{/* Row 1 — Summary cards */}
|
{/* Row 1 — Summary cards */}
|
||||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6">
|
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6 2xl:grid-cols-6">
|
||||||
<SummaryCard
|
<SummaryCard
|
||||||
title="Tokens Input"
|
title="Tokens Input"
|
||||||
value={summary ? fmtTokens(summary.tokens_input) : undefined}
|
value={summary ? fmtTokens(summary.tokens_input) : undefined}
|
||||||
@@ -392,7 +392,7 @@ function TokenUsageCostsSection() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Row 2 — Time series + model donut */}
|
{/* Row 2 — Time series + model donut */}
|
||||||
<div className="grid gap-4 lg:grid-cols-3">
|
<div className="grid gap-4 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-3">
|
||||||
<div className="lg:col-span-2">
|
<div className="lg:col-span-2">
|
||||||
<UsageTimeSeriesChart data={timeSeries} isLoading={loadingTS} />
|
<UsageTimeSeriesChart data={timeSeries} isLoading={loadingTS} />
|
||||||
</div>
|
</div>
|
||||||
@@ -400,13 +400,13 @@ function TokenUsageCostsSection() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Row 3 — Agent bar + team bar */}
|
{/* Row 3 — Agent bar + team bar */}
|
||||||
<div className="grid gap-4 lg:grid-cols-2">
|
<div className="grid gap-4 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2">
|
||||||
<AgentUsageChart data={agentUsage} isLoading={loadingAgents} />
|
<AgentUsageChart data={agentUsage} isLoading={loadingAgents} />
|
||||||
<TeamUsageChart data={teamUsage} isLoading={loadingTeams} />
|
<TeamUsageChart data={teamUsage} isLoading={loadingTeams} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Row 4 — Projection + cache efficiency */}
|
{/* Row 4 — Projection + cache efficiency */}
|
||||||
<div className="grid gap-4 sm:grid-cols-2">
|
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2">
|
||||||
<ProjectionCard projection={projection} isLoading={loadingProj} />
|
<ProjectionCard projection={projection} isLoading={loadingProj} />
|
||||||
<CacheEfficiencyCard cacheStats={cacheStats} isLoading={loadingCache} />
|
<CacheEfficiencyCard cacheStats={cacheStats} isLoading={loadingCache} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ export function AgentGrid({
|
|||||||
columns = 4,
|
columns = 4,
|
||||||
}: AgentGridProps) {
|
}: AgentGridProps) {
|
||||||
const gridCols = {
|
const gridCols = {
|
||||||
3: "md:grid-cols-3",
|
3: "md:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-4",
|
||||||
4: "md:grid-cols-3 lg:grid-cols-4",
|
4: "md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-5",
|
||||||
5: "md:grid-cols-3 lg:grid-cols-5",
|
5: "md:grid-cols-3 lg:grid-cols-5 xl:grid-cols-5 2xl:grid-cols-5",
|
||||||
}[columns] || "md:grid-cols-3 lg:grid-cols-4";
|
}[columns] || "md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-5";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function AuditorDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Top Row: Live Feeds + Quality Metrics */}
|
{/* Top Row: Live Feeds + Quality Metrics */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2 gap-6">
|
||||||
<LiveFeedsPanel
|
<LiveFeedsPanel
|
||||||
feeds={dashboard?.live_feeds}
|
feeds={dashboard?.live_feeds}
|
||||||
isLoading={loadingDashboard}
|
isLoading={loadingDashboard}
|
||||||
@@ -60,7 +60,7 @@ export function AuditorDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom Row: Flagged Items + Reports */}
|
{/* Bottom Row: Flagged Items + Reports */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2 gap-6">
|
||||||
<FlaggedItemsPanel flags={flags} isLoading={loadingFlags} />
|
<FlaggedItemsPanel flags={flags} isLoading={loadingFlags} />
|
||||||
<ReportsPanel reports={reports} isLoading={loadingReports} />
|
<ReportsPanel reports={reports} isLoading={loadingReports} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export function CommandCenter() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Metrics, Alerts, and Usage Row */}
|
{/* Metrics, Alerts, and Usage Row */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-3 gap-6">
|
||||||
<KeyMetricsPanel
|
<KeyMetricsPanel
|
||||||
metrics={overview?.key_metrics}
|
metrics={overview?.key_metrics}
|
||||||
isLoading={loadingOverview}
|
isLoading={loadingOverview}
|
||||||
@@ -73,7 +73,7 @@ export function CommandCenter() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Blockers and Activity Row */}
|
{/* Blockers and Activity Row */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2 gap-6">
|
||||||
<ActiveBlockersPanel tasks={tasks} isLoading={loadingTasks} />
|
<ActiveBlockersPanel tasks={tasks} isLoading={loadingTasks} />
|
||||||
<RecentActivityFeed
|
<RecentActivityFeed
|
||||||
activities={activity as Activity[] | undefined}
|
activities={activity as Activity[] | undefined}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ interface TeamHealthCardsProps {
|
|||||||
export function TeamHealthCards({ teams, isLoading }: TeamHealthCardsProps) {
|
export function TeamHealthCards({ teams, isLoading }: TeamHealthCardsProps) {
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-6 gap-4">
|
||||||
{[...Array(4)].map((_, i) => (
|
{[...Array(4)].map((_, i) => (
|
||||||
<Skeleton key={i} className="h-48" />
|
<Skeleton key={i} className="h-48" />
|
||||||
))}
|
))}
|
||||||
@@ -29,7 +29,7 @@ export function TeamHealthCards({ teams, isLoading }: TeamHealthCardsProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-6 gap-4">
|
||||||
{teams.map((health) => (
|
{teams.map((health) => (
|
||||||
<TeamHealthCard key={health.team} health={health} />
|
<TeamHealthCard key={health.team} health={health} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ function GitBrowserContent() {
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold tracking-tight">Git Operations</h1>
|
<h1 className="text-3xl font-bold tracking-tight">Git Operations</h1>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
@@ -203,7 +203,7 @@ function GitBrowserContent() {
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* Project Selector */}
|
{/* Project Selector */}
|
||||||
<Select value={projectSlug} onValueChange={handleProjectChange}>
|
<Select value={projectSlug} onValueChange={handleProjectChange}>
|
||||||
<SelectTrigger className="w-64">
|
<SelectTrigger className="w-full sm:w-64">
|
||||||
<FolderGit2 className="h-4 w-4 mr-2" />
|
<FolderGit2 className="h-4 w-4 mr-2" />
|
||||||
<SelectValue placeholder="Select a project..." />
|
<SelectValue placeholder="Select a project..." />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
|
|||||||
Reference in New Issue
Block a user