feat(dashboard): migrate to tailwind css + shadcn/ui component system#262
Open
JacksCodeVault wants to merge 1 commit into
Open
feat(dashboard): migrate to tailwind css + shadcn/ui component system#262JacksCodeVault wants to merge 1 commit into
JacksCodeVault wants to merge 1 commit into
Conversation
This commit modernizes the OpenWA dashboard by migrating from a custom CSS architecture to a scalable component-based design system Core infrastructure: - Added Tailwind CSS, PostCSS, and autoprefixer configuration - Integrated shadcn/ui component library with base components - Added class-variance-authority for component variant management - Configured tailwind-merge for proper class resolution Component system: - Created reusable UI components (badge, button, dialog, dropdown-menu, input, scroll-area, select, separator, tabs, tooltip, avatar, alert-dialog) - All components follow shadcn/ui conventions and support theming - Components leverage Radix UI for accessibility and behavior Page components: - Refactored ApiKeys, Chats, Dashboard, Infrastructure, Login, Logs, MessageTester, Plugins, Sessions, and Webhooks pages - Removed individual page CSS files (replaced with Tailwind utilities) - Updated all pages to use new UI component library Dependencies: - Added: @radix-ui/*, @phosphor-icons/react, class-variance-authority, clsx, tailwindcss, postcss, autoprefixer, @emoji-mart/* - Maintained: existing React, routing, query, and i18n dependencies Styling: - Migrated from CSS Modules to Tailwind CSS utilities - Consolidated global styles in index.css - Improved layout consistency across all pages - Added responsive design support via Tailwind breakpoints Fixes: - Fixed TypeScript issues with unused exports in archiver.d.ts - Fixed deprecated baseUrl warning in tsconfig.app.json - Fixed Radix UI Slot API usage in component implementations - Fixed unused variable warnings in component declarations Benefits: - Reduced CSS file count by consolidating styles into Tailwind utilities - Improved consistency with reusable component library - Enhanced maintainability with centralized design tokens - Better accessibility with Radix UI primitives - Faster development with pre-built, tested components - Improved type safety and developer experience Testing: - All 429 unit tests pass - Dashboard build completes successfully with no TypeScript errors - ESLint and Prettier checks pass - All pages render correctly with new component system BREAKING CHANGE: CSS class names and structure have changed significantly. Custom CSS overrides will need to be updated to use Tailwind utilities.
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR appears to migrate the dashboard UI from bespoke CSS to a Tailwind + shadcn/Radix component approach, while also expanding frontend API typings and adding a local archiver type declaration.
Changes:
- Added TailwindCSS setup (config, PostCSS, CSS variables) and introduced a new UI component library layer (Radix-based).
- Refactored multiple dashboard pages/components to use new UI primitives and swapped icon set to
@phosphor-icons/react. - Extended API typings/endpoints (e.g., new session status, chat profile picture, contact profile picture fetch).
Reviewed changes
Copilot reviewed 56 out of 59 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/archiver.d.ts | Adds local TypeScript declarations for the archiver module. |
| dashboard/vite.config.ts | Adds @ alias to dashboard Vite config for simpler imports. |
| dashboard/tsconfig.app.json | Adds TS path alias mapping and suppresses TS 6.0 deprecations. |
| dashboard/tailwind.config.js | Introduces Tailwind configuration with custom theme tokens. |
| dashboard/src/services/api.ts | Extends API types (new status, profilePic) and adds profile picture endpoint. |
| dashboard/src/pages/Webhooks.css | Removes legacy CSS (likely replaced by Tailwind styling). |
| dashboard/src/pages/Sessions.css | Removes legacy CSS (likely replaced by Tailwind styling). |
| dashboard/src/pages/Plugins.tsx | Refactors Plugins page to new UI primitives + Phosphor icons. |
| dashboard/src/pages/Plugins.css | Removes legacy CSS (replaced by Tailwind styling). |
| dashboard/src/pages/MessageTester.tsx | Refactors Message Tester to new UI primitives + Tailwind. |
| dashboard/src/pages/Logs.tsx | Refactors Logs page to new UI primitives + Tailwind. |
| dashboard/src/pages/Logs.css | Removes legacy CSS (replaced by Tailwind styling). |
| dashboard/src/pages/Login.tsx | Refactors Login page to new UI primitives + Tailwind. |
| dashboard/src/pages/Login.css | Removes legacy CSS (replaced by Tailwind styling). |
| dashboard/src/pages/Infrastructure.css | Removes legacy CSS (likely replaced elsewhere / pending migration). |
| dashboard/src/pages/Dashboard.tsx | Refactors Dashboard page to Tailwind layout and new UI components. |
| dashboard/src/pages/Dashboard.css | Removes legacy CSS (replaced by Tailwind styling). |
| dashboard/src/pages/ApiKeys.css | Removes legacy CSS (likely replaced elsewhere / pending migration). |
| dashboard/src/lib/utils.ts | Adds cn() helper (clsx + tailwind-merge). |
| dashboard/src/index.css | Replaces prior global CSS with Tailwind layers + CSS variable tokens. |
| dashboard/src/i18n/locales/zh-HK.json | Adds common.viewAll translation string. |
| dashboard/src/i18n/locales/zh-CN.json | Adds common.viewAll translation string. |
| dashboard/src/i18n/locales/te.json | Adds common.viewAll translation string. |
| dashboard/src/i18n/locales/it.json | Adds common.viewAll translation string. |
| dashboard/src/i18n/locales/he.json | Adds common.viewAll translation string. |
| dashboard/src/i18n/locales/fr.json | Adds common.viewAll translation string. |
| dashboard/src/i18n/locales/en.json | Adds filters/menu strings and new authenticating status label. |
| dashboard/src/i18n/locales/ar.json | Adds common.viewAll translation string. |
| dashboard/src/hooks/useTheme.ts | Adjusts theme application to use .dark class + listens to OS changes. |
| dashboard/src/components/ui/tooltip.tsx | Adds Radix tooltip wrapper styled with Tailwind utilities. |
| dashboard/src/components/ui/tabs.tsx | Adds Radix tabs wrapper. |
| dashboard/src/components/ui/separator.tsx | Adds Radix separator wrapper. |
| dashboard/src/components/ui/select.tsx | Adds Radix select wrapper. |
| dashboard/src/components/ui/scroll-area.tsx | Adds a lightweight ScrollArea wrapper (non-Radix). |
| dashboard/src/components/ui/input.tsx | Adds styled input primitive. |
| dashboard/src/components/ui/dropdown-menu.tsx | Adds Radix dropdown-menu wrappers. |
| dashboard/src/components/ui/dialog.tsx | Adds Radix dialog wrappers. |
| dashboard/src/components/ui/card.tsx | Adds Card primitive. |
| dashboard/src/components/ui/button.tsx | Adds Button primitive (CVA variants). |
| dashboard/src/components/ui/badge.tsx | Adds Badge primitive (CVA variants). |
| dashboard/src/components/ui/avatar.tsx | Adds Radix avatar wrapper. |
| dashboard/src/components/ui/alert-dialog.tsx | Adds Radix alert-dialog wrapper. |
| dashboard/src/components/Toast.tsx | Adjusts toast icon rendering to fit updated styling. |
| dashboard/src/components/Toast.css | Updates toast styling to use CSS variables / new theme tokens. |
| dashboard/src/components/Layout.tsx | Refactors navigation/sidebar to Tailwind + Tooltip + Phosphor icons. |
| dashboard/src/components/Layout.css | Replaces prior layout CSS with minimal base + scrollbar styling. |
| dashboard/src/components/ErrorBoundary.tsx | Refactors error boundary UI to new Button + icons. |
| dashboard/postcss.config.js | Adds PostCSS config for Tailwind + autoprefixer. |
| dashboard/package.json | Adds Tailwind/shadcn/Radix dependencies and related utilities. |
| dashboard/index.html | Adds inline theme bootstrapping to avoid flash of wrong theme. |
| dashboard/components.json | Adds shadcn UI configuration and path aliases. |
| .gitignore | Updates agent-related ignores and adds several tool folders to ignore list. |
Comments suppressed due to low confidence (4)
dashboard/src/pages/Dashboard.tsx:1
- These newly introduced UI strings are hard-coded in English instead of using
t(...), which breaks i18n consistency (especially since this PR updates locale files). Please move these into translation keys (e.g.,dashboard.loading,dashboard.errorTitle,dashboard.overviewLabel,common.stop,common.open, etc.) and uset()so they’re localizable.
dashboard/src/pages/Dashboard.tsx:1 - These newly introduced UI strings are hard-coded in English instead of using
t(...), which breaks i18n consistency (especially since this PR updates locale files). Please move these into translation keys (e.g.,dashboard.loading,dashboard.errorTitle,dashboard.overviewLabel,common.stop,common.open, etc.) and uset()so they’re localizable.
dashboard/src/pages/Dashboard.tsx:1 - These newly introduced UI strings are hard-coded in English instead of using
t(...), which breaks i18n consistency (especially since this PR updates locale files). Please move these into translation keys (e.g.,dashboard.loading,dashboard.errorTitle,dashboard.overviewLabel,common.stop,common.open, etc.) and uset()so they’re localizable.
dashboard/src/pages/Dashboard.tsx:1 - These newly introduced UI strings are hard-coded in English instead of using
t(...), which breaks i18n consistency (especially since this PR updates locale files). Please move these into translation keys (e.g.,dashboard.loading,dashboard.errorTitle,dashboard.overviewLabel,common.stop,common.open, etc.) and uset()so they’re localizable.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+44
to
+47
| className={cn( | ||
| "z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-lg bg-foreground px-3 py-1.5 text-xs text-background shadow-md has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-none data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", | ||
| className | ||
| )} |
Comment on lines
+170
to
+178
| <NavLink | ||
| to={to} | ||
| className={cn( | ||
| "nav-item flex items-center gap-3 p-3 rounded-lg text-muted-foreground hover:text-foreground transition-colors", | ||
| isCollapsed && "justify-center px-0" | ||
| )} | ||
| end={to === '/'} | ||
| onClick={handleNavClick} | ||
| > |
Comment on lines
1
to
7
| .layout { | ||
| display: flex; | ||
| min-height: 100vh; | ||
| } | ||
|
|
||
| /* ==================== Sidebar ==================== */ | ||
| .sidebar { | ||
| width: 260px; | ||
| background: var(--bg-white); | ||
| border-right: 1px solid var(--border); | ||
| display: flex; | ||
| flex-direction: column; | ||
| position: fixed; | ||
| height: 100vh; | ||
| z-index: 100; | ||
| transition: | ||
| width 0.3s ease, | ||
| transform 0.3s ease; | ||
| } | ||
|
|
||
| .sidebar.collapsed { | ||
| width: 72px; | ||
| } | ||
|
|
||
| .sidebar-header { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 0.75rem; | ||
| padding: 1.5rem; | ||
| border-bottom: 1px solid var(--border); | ||
| min-height: 72px; | ||
| } | ||
|
|
||
| .sidebar.collapsed .sidebar-header { | ||
| justify-content: center; | ||
| padding: 1.5rem 1rem; | ||
| } | ||
|
|
||
| .sidebar-logo { | ||
| width: 28px; | ||
| height: 28px; | ||
| object-fit: contain; | ||
| flex-shrink: 0; | ||
| } | ||
|
|
||
| .mobile-brand .sidebar-logo { | ||
| width: 24px; | ||
| height: 24px; | ||
| } | ||
|
|
||
| .sidebar-brand { | ||
| display: flex; | ||
| flex-direction: column; | ||
| width: 100vw; | ||
| overflow: hidden; | ||
| white-space: nowrap; | ||
| } | ||
|
|
||
| .brand-name { | ||
| font-size: 1.125rem; | ||
| font-weight: 800; | ||
| color: var(--text-primary); | ||
| letter-spacing: -0.01em; | ||
| } | ||
|
|
||
| .brand-subtitle { | ||
| font-size: 0.7rem; | ||
| font-weight: 500; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| color: var(--text-muted); | ||
| background-color: var(--background); | ||
| } |
Comment on lines
+26
to
33
| ::-webkit-scrollbar-thumb { | ||
| background: rgba(var(--foreground), 0.2); | ||
| border-radius: 3px; | ||
| } | ||
|
|
||
| [dir="rtl"] .sidebar.collapsed .language-menu-list { | ||
| right: calc(100% + 0.5rem); | ||
| left: auto; | ||
| ::-webkit-scrollbar-thumb:hover { | ||
| background: rgba(var(--foreground), 0.3); | ||
| } |
| @@ -1,10 +1,12 @@ | |||
| import { useNavigate } from 'react-router-dom'; | |||
Comment on lines
+137
to
+139
| isMobile && "mobile fixed inset-y-0 left-0 z-50", | ||
| isMobile && !isMobileOpen && "-translate-x-full", | ||
| isMobileOpen && "translate-x-0" |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 56 out of 59 changed files in this pull request and generated 6 comments.
Comments suppressed due to low confidence (4)
dashboard/src/pages/Logs.tsx:1
- These filters are clickable
<span>badges. Spans aren’t keyboard-focusable and don’t provide button semantics, so the filter controls aren’t accessible. Use a<button>(or yourButtoncomponent) instead, or add properrole=\"button\",tabIndex={0}, and keyboard handlers.
dashboard/src/pages/Login.tsx:1 - The visibility toggle is removed from the tab order with
tabIndex={-1}, which prevents keyboard users from toggling password visibility. RemovetabIndex={-1}and add an accessible label (e.g.,aria-label) so it’s operable via keyboard and screen readers.
dashboard/src/pages/Dashboard.tsx:1 - This introduces a hard-coded UI string (
Overview) in a localized app. Please replace it with a translation key (e.g., underdashboard.*orcommon.*) to avoid i18n regressions.
dashboard/src/pages/Dashboard.tsx:1 - This introduces a hard-coded loading message (
Loading dashboard) in a localized UI. Prefert(...)with a translation key to keep language parity.
Comment on lines
+44
to
+47
| className={cn( | ||
| "z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-lg bg-foreground px-3 py-1.5 text-xs text-background shadow-md has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-none data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", | ||
| className | ||
| )} |
| <SelectPrimitive.Content | ||
| data-slot="select-content" | ||
| data-align-trigger={position === "item-aligned"} | ||
| className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )} |
Comment on lines
1
to
7
| .layout { | ||
| display: flex; | ||
| min-height: 100vh; | ||
| } | ||
|
|
||
| /* ==================== Sidebar ==================== */ | ||
| .sidebar { | ||
| width: 260px; | ||
| background: var(--bg-white); | ||
| border-right: 1px solid var(--border); | ||
| display: flex; | ||
| flex-direction: column; | ||
| position: fixed; | ||
| height: 100vh; | ||
| z-index: 100; | ||
| transition: | ||
| width 0.3s ease, | ||
| transform 0.3s ease; | ||
| } | ||
|
|
||
| .sidebar.collapsed { | ||
| width: 72px; | ||
| } | ||
|
|
||
| .sidebar-header { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 0.75rem; | ||
| padding: 1.5rem; | ||
| border-bottom: 1px solid var(--border); | ||
| min-height: 72px; | ||
| } | ||
|
|
||
| .sidebar.collapsed .sidebar-header { | ||
| justify-content: center; | ||
| padding: 1.5rem 1rem; | ||
| } | ||
|
|
||
| .sidebar-logo { | ||
| width: 28px; | ||
| height: 28px; | ||
| object-fit: contain; | ||
| flex-shrink: 0; | ||
| } | ||
|
|
||
| .mobile-brand .sidebar-logo { | ||
| width: 24px; | ||
| height: 24px; | ||
| } | ||
|
|
||
| .sidebar-brand { | ||
| display: flex; | ||
| flex-direction: column; | ||
| width: 100vw; | ||
| overflow: hidden; | ||
| white-space: nowrap; | ||
| } | ||
|
|
||
| .brand-name { | ||
| font-size: 1.125rem; | ||
| font-weight: 800; | ||
| color: var(--text-primary); | ||
| letter-spacing: -0.01em; | ||
| } | ||
|
|
||
| .brand-subtitle { | ||
| font-size: 0.7rem; | ||
| font-weight: 500; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| color: var(--text-muted); | ||
| background-color: var(--background); | ||
| } |
Comment on lines
+26
to
33
| ::-webkit-scrollbar-thumb { | ||
| background: rgba(var(--foreground), 0.2); | ||
| border-radius: 3px; | ||
| } | ||
|
|
||
| [dir="rtl"] .sidebar.collapsed .language-menu-list { | ||
| right: calc(100% + 0.5rem); | ||
| left: auto; | ||
| ::-webkit-scrollbar-thumb:hover { | ||
| background: rgba(var(--foreground), 0.3); | ||
| } |
Comment on lines
+170
to
+178
| <NavLink | ||
| to={to} | ||
| className={cn( | ||
| "nav-item flex items-center gap-3 p-3 rounded-lg text-muted-foreground hover:text-foreground transition-colors", | ||
| isCollapsed && "justify-center px-0" | ||
| )} | ||
| end={to === '/'} | ||
| onClick={handleNavClick} | ||
| > |
Comment on lines
+259
to
+263
| <main className={cn( | ||
| "main-content flex-1 h-screen flex flex-col overflow-hidden" | ||
| )}> | ||
| <Outlet /> | ||
| </main> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This commit modernizes the OpenWA dashboard by migrating from a custom CSS architecture to a scalable component-based design system
Core infrastructure:
Component system:
Page components:
Dependencies:
Styling:
Fixes:
Benefits:
Testing:
BREAKING CHANGE: CSS class names and structure have changed significantly. Custom CSS overrides will need to be updated to use Tailwind utilities.