Conversation
There was a problem hiding this comment.
Pull request overview
This PR revamps the Admin Dashboard UI to better align with the newer design system by updating styling for shared UI components (dropdown), analytics cards/layout, and the catalog table/list presentation.
Changes:
- Replaced the common
Dropdownwith a custom trigger/menu UI and new styling (including dark theme rules). - Updated catalog and analytics CSS modules to the new card/table visual style and responsive layouts.
- Introduced new
AdminDashboard.module.cssand added anadmin-dashboardwrapper class hook inAdminDashboard.jsx.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/ui/src/components/common/dropdown/Dropdown.module.css | New dropdown trigger/menu styling, animations, and dark-mode rules. |
| packages/ui/src/components/common/dropdown/Dropdown.jsx | Reworked dropdown into a custom trigger + menu while keeping a hidden <select>. |
| packages/ui/src/components/catalog/ImageUploadModal.module.css | Tweaked preview/dropzone form layout styles. |
| packages/ui/src/components/catalog/CatalogItem.module.css | Updated catalog row styling and added placeholder/name/meta styling. |
| packages/ui/src/components/catalog/CatalogItem.jsx | Added a preview placeholder and richer name/meta presentation. |
| packages/ui/src/components/catalog/Catalog.module.css | Reworked catalog header/table/footer styling to a card/table layout. |
| packages/ui/src/components/analytics/AnalyticsCard.module.css | Updated analytics cards to the new visual style and simplified responsive rules. |
| packages/ui/src/components/analytics/Analytics.module.css | Switched analytics layout to a responsive grid. |
| packages/ui/src/components/admin/AdminDashboard.module.css | Added new admin dashboard container/header/title styles. |
| packages/ui/src/components/admin/AdminDashboard.jsx | Added wrapper class + test id on the root dashboard container. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const getLabel = () => { | ||
| if (!selectedOption) return "Select Option"; | ||
| if (isObjectArray) { | ||
| const option = options.find((opt) => { | ||
| const val = valueKey ? opt[valueKey] : opt.value; | ||
| return val === selectedOption; | ||
| }); | ||
| return option | ||
| ? labelKey | ||
| ? option[labelKey] | ||
| : option.label | ||
| : selectedOption; | ||
| } | ||
| return selectedOption.toUpperCase(); |
There was a problem hiding this comment.
selectedOption is treated as a string (from the hidden ’s onChange), but the custom menu passes raw value from the option object (often a number). This causes getLabel()’s find comparison and the isSelected check to fail (e.g., EXPIRY_KEYS_OPTION values are numbers), so the trigger can display the raw value ("7") and the selected item won’t be highlighted. Normalize the types (e.g., compare via String(val) === String(selectedOption) and pass String(value) to setSelectedOption, or update the component API/PropTypes to consistently support numbers).
| <div | ||
| className={`${styles["dropdown-trigger"]} ${isOpen ? styles["is-open"] : ""}`} | ||
| onClick={() => setIsOpen(!isOpen)} | ||
| aria-haspopup="listbox" | ||
| aria-expanded={isOpen} | ||
| > | ||
| <span>{getLabel()}</span> | ||
| <ChevronDown size={16} className={styles["arrow-icon"]} /> | ||
| </div> |
There was a problem hiding this comment.
The trigger is a clickable <div> without keyboard support (no role="button", tabIndex, or Enter/Space key handling). With the native <select> visually hidden and non-interactive, this makes the dropdown effectively unusable for keyboard users. Use a <button type="button"> for the trigger (or add proper role/tabIndex + key handlers) and ensure focus styles are visible.
| {isOpen && ( | ||
| <div className={styles["dropdown-menu"]} role="listbox"> | ||
| {options?.map((option) => { | ||
| const value = isObjectArray | ||
| ? valueKey | ||
| ? option[valueKey] | ||
| : option.value | ||
| : option; | ||
| const label = isObjectArray | ||
| ? labelKey | ||
| ? option[labelKey] | ||
| : option.label | ||
| : option.toUpperCase(); | ||
| const isSelected = value === selectedOption; | ||
|
|
||
| return ( | ||
| <option key={value} className={styles["option"]} value={value}> | ||
| <div | ||
| key={value} | ||
| className={`${styles["dropdown-item"]} ${isSelected ? styles["selected"] : ""}`} | ||
| onClick={() => handleOptionClick(value)} | ||
| role="option" | ||
| aria-selected={isSelected} | ||
| > | ||
| {label} | ||
| </option> | ||
| </div> | ||
| ); |
There was a problem hiding this comment.
The options in the custom menu are rendered as <div> elements with onClick only. Even with role="option", they are not focusable and have no keyboard interaction, so users cannot navigate/select options via keyboard or assistive tech in a standard way. Consider implementing an accessible combobox/listbox pattern (roving tabIndex, arrow-key navigation, Enter/Escape handling) or render options as buttons within a properly managed listbox.
| @@ -27,37 +54,48 @@ | |||
| justify-content: flex-end; | |||
| } | |||
There was a problem hiding this comment.
CatalogItem.jsx still uses styles["created"] and styles["updated"], but those class selectors are no longer defined in CatalogItem.module.css. This will result in missing styling (and may render literal "undefined" class names in some string interpolations elsewhere). Reintroduce .created/.updated styles (and any responsive variants) or update the JSX to use the new/remaining class names.
| .catalog-footer-nav-btn { | ||
| padding: 8px 14px; | ||
| border: 1px solid var(--table-heading-bg); | ||
| border-radius: 8px; | ||
| background-color: var(--white); | ||
| display: flex; | ||
| gap: 8px; | ||
| align-items: center; | ||
| gap: 0.5rem; | ||
| padding: 0.5rem 1rem; | ||
| border: 1px solid var(--border-color); | ||
| border-radius: 0.5rem; | ||
| background: var(--white); | ||
| cursor: pointer; | ||
| font-weight: 500; | ||
| transition: background 0.2s; | ||
| } | ||
|
|
||
| .catalog-nav-left-arrow:after { | ||
| content: " Previous"; | ||
| color: var(--description); | ||
| } | ||
|
|
||
| .catalog-nav-right-arrow:before { | ||
| content: "Next "; | ||
| color: var(--description); | ||
| .catalog-footer-nav-btn:hover:not(:disabled) { | ||
| background: #f1f5f9; | ||
| } | ||
|
|
||
| .catalog-footer-nav-btn-disable { | ||
| cursor: not-allowed; | ||
| .catalog-footer-nav-btn:disabled { | ||
| opacity: 0.5; | ||
| cursor: not-allowed; | ||
| } | ||
|
|
||
| .cur-page { | ||
| font-weight: 700; | ||
| color: var(--black); | ||
| font-weight: bolder; | ||
| } | ||
|
|
||
| @media (max-width: 780px) { | ||
| .catalog-table-header { | ||
| display: none; | ||
| } | ||
| .catalog-search-modal { | ||
| background: var(--white); | ||
| border-radius: 0.75rem; | ||
| border: 1px solid var(--border-color); | ||
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); | ||
| padding: 1.5rem; | ||
| margin-top: 1rem; | ||
| } | ||
|
|
||
| .catalog-search { | ||
| flex-direction: column; | ||
| } | ||
| .catalog-search-modal-header { | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| margin-bottom: 1rem; | ||
| } | ||
|
|
There was a problem hiding this comment.
Several class selectors that Catalog.jsx relies on appear to have been removed from this module (e.g., catalog-search-modal-title, catalog-search-modal-cross, web-search-catalog-body, web-search-actions, search-modal-row, search-modal-img, search-modal-company-name, catalog-footer-nav-btn-disable, catalog-nav-left-arrow, catalog-nav-right-arrow). With CSS Modules, those usages will resolve to undefined, breaking styling and potentially producing stray "undefined" class names in the rendered DOM. Either restore these selectors or update Catalog.jsx to use the new class names introduced in this revamp.
| <div className={styles["admin-dashboard"]} data-testid="admin-dashboard"> | ||
| <div className={styles["page-header"]}> | ||
| <div className={styles["title-section"]}> |
There was a problem hiding this comment.
AdminDashboard.jsx applies styles["admin-dashboard"], but styles is passed in from Dashboard.jsx and Dashboard.module.css does not define admin-dashboard. As a result, this wrapper styling won’t apply. Import and use AdminDashboard.module.css inside AdminDashboard.jsx (and stop passing styles), or add the admin-dashboard class to the stylesheet that is actually being passed in.
| .admin-dashboard { | ||
| padding: 0 2rem 2rem 2rem; | ||
| max-width: 1280px; | ||
| margin: 0 auto; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 1rem; | ||
| } | ||
|
|
||
| .dashboard-header { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| margin-bottom: 0.5rem; | ||
| } | ||
|
|
||
| .dashboard-title { | ||
| font-size: 1.5rem; | ||
| font-weight: 700; | ||
| color: var(--black); | ||
| letter-spacing: -0.025em; | ||
| } | ||
|
|
||
| @media (max-width: 768px) { | ||
| .admin-dashboard { | ||
| padding: 1rem; | ||
| } | ||
| } |
There was a problem hiding this comment.
This new CSS module doesn’t appear to be imported anywhere in the codebase, so its styles won’t take effect and it adds dead code/maintenance overhead. Either import it from AdminDashboard.jsx (and update the component to use its class names consistently) or remove the file if the dashboard continues to use Dashboard.module.css via props.
| .admin-dashboard { | |
| padding: 0 2rem 2rem 2rem; | |
| max-width: 1280px; | |
| margin: 0 auto; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| } | |
| .dashboard-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-bottom: 0.5rem; | |
| } | |
| .dashboard-title { | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| color: var(--black); | |
| letter-spacing: -0.025em; | |
| } | |
| @media (max-width: 768px) { | |
| .admin-dashboard { | |
| padding: 1rem; | |
| } | |
| } | |
| /* AdminDashboard.module.css is intentionally left empty. | |
| * The admin dashboard currently uses styles from another module | |
| * (for example, Dashboard.module.css) and this file is kept only | |
| * to avoid dead, unused CSS rules. | |
| */ |
| [data-theme="dark"] .dropdown-trigger { | ||
| background-color: #1e293b; | ||
| border-color: #334155; | ||
| color: #f8fafc; | ||
| } | ||
|
|
||
| [data-theme="dark"] .dropdown-menu { | ||
| background-color: #1e293b; | ||
| border-color: #334155; | ||
| } |
There was a problem hiding this comment.
This module defines dark-theme styles for the new .dropdown-trigger/menu/items, but the file still contains a [data-theme="dark"] .dropdown { ... } rule later on (targeting the removed .dropdown select). Since .dropdown is no longer rendered, that selector is now dead and should be removed or updated to target the new trigger/menu classes to avoid confusion and keep dark theme styling consistent.
|



Description
closes #988
this PR updates the UI of the admin dashboard to make it consistent with the new design style
What type of PR is this? (Check all applicable)
Screenshots (if applicable)
Checklist