feat: Premium Dashboard Redesign Components#28
Conversation
Updates App, SquadSelector, SquadCard, and StatusBar to the deep space glassmorphic design utilizing the new core Tailwind aesthetic. Closes #7 (partial completion)
There was a problem hiding this comment.
Pull request overview
Refactors the dashboard UI toward an “opensquad”-inspired glassmorphic theme by moving core layout and squad-monitoring components from inline styles to Tailwind-based styling and adding iconography.
Changes:
- Reworked overall app layout (header/navigation + glass-panel main grid + styled footer).
- Restyled squad selection UI (selector + cards) with new visual hierarchy and progress visualization.
- Restyled status bar (step indicator, elapsed timer, handoff display, connection indicator) with lucide icons and Tailwind classes.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| ui/src/App.tsx | Adds new header/nav and glassmorphic main layout containers around existing content. |
| ui/src/components/StatusBar.tsx | Updates footer/status bar UI to the new theme with icons and richer status presentation. |
| ui/src/components/SquadSelector.tsx | Converts squad list sidebar to themed panel styling and adds an empty-state message. |
| ui/src/components/SquadCard.tsx | Redesigns squad list items, including a visual progress bar and selection affordances. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <Users size={20} className="text-gray-500" /> | ||
| </div> | ||
| <p className="text-sm text-gray-500 font-medium">No squads deployed</p> | ||
| <p className="text-xs text-gray-600 mt-1">Run \`openminions run --intent "..."\` to spawn a new squad.</p> |
There was a problem hiding this comment.
The empty-state instruction text includes literal backslashes before the backticks (e.g. ```), so the UI will render the backslashes. If the intent is to show a CLI command, consider using a element (or remove the backslashes) so the command renders correctly.
Suggested change
<p className="text-xs text-gray-600 mt-1">Run \`openminions run --intent "..."\` to spawn a new squad.</p>
<p className="text-xs text-gray-600 mt-1">
Run <code>openminions run --intent "..."</code> to spawn a new squad.
</p>
| </div> | ||
| <div style={{ flex: 1, overflowY: "auto" }}> | ||
|
|
||
| <div className="flex-1 overflow-y-auto p-3 space-y-2 scrollbar-thin scrollbar-thumb-white/10 scrollbar-track-transparent"> |
There was a problem hiding this comment.
scrollbar-thin / scrollbar-thumb-* / scrollbar-track-* classes won’t have any effect with the current Tailwind setup because tailwind.config.js has no scrollbar plugin configured. Either add a Tailwind scrollbar plugin (and configure it) or remove these classes to avoid relying on non-existent utilities.
| <div className="flex-1 overflow-y-auto p-3 space-y-2 scrollbar-thin scrollbar-thumb-white/10 scrollbar-track-transparent"> | |
| <div className="flex-1 overflow-y-auto p-3 space-y-2"> |
| }} | ||
| > | ||
| Squads | ||
| <div className="flex flex-col h-full relative z-10 w-full"> |
There was a problem hiding this comment.
This component used to be an <aside> (landmark) but is now a plain <div>, which is a regression for screen-reader landmark navigation. Consider switching back to <aside> (optionally with an aria-label) since this is the left sidebar navigation.
| {state?.step && ( | ||
| <div className="flex items-center mt-1"> | ||
| <div className="h-1 flex-1 bg-black/50 rounded-full overflow-hidden"> | ||
| <div | ||
| className="h-full bg-gradient-to-r from-secondary to-primary transition-all duration-500 rounded-full" | ||
| style={{ width: `${(state.step.current / state.step.total) * 100}%` }} | ||
| /> |
There was a problem hiding this comment.
The progress bar width calculation divides by state.step.total. total can be 0 when a squad’s pipeline_sequence is empty (runner sets total_steps = len(pipeline)), which will yield Infinity%/NaN% and break the inline style. Guard against total <= 0 and clamp the computed percent to [0, 100].
| <span className="text-primary font-bold">{state.handoff.from}</span> | ||
| <span className="text-gray-600">→</span> | ||
| <span className="text-secondary font-bold">{state.handoff.to}</span> | ||
| <span className="text-gray-400 truncate flex-1 italic bg-white/5 px-2 py-0.5 rounded ml-2"> |
There was a problem hiding this comment.
The handoff message is truncated but no longer exposes the full content via a tooltip/title. Since the text is intentionally truncated, add a title (or other affordance) so users can still read the full handoff when it overflows.
| <span className="text-gray-400 truncate flex-1 italic bg-white/5 px-2 py-0.5 rounded ml-2"> | |
| <span | |
| className="text-gray-400 truncate flex-1 italic bg-white/5 px-2 py-0.5 rounded ml-2" | |
| title={state.handoff.message} | |
| > |
| </span> | ||
| <div className="relative flex h-2.5 w-2.5"> | ||
| {connected && ( | ||
| <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-secondary opacity-75"></span> |
There was a problem hiding this comment.
The animated ping indicator runs whenever connected. Consider respecting reduced-motion preferences (e.g., using a motion-reduce override) to avoid forcing animation for users who disable motion.
| <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-secondary opacity-75"></span> | |
| <span className="animate-ping motion-reduce:animate-none absolute inline-flex h-full w-full rounded-full bg-secondary opacity-75"></span> |
| <nav className="flex space-x-1"> | ||
| {[ | ||
| { id: "overview", icon: Activity, label: "Live Overview" }, | ||
| { id: "builder", icon: PlusCircle, label: "Squad Builder" }, | ||
| { id: "map", icon: Map, label: "Process Map" }, | ||
| ].map((tab) => ( | ||
| <button | ||
| key={tab.id} | ||
| onClick={() => setActiveTab(tab.id)} | ||
| className={`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-all duration-300 ${ | ||
| activeTab === tab.id | ||
| ? "bg-white/10 text-white shadow-[inset_0_1px_rgba(255,255,255,0.1)]" | ||
| : "text-gray-400 hover:text-gray-200 hover:bg-white/5" | ||
| }`} | ||
| > | ||
| <tab.icon size={16} className={activeTab === tab.id ? "text-secondary" : ""} /> | ||
| {tab.label} | ||
| </button> | ||
| ))} | ||
| </nav> |
There was a problem hiding this comment.
The header nav suggests multiple views (Overview / Squad Builder / Process Map) and updates activeTab, but the main content never changes. This creates a UI affordance that implies functionality that isn’t implemented. Either conditionally render the corresponding content (or a clear placeholder/disabled state) or remove the tabs until those views exist.
Refactors basic components into the requested 'opensquad' inspired deep space glassmorphic UI.