Skip to content

Conversation

@DaxServer
Copy link
Owner

The changes introduce a new ColumnHeaderMenu component that provides a dropdown menu with various options for the column header. This includes:

  • Sorting the column in ascending or descending order
  • Filtering the column
  • Grouping by the column
  • Applying common text transformations to the column cells (trim whitespace, collapse consecutive whitespace, convert to uppercase)

The component is added to the DataTabPanel component, where it is displayed next to the column header if the column is not a primary key. This provides users with more control and flexibility over the data processing features.

The changes introduce a new `ColumnHeaderMenu` component that provides a dropdown menu with various options for the column header. This includes:

- Sorting the column in ascending or descending order
- Filtering the column
- Grouping by the column
- Applying common text transformations to the column cells (trim whitespace, collapse consecutive whitespace, convert to uppercase)

The component is added to the `DataTabPanel` component, where it is displayed next to the column header if the column is not a primary key. This provides users with more control and flexibility over the data processing features.
@coderabbitai
Copy link

coderabbitai bot commented Sep 28, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a contextual column header menu for non-primary-key columns in the Data tab. Access via a chevron button to open a popup menu with options: Sort (Ascending/Descending), Filter, Group By, and Edit Cells (Trim, Collapse whitespace, Uppercase/Lowercase/Titlecase, Replace). Includes accessibility attributes.
  • Chores

    • Updated frontend tooling and auto-imports to support the new menu components and types, including expanded globals and configuration adjustments.

Walkthrough

Adds a new ColumnHeaderMenu Vue component using PrimeVue TieredMenu, wires it into DataTabPanel column headers for non-PK columns, and updates type declarations and auto-import/ESLint/Vite configs to expose PrimeVue MenuItem and TieredMenu globally.

Changes

Cohort / File(s) Summary
ESLint and Auto-Import Config
frontend/.eslintrc-auto-import.json, frontend/vite.config.ts
Adds globals/auto-imports for MenuItem and TieredMenu to support PrimeVue menu usage.
Type Declarations
frontend/auto-imports.d.ts
Declares global const MenuItem and re-exports types MenuItem and TieredMenu.
Global Components Typings
frontend/components.d.ts
Registers GlobalComponents.ColumnHeaderMenu and GlobalComponents.TieredMenu.
ESLint Config Merge
frontend/eslint.config.ts
Merges globals.browser into ESLint config globals.
New UI Component
frontend/src/features/data-processing/components/ColumnHeaderMenu.vue
Introduces a TieredMenu-based column header context menu with nested actions (sorting, filtering, grouping, editing transforms); uses popup show/hide and aria bindings.
Integration into DataTabPanel
frontend/src/features/data-processing/components/DataTabPanel.vue
Renders ColumnHeaderMenu for non-primary-key columns in header templates; primary-key columns continue to show PK icon.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant DataTabPanel
    participant ColumnHeaderMenu
    participant TieredMenu

    User->>DataTabPanel: View table headers
    DataTabPanel->>ColumnHeaderMenu: Render for non-PK columns
    User->>ColumnHeaderMenu: Click chevron button
    ColumnHeaderMenu->>TieredMenu: toggle(event) [popup]
    TieredMenu-->>ColumnHeaderMenu: onShow → isOpen=true
    User->>TieredMenu: Select menu item (e.g., Sort Asc)
    TieredMenu-->>ColumnHeaderMenu: command callback
    ColumnHeaderMenu->>ColumnHeaderMenu: Handle action (console.log)
    User->>TieredMenu: Click outside / close
    TieredMenu-->>ColumnHeaderMenu: onHide → isOpen=false
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly and accurately summarizes the primary change by highlighting the addition of a column header menu within the data-processing domain, matching the new ColumnHeaderMenu component and its integration, and follows conventional commit style.
Description Check ✅ Passed The description clearly outlines the introduction and purpose of the ColumnHeaderMenu component, enumerates its features and explains how it is integrated into DataTabPanel, directly reflecting the changes in the pull request.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-column-header-menu

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/vite.config.ts (1)

33-51: Fix TieredMenu auto-import to target the default export

PrimeVue’s 'primevue/tieredmenu' module only exposes a default export. With the current AutoImport entry, unplugin-auto-import generates import type { TieredMenu } from 'primevue/tieredmenu', which immediately fails with TS2614 because that named export does not exist. Please switch the config to alias the default export (or drop the auto-import and perform an explicit default import where it’s used) so the build can pass.

Apply this diff to alias the default export correctly:

         {
           from: 'primevue/fileupload',
           imports: ['FileUploadUploaderEvent'],
           type: true,
         },
         {
           from: 'primevue/menuitem',
           imports: ['MenuItem'],
           type: true,
         },
         {
           from: 'primevue/paginator',
           imports: ['PageState'],
           type: true,
         },
         {
-          from: 'primevue/tieredmenu',
-          imports: ['TieredMenu'],
-          type: true,
+          from: 'primevue/tieredmenu',
+          imports: [{ name: 'default', as: 'TieredMenu' }],
+          type: true,
         },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cff016f and dad34a5.

📒 Files selected for processing (7)
  • frontend/.eslintrc-auto-import.json (2 hunks)
  • frontend/auto-imports.d.ts (2 hunks)
  • frontend/components.d.ts (2 hunks)
  • frontend/eslint.config.ts (2 hunks)
  • frontend/src/features/data-processing/components/ColumnHeaderMenu.vue (1 hunks)
  • frontend/src/features/data-processing/components/DataTabPanel.vue (1 hunks)
  • frontend/vite.config.ts (1 hunks)

Comment on lines +11 to +84
const menuItems = ref<MenuItem[]>([
{
label: 'Sort',
icon: 'pi pi-sort-amount-down',
items: [
{
label: 'Ascending',
icon: 'pi pi-sort-amount-up',
command: () => console.log(`Sort ${props.columnHeader} Ascending`),
},
{
label: 'Descending',
icon: 'pi pi-sort-amount-down',
command: () => console.log(`Sort ${props.columnHeader} Descending`),
},
],
},
{
separator: true,
},
{
label: 'Filter',
icon: 'pi pi-filter',
command: () => console.log(`Filter ${props.columnHeader}`),
},
{
label: 'Group By',
icon: 'pi pi-objects-column',
command: () => console.log(`Group By ${props.columnHeader}`),
},
{
separator: true,
},
{
label: 'Edit cells',
icon: 'pi pi-code',
items: [
{
label: 'Common transforms',
icon: 'pi pi-text',
items: [
{
label: 'Trim leading and trailing whitespace',
command: () => console.log(`Trim whitespace in ${props.columnHeader}`),
},
{
label: 'Collapse consecutive whitespace',
command: () => console.log(`Collapse consecutive whitespace in ${props.columnHeader}`),
},
{
separator: true,
},
{
label: 'To uppercase',
command: () => console.log(`Transform ${props.columnHeader} to Uppercase`),
},
{
label: 'To lowercase',
command: () => console.log(`Transform ${props.columnHeader} to Lowercase`),
},
{
label: 'To titlecase',
command: () => console.log(`Transform ${props.columnHeader} to Title Case`),
},
],
},
{
label: 'Replace',
icon: 'pi pi-code',
command: () => console.log(`Replace in ${props.columnHeader}`),
},
],
},
])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Menu commands need to trigger real column actions.

All menu items currently just console.log, so the dropdown never actually sorts, filters, groups, or transforms the column. From a user perspective the feature is broken. Please emit the proper events (or invoke the existing handlers) so the menu performs the promised operations instead of logging.

+const emit = defineEmits<{
+  (e: 'sort', payload: { columnField: string; direction: 'asc' | 'desc' }): void
+  (e: 'filter', payload: { columnField: string }): void
+  (e: 'group', payload: { columnField: string }): void
+  (e: 'transform', payload: { columnField: string; operation: 'trim' | 'collapse-whitespace' | 'uppercase' | 'lowercase' | 'titlecase' }): void
+  (e: 'replace', payload: { columnField: string }): void
+}>()
+
 const menuItems = ref<MenuItem[]>([
   {
     label: 'Sort',
     icon: 'pi pi-sort-amount-down',
     items: [
       {
         label: 'Ascending',
         icon: 'pi pi-sort-amount-up',
-        command: () => console.log(`Sort ${props.columnHeader} Ascending`),
+        command: () => emit('sort', { columnField: props.columnField, direction: 'asc' }),
       },
       {
         label: 'Descending',
         icon: 'pi pi-sort-amount-down',
-        command: () => console.log(`Sort ${props.columnHeader} Descending`),
+        command: () => emit('sort', { columnField: props.columnField, direction: 'desc' }),
       },
     ],
   },
   {
     separator: true,
   },
   {
     label: 'Filter',
     icon: 'pi pi-filter',
-    command: () => console.log(`Filter ${props.columnHeader}`),
+    command: () => emit('filter', { columnField: props.columnField }),
   },
   {
     label: 'Group By',
     icon: 'pi pi-objects-column',
-    command: () => console.log(`Group By ${props.columnHeader}`),
+    command: () => emit('group', { columnField: props.columnField }),
   },
   {
     separator: true,
   },
   {
     label: 'Edit cells',
     icon: 'pi pi-code',
     items: [
       {
         label: 'Common transforms',
         icon: 'pi pi-text',
         items: [
           {
             label: 'Trim leading and trailing whitespace',
-            command: () => console.log(`Trim whitespace in ${props.columnHeader}`),
+            command: () => emit('transform', { columnField: props.columnField, operation: 'trim' }),
           },
           {
             label: 'Collapse consecutive whitespace',
-            command: () => console.log(`Collapse consecutive whitespace in ${props.columnHeader}`),
+            command: () => emit('transform', { columnField: props.columnField, operation: 'collapse-whitespace' }),
           },
           {
             separator: true,
           },
           {
             label: 'To uppercase',
-            command: () => console.log(`Transform ${props.columnHeader} to Uppercase`),
+            command: () => emit('transform', { columnField: props.columnField, operation: 'uppercase' }),
           },
           {
             label: 'To lowercase',
-            command: () => console.log(`Transform ${props.columnHeader} to Lowercase`),
+            command: () => emit('transform', { columnField: props.columnField, operation: 'lowercase' }),
           },
           {
             label: 'To titlecase',
-            command: () => console.log(`Transform ${props.columnHeader} to Title Case`),
+            command: () => emit('transform', { columnField: props.columnField, operation: 'titlecase' }),
           },
         ],
       },
       {
         label: 'Replace',
         icon: 'pi pi-code',
-        command: () => console.log(`Replace in ${props.columnHeader}`),
+        command: () => emit('replace', { columnField: props.columnField }),
       },
     ],
   },
 ])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const menuItems = ref<MenuItem[]>([
{
label: 'Sort',
icon: 'pi pi-sort-amount-down',
items: [
{
label: 'Ascending',
icon: 'pi pi-sort-amount-up',
command: () => console.log(`Sort ${props.columnHeader} Ascending`),
},
{
label: 'Descending',
icon: 'pi pi-sort-amount-down',
command: () => console.log(`Sort ${props.columnHeader} Descending`),
},
],
},
{
separator: true,
},
{
label: 'Filter',
icon: 'pi pi-filter',
command: () => console.log(`Filter ${props.columnHeader}`),
},
{
label: 'Group By',
icon: 'pi pi-objects-column',
command: () => console.log(`Group By ${props.columnHeader}`),
},
{
separator: true,
},
{
label: 'Edit cells',
icon: 'pi pi-code',
items: [
{
label: 'Common transforms',
icon: 'pi pi-text',
items: [
{
label: 'Trim leading and trailing whitespace',
command: () => console.log(`Trim whitespace in ${props.columnHeader}`),
},
{
label: 'Collapse consecutive whitespace',
command: () => console.log(`Collapse consecutive whitespace in ${props.columnHeader}`),
},
{
separator: true,
},
{
label: 'To uppercase',
command: () => console.log(`Transform ${props.columnHeader} to Uppercase`),
},
{
label: 'To lowercase',
command: () => console.log(`Transform ${props.columnHeader} to Lowercase`),
},
{
label: 'To titlecase',
command: () => console.log(`Transform ${props.columnHeader} to Title Case`),
},
],
},
{
label: 'Replace',
icon: 'pi pi-code',
command: () => console.log(`Replace in ${props.columnHeader}`),
},
],
},
])
const emit = defineEmits<{
(e: 'sort', payload: { columnField: string; direction: 'asc' | 'desc' }): void
(e: 'filter', payload: { columnField: string }): void
(e: 'group', payload: { columnField: string }): void
(e: 'transform', payload: { columnField: string; operation: 'trim' | 'collapse-whitespace' | 'uppercase' | 'lowercase' | 'titlecase' }): void
(e: 'replace', payload: { columnField: string }): void
}>()
const menuItems = ref<MenuItem[]>([
{
label: 'Sort',
icon: 'pi pi-sort-amount-down',
items: [
{
label: 'Ascending',
icon: 'pi pi-sort-amount-up',
command: () => emit('sort', { columnField: props.columnField, direction: 'asc' }),
},
{
label: 'Descending',
icon: 'pi pi-sort-amount-down',
command: () => emit('sort', { columnField: props.columnField, direction: 'desc' }),
},
],
},
{
separator: true,
},
{
label: 'Filter',
icon: 'pi pi-filter',
command: () => emit('filter', { columnField: props.columnField }),
},
{
label: 'Group By',
icon: 'pi pi-objects-column',
command: () => emit('group', { columnField: props.columnField }),
},
{
separator: true,
},
{
label: 'Edit cells',
icon: 'pi pi-code',
items: [
{
label: 'Common transforms',
icon: 'pi pi-text',
items: [
{
label: 'Trim leading and trailing whitespace',
command: () => emit('transform', { columnField: props.columnField, operation: 'trim' }),
},
{
label: 'Collapse consecutive whitespace',
command: () => emit('transform', { columnField: props.columnField, operation: 'collapse-whitespace' }),
},
{
separator: true,
},
{
label: 'To uppercase',
command: () => emit('transform', { columnField: props.columnField, operation: 'uppercase' }),
},
{
label: 'To lowercase',
command: () => emit('transform', { columnField: props.columnField, operation: 'lowercase' }),
},
{
label: 'To titlecase',
command: () => emit('transform', { columnField: props.columnField, operation: 'titlecase' }),
},
],
},
{
label: 'Replace',
icon: 'pi pi-code',
command: () => emit('replace', { columnField: props.columnField }),
},
],
},
])
🤖 Prompt for AI Agents
In frontend/src/features/data-processing/components/ColumnHeaderMenu.vue around
lines 11-84, the menu item commands only console.log and must instead trigger
real actions: replace each console.log callback with calls that emit the
appropriate events (e.g. emit('sort', { column: props.columnHeader, direction:
'asc'|'desc' }), emit('filter', { column: props.columnHeader }), emit('group', {
column: props.columnHeader }), emit('transform', { column: props.columnHeader,
type: 'trim'|'collapseWhitespace'|'uppercase'|'lowercase'|'titlecase' }),
emit('replace', { column: props.columnHeader })) or, if the parent handlers were
passed via props, call those handler props (e.g.
props.onSort(props.columnHeader, 'asc') etc.); ensure defineEmits/emit or the
prop handler names are used consistently and include descriptive payloads so the
parent/consumer can perform the actual sort/filter/group/transform/replace
operations.

@DaxServer DaxServer merged commit 47d67c3 into main Sep 28, 2025
6 checks passed
@DaxServer DaxServer deleted the add-column-header-menu branch September 28, 2025 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants