-
Notifications
You must be signed in to change notification settings - Fork 0
feat(data-processing): add column header menu #167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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.
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
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. Comment |
There was a problem hiding this 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 exportPrimeVue’s
'primevue/tieredmenu'module only exposes a default export. With the current AutoImport entry, unplugin-auto-import generatesimport type { TieredMenu } from 'primevue/tieredmenu', which immediately fails withTS2614because 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
📒 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)
| 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}`), | ||
| }, | ||
| ], | ||
| }, | ||
| ]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
The changes introduce a new
ColumnHeaderMenucomponent that provides a dropdown menu with various options for the column header. This includes:The component is added to the
DataTabPanelcomponent, 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.