fix(web,ui): visual polish aligned to design reference (#457)
- Add responsive CSS classes for dashboard grid and metrics strip (3-col tablet, 2-col mobile, single-col stacking) - Fix QuickActions layout from vertical to horizontal per design ref - Replace non-existent var(--r-md) token with var(--r) in OrchestratorSessions and QuickActions - Fix NavItem borderRadius to var(--r-sm) matching reference - Fix ActivityFeed spacing (gap 12px, padding 10px per reference) - Refactor MetricsStrip to CSS class-based responsive grid Task: MS-P1-003 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,13 +11,7 @@ export default function DashboardPage(): ReactElement {
|
|||||||
return (
|
return (
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
||||||
<DashboardMetrics />
|
<DashboardMetrics />
|
||||||
<div
|
<div className="dash-grid">
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "1fr 320px",
|
|
||||||
gap: 16,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 16, minWidth: 0 }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: 16, minWidth: 0 }}>
|
||||||
<OrchestratorSessions />
|
<OrchestratorSessions />
|
||||||
<QuickActions />
|
<QuickActions />
|
||||||
|
|||||||
@@ -765,6 +765,62 @@ body::before {
|
|||||||
animation: scaleIn 0.1s ease-out;
|
animation: scaleIn 0.1s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Dashboard Layout — Responsive Grids
|
||||||
|
----------------------------------------------------------------------------- */
|
||||||
|
.metrics-strip {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(var(--ms-cols, 6), 1fr);
|
||||||
|
gap: 0;
|
||||||
|
border-radius: var(--r-lg);
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-cell {
|
||||||
|
border-left: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-cell:first-child {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.metrics-strip {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-cell:nth-child(3n + 1) {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.metrics-strip {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-cell:nth-child(3n + 1) {
|
||||||
|
border-left: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-cell:nth-child(2n + 1) {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 320px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.dash-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
Responsive Typography Adjustments
|
Responsive Typography Adjustments
|
||||||
----------------------------------------------------------------------------- */
|
----------------------------------------------------------------------------- */
|
||||||
|
|||||||
@@ -102,8 +102,8 @@ function ActivityItemRow({ item }: ActivityItemRowProps): ReactElement {
|
|||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "flex-start",
|
alignItems: "flex-start",
|
||||||
gap: 10,
|
gap: 12,
|
||||||
padding: "8px 0",
|
padding: "10px 0",
|
||||||
borderBottom: "1px solid var(--border)",
|
borderBottom: "1px solid var(--border)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ function OrchCard({ session }: OrchCardProps): ReactElement {
|
|||||||
style={{
|
style={{
|
||||||
background: "var(--bg-mid)",
|
background: "var(--bg-mid)",
|
||||||
border: "1px solid var(--border)",
|
border: "1px solid var(--border)",
|
||||||
borderRadius: "var(--r-md)",
|
borderRadius: "var(--r)",
|
||||||
padding: "12px 14px",
|
padding: "12px 14px",
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ interface QuickAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const actions: QuickAction[] = [
|
const actions: QuickAction[] = [
|
||||||
{ id: "new-project", label: "New Project", icon: "🚀", iconBg: "rgba(47,128,255,0.15)" },
|
{ id: "new-project", label: "New Project", icon: "🚀", iconBg: "rgba(47,128,255,0.12)" },
|
||||||
{ id: "spawn-agent", label: "Spawn Agent", icon: "🤖", iconBg: "rgba(139,92,246,0.15)" },
|
{ id: "spawn-agent", label: "Spawn Agent", icon: "🤖", iconBg: "rgba(139,92,246,0.12)" },
|
||||||
{ id: "view-telemetry", label: "View Telemetry", icon: "📊", iconBg: "rgba(20,184,166,0.15)" },
|
{ id: "view-telemetry", label: "View Telemetry", icon: "📊", iconBg: "rgba(20,184,166,0.12)" },
|
||||||
{ id: "review-tasks", label: "Review Tasks", icon: "📋", iconBg: "rgba(245,158,11,0.15)" },
|
{ id: "review-tasks", label: "Review Tasks", icon: "📋", iconBg: "rgba(245,158,11,0.12)" },
|
||||||
];
|
];
|
||||||
|
|
||||||
interface ActionButtonProps {
|
interface ActionButtonProps {
|
||||||
@@ -36,24 +36,25 @@ function ActionButton({ action }: ActionButtonProps): ReactElement {
|
|||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
|
||||||
gap: 8,
|
gap: 8,
|
||||||
padding: "16px 12px",
|
padding: "10px 12px",
|
||||||
borderRadius: "var(--r-md)",
|
borderRadius: "var(--r)",
|
||||||
border: `1px solid ${hovered ? "var(--ms-border-700)" : "var(--border)"}`,
|
border: `1px solid ${hovered ? "var(--ms-border-700)" : "var(--border)"}`,
|
||||||
background: hovered ? "var(--surface)" : "var(--bg-mid)",
|
background: hovered ? "var(--surface)" : "var(--bg-mid)",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
transition: "border-color 0.15s, background 0.15s",
|
transition: "border-color 0.15s, background 0.15s, color 0.15s",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
fontSize: "0.8rem",
|
||||||
|
fontWeight: 600,
|
||||||
|
color: hovered ? "var(--text)" : "var(--text-2)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
borderRadius: 6,
|
borderRadius: 5,
|
||||||
background: action.iconBg,
|
background: action.iconBg,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
@@ -63,15 +64,7 @@ function ActionButton({ action }: ActionButtonProps): ReactElement {
|
|||||||
>
|
>
|
||||||
{action.icon}
|
{action.icon}
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span>{action.label}</span>
|
||||||
style={{
|
|
||||||
fontSize: "0.8rem",
|
|
||||||
fontWeight: 600,
|
|
||||||
color: "var(--text)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{action.label}
|
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -84,7 +77,7 @@ export function QuickActions(): ReactElement {
|
|||||||
style={{
|
style={{
|
||||||
display: "grid",
|
display: "grid",
|
||||||
gridTemplateColumns: "1fr 1fr",
|
gridTemplateColumns: "1fr 1fr",
|
||||||
gap: 10,
|
gap: 8,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{actions.map((action) => (
|
{actions.map((action) => (
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ function NavItem({ item, isActive, collapsed }: NavItemProps): React.JSX.Element
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "11px",
|
gap: "11px",
|
||||||
padding: "9px 10px",
|
padding: "9px 10px",
|
||||||
borderRadius: "6px",
|
borderRadius: "var(--r-sm)",
|
||||||
fontSize: "0.875rem",
|
fontSize: "0.875rem",
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
color: isActive ? "var(--text)" : "var(--muted)",
|
color: isActive ? "var(--text)" : "var(--muted)",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export interface MetricsStripProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function MetricCellItem({ cell, isFirst }: { cell: MetricCell; isFirst: boolean }): ReactElement {
|
function MetricCellItem({ cell }: { cell: MetricCell }): ReactElement {
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
|
|
||||||
const trendColor =
|
const trendColor =
|
||||||
@@ -28,6 +28,7 @@ function MetricCellItem({ cell, isFirst }: { cell: MetricCell; isFirst: boolean
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
className="metric-cell"
|
||||||
onMouseEnter={(): void => {
|
onMouseEnter={(): void => {
|
||||||
setHovered(true);
|
setHovered(true);
|
||||||
}}
|
}}
|
||||||
@@ -37,7 +38,6 @@ function MetricCellItem({ cell, isFirst }: { cell: MetricCell; isFirst: boolean
|
|||||||
style={{
|
style={{
|
||||||
padding: "14px 16px",
|
padding: "14px 16px",
|
||||||
background: hovered ? "var(--surface-2)" : "var(--surface)",
|
background: hovered ? "var(--surface-2)" : "var(--surface)",
|
||||||
borderLeft: isFirst ? "none" : "1px solid var(--border)",
|
|
||||||
borderTop: `2px solid ${cell.color}`,
|
borderTop: `2px solid ${cell.color}`,
|
||||||
transition: "background 0.15s ease",
|
transition: "background 0.15s ease",
|
||||||
}}
|
}}
|
||||||
@@ -82,17 +82,15 @@ function MetricCellItem({ cell, isFirst }: { cell: MetricCell; isFirst: boolean
|
|||||||
export function MetricsStrip({ cells, className = "" }: MetricsStripProps): ReactElement {
|
export function MetricsStrip({ cells, className = "" }: MetricsStripProps): ReactElement {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={className}
|
className={`metrics-strip ${className}`.trim()}
|
||||||
style={{
|
style={
|
||||||
display: "grid",
|
{
|
||||||
gridTemplateColumns: `repeat(${String(cells.length)}, 1fr)`,
|
"--ms-cols": String(cells.length),
|
||||||
borderRadius: "var(--r-lg)",
|
} as React.CSSProperties
|
||||||
overflow: "hidden",
|
}
|
||||||
border: "1px solid var(--border)",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{cells.map((cell, index) => (
|
{cells.map((cell) => (
|
||||||
<MetricCellItem key={cell.label} cell={cell} isFirst={index === 0} />
|
<MetricCellItem key={cell.label} cell={cell} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user