Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/.eslintrc-auto-import.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"Label": true,
"MaybeRef": true,
"MaybeRefOrGetter": true,
"MenuItem": true,
"PageState": true,
"ProjectColumn": true,
"ProjectFile": true,
Expand All @@ -58,6 +59,7 @@
"StatementSchema1": true,
"StatementSchemaMapping": true,
"TermsSchemaMapping": true,
"TieredMenu": true,
"TransformationFunction": true,
"TransformationParameter": true,
"TransformationRule": true,
Expand Down
7 changes: 7 additions & 0 deletions frontend/auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ declare global {
const ApiKey: typeof import('./src/core/plugins/api')['ApiKey']
const ApiPlugin: typeof import('./src/core/plugins/api')['ApiPlugin']
const EffectScope: typeof import('vue')['EffectScope']
const MenuItem: typeof import('primevue/menuitem')['MenuItem']
const ValidationMessages: typeof import('./src/shared/types/wikibase-schema')['ValidationMessages']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const api: typeof import('./src/core/plugins/api')['api']
Expand Down Expand Up @@ -359,9 +360,15 @@ declare global {
export type { FileUploadUploaderEvent } from 'primevue/fileupload'
import('primevue/fileupload')
// @ts-ignore
export type { MenuItem } from 'primevue/menuitem'
import('primevue/menuitem')
// @ts-ignore
export type { PageState } from 'primevue/paginator'
import('primevue/paginator')
// @ts-ignore
export type { TieredMenu } from 'primevue/tieredmenu'
import('primevue/tieredmenu')
// @ts-ignore
export type { UUID } from 'crypto'
import('crypto')
// @ts-ignore
Expand Down
2 changes: 2 additions & 0 deletions frontend/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ declare module 'vue' {
Chip: typeof import('primevue/chip')['default']
ClaimEditor: typeof import('./src/features/wikibase-schema/components/ClaimEditor.vue')['default']
Column: typeof import('primevue/column')['default']
ColumnHeaderMenu: typeof import('./src/features/data-processing/components/ColumnHeaderMenu.vue')['default']
ColumnPalette: typeof import('./src/features/data-processing/components/ColumnPalette.vue')['default']
ConfirmDialog: typeof import('primevue/confirmdialog')['default']
CreateProject: typeof import('./src/features/project-management/pages/CreateProject.vue')['default']
Expand Down Expand Up @@ -54,6 +55,7 @@ declare module 'vue' {
Tag: typeof import('primevue/tag')['default']
TermSection: typeof import('./src/features/wikibase-schema/components/TermSection.vue')['default']
TermsEditor: typeof import('./src/features/wikibase-schema/components/TermsEditor.vue')['default']
TieredMenu: typeof import('primevue/tieredmenu')['default']
Toast: typeof import('primevue/toast')['default']
ToggleSwitch: typeof import('primevue/toggleswitch')['default']
ValidationDisplay: typeof import('./src/features/wikibase-schema/components/ValidationDisplay.vue')['default']
Expand Down
2 changes: 2 additions & 0 deletions frontend/eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import js from '@eslint/js'
import prettierConfig from 'eslint-config-prettier'
import pluginVue from 'eslint-plugin-vue'
import { defineConfig } from 'eslint/config'
import globals from 'globals'
import tseslint from 'typescript-eslint'
import vueParser from 'vue-eslint-parser'
import autoImportGlobals from './.eslintrc-auto-import.json'
Expand Down Expand Up @@ -35,6 +36,7 @@ export default defineConfig(
extraFileExtensions: ['.vue'],
},
globals: {
...globals.browser,
...autoImportGlobals.globals,
},
},
Expand Down
108 changes: 108 additions & 0 deletions frontend/src/features/data-processing/components/ColumnHeaderMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<script setup lang="ts">
const props = defineProps<{
columnField: string
columnHeader: string
isPrimaryKey: boolean
}>()

const menu = ref()
const isOpen = ref(false)

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}`),
},
],
},
])
Comment on lines +11 to +84
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.

</script>

<template>
<div class="flex items-center">
<Button
v-if="!isPrimaryKey"
type="button"
icon="pi pi-chevron-down"
class="p-button-rounded p-button-text p-button-sm"
:aria-controls="`column-menu-${columnField}`"
aria-haspopup="true"
:aria-expanded="isOpen"
@click="(event) => menu.toggle(event)"
/>
<TieredMenu
:id="`column-menu-${columnField}`"
ref="menu"
:model="menuItems"
popup
@show="() => (isOpen = true)"
@hide="() => (isOpen = false)"
/>
</div>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ onUnmounted(() => clearProject())
v-if="col.pk"
class="pi pi-key text-blue-600"
/>
<ColumnHeaderMenu
v-if="!col.pk"
:column-field="col.field"
:column-header="col.header"
:is-primary-key="col.pk"
/>
<span>{{ col.header }}</span>
</div>
</template>
Expand Down
10 changes: 10 additions & 0 deletions frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,21 @@ export default defineConfig({
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/useconfirm',
imports: ['useConfirm'],
Expand Down