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
59 changes: 1 addition & 58 deletions .kiro/specs/wikibase-schema-editor/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,66 +200,9 @@
- Update validation to trigger on dragstart events
- _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7_

- [ ] 34. Implement autosave functionality in schema store
- [x] 34. Implement autosave functionality in schema store
- Write tests for immediate Pinia store updates on all schema changes
- Write tests for isDirty flag management and lastSyncedAt tracking
- Write tests for automatic store persistence without manual save operations
- Implement autosave mutations in schema store for all schema operations
- _Requirements: 10.1, 10.2, 10.8_

- [ ] 35. Remove save/cancel buttons from TermsEditor component
- Write tests for immediate term mapping updates without save buttons
- Write tests for automatic language selection persistence
- Remove save/cancel buttons from TermsEditor interface
- Update TermsEditor to use autosave store mutations
- _Requirements: 3.6, 3.7, 10.5_

- [ ] 36. Remove save/cancel buttons from StatementEditor component
- Write tests for immediate statement configuration updates without save buttons
- Write tests for automatic property selection and rank changes persistence
- Remove save/cancel buttons from StatementEditor interface
- Update StatementEditor to use autosave store mutations
- _Requirements: 4.7, 4.8, 5.6, 5.7, 10.5_

- [ ] 37. Remove save/cancel buttons from QualifiersEditor component
- Write tests for immediate qualifier updates without save buttons
- Write tests for automatic qualifier property and value mapping persistence
- Remove save/cancel buttons from QualifiersEditor interface
- Update QualifiersEditor to use autosave store mutations
- _Requirements: 6.6, 6.7, 10.5_

- [ ] 38. Remove save/cancel buttons from ReferencesEditor component
- Write tests for immediate reference updates without save buttons
- Write tests for automatic reference property and value mapping persistence
- Remove save/cancel buttons from ReferencesEditor interface
- Update ReferencesEditor to use autosave store mutations
- _Requirements: 7.6, 7.7, 10.5_

- [ ] 39. Ensure existing manual backend sync works with autosave store state
- Write tests to verify existing backend sync functionality works with autosave-enabled store
- Write tests to confirm manual "Save to Server" button continues to work as currently implemented
- Verify existing persistence layer correctly reads from autosave-updated Pinia store state
- Ensure no changes to existing manual backend synchronization behavior
- _Requirements: 10.3, 10.4_

- [ ] 40. Update drag-and-drop operations to use autosave
- Write tests for immediate store updates on column drop operations
- Write tests for automatic mapping persistence without confirmation dialogs
- Update all drop zone handlers to use autosave store mutations
- Remove any remaining save/cancel patterns from drag-and-drop workflows
- _Requirements: 10.1, 10.2, 10.5_

- [ ] 41. Update schema selection workflow for autosave
- Write tests for autosave enablement when loading existing schemas
- Write tests for autosave enablement when creating new schemas
- Update SchemaSelector to initialize autosave behavior on schema selection
- Ensure seamless transition from selection to autosave-enabled editor
- _Requirements: 9.4, 9.5, 9.8_

- [ ] 42. Update all existing test cases for autosave behavior
- Update TermsEditor tests to expect immediate store updates instead of save button interactions
- Update StatementEditor tests to expect automatic persistence instead of manual save operations
- Update QualifiersEditor and ReferencesEditor tests for autosave behavior
- Update drag-and-drop tests to expect immediate store persistence
- Update schema store tests to validate autosave mutations and dirty flag management
- _Requirements: 10.1, 10.2, 10.5, 10.8_
4 changes: 3 additions & 1 deletion frontend/.eslintrc-auto-import.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"SchemaCompletenessResult": true,
"SchemaDragDropContext": true,
"SchemaRequest": true,
"ShallowRef": true,
"Slot": true,
"Slots": true,
"StatementRank": true,
Expand Down Expand Up @@ -106,6 +107,7 @@
"getActivePinia": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"getCurrentWatcher": true,
"h": true,
"ignorableWatch": true,
"inject": true,
Expand All @@ -115,6 +117,7 @@
"isReactive": true,
"isReadonly": true,
"isRef": true,
"isShallow": true,
"makeDestructurable": true,
"mapActions": true,
"mapGetters": true,
Expand Down Expand Up @@ -218,7 +221,6 @@
"useClipboardItems": true,
"useCloned": true,
"useColorMode": true,
"useColumnConversion": true,
"useColumnDataTypeIndicators": true,
"useColumnGeneration": true,
"useConfirm": true,
Expand Down
10 changes: 5 additions & 5 deletions frontend/auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ declare global {
const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const getCurrentWatcher: typeof import('vue')['getCurrentWatcher']
const h: typeof import('vue')['h']
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
const inject: typeof import('vue')['inject']
Expand All @@ -54,6 +55,7 @@ declare global {
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const isShallow: typeof import('vue')['isShallow']
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
const mapActions: typeof import('pinia')['mapActions']
const mapGetters: typeof import('pinia')['mapGetters']
Expand Down Expand Up @@ -157,7 +159,6 @@ declare global {
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
const useCloned: typeof import('@vueuse/core')['useCloned']
const useColorMode: typeof import('@vueuse/core')['useColorMode']
const useColumnConversion: typeof import('./src/features/data-processing/composables/useColumnConversion')['useColumnConversion']
const useColumnDataTypeIndicators: typeof import('./src/features/data-processing/composables/useColumnDataTypeIndicators')['useColumnDataTypeIndicators']
const useColumnGeneration: typeof import('./src/features/data-processing/composables/useColumnGeneration')['useColumnGeneration']
const useConfirm: typeof import('primevue/useconfirm')['useConfirm']
Expand Down Expand Up @@ -217,7 +218,6 @@ declare global {
const useInterval: typeof import('@vueuse/core')['useInterval']
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
const useLanguageDropZone: typeof import('./src/features/data-processing/composables/useLanguageDropZone')['useLanguageDropZone']
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
const useLink: typeof import('vue-router')['useLink']
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
Expand Down Expand Up @@ -270,7 +270,6 @@ declare global {
const useSchemaApi: typeof import('./src/features/wikibase-schema/composables/useSchemaApi')['useSchemaApi']
const useSchemaBuilder: typeof import('./src/features/wikibase-schema/composables/useSchemaBuilder')['useSchemaBuilder']
const useSchemaCompletenessValidation: typeof import('./src/features/wikibase-schema/composables/useSchemaCompletenessValidation')['useSchemaCompletenessValidation']
const useSchemaDropZone: typeof import('./src/features/wikibase-schema/composables/useSchemaDropZone')['useSchemaDropZone']
const useSchemaPersistence: typeof import('./src/features/wikibase-schema/composables/useSchemaPersistence')['useSchemaPersistence']
const useSchemaSelection: typeof import('./src/features/wikibase-schema/composables/useSchemaSelection')['useSchemaSelection']
const useSchemaStore: typeof import('./src/features/wikibase-schema/stores/schema.store')['useSchemaStore']
Expand Down Expand Up @@ -356,7 +355,7 @@ declare global {
// for type re-export
declare global {
// @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
// @ts-ignore
export type { FileUploadUploaderEvent } from 'primevue/fileupload'
Expand Down Expand Up @@ -443,6 +442,7 @@ declare module 'vue' {
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getCurrentWatcher: UnwrapRef<typeof import('vue')['getCurrentWatcher']>
readonly h: UnwrapRef<typeof import('vue')['h']>
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
readonly inject: UnwrapRef<typeof import('vue')['inject']>
Expand All @@ -452,6 +452,7 @@ declare module 'vue' {
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly isShallow: UnwrapRef<typeof import('vue')['isShallow']>
readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']>
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
Expand Down Expand Up @@ -555,7 +556,6 @@ declare module 'vue' {
readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']>
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
readonly useColumnConversion: UnwrapRef<typeof import('./src/features/data-processing/composables/useColumnConversion')['useColumnConversion']>
readonly useColumnDataTypeIndicators: UnwrapRef<typeof import('./src/features/data-processing/composables/useColumnDataTypeIndicators')['useColumnDataTypeIndicators']>
readonly useColumnGeneration: UnwrapRef<typeof import('./src/features/data-processing/composables/useColumnGeneration')['useColumnGeneration']>
readonly useConfirm: UnwrapRef<typeof import('primevue/useconfirm')['useConfirm']>
Expand Down
8 changes: 4 additions & 4 deletions frontend/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ declare module 'vue' {
Button: typeof import('primevue/button')['default']
Card: typeof import('primevue/card')['default']
Chip: typeof import('primevue/chip')['default']
ClaimEditor: typeof import('./src/features/wikibase-schema/components/ClaimEditor.vue')['default']
Column: typeof import('primevue/column')['default']
ColumnPalette: typeof import('./src/features/data-processing/components/ColumnPalette.vue')['default']
ConfirmDialog: typeof import('primevue/confirmdialog')['default']
Expand All @@ -20,13 +21,10 @@ declare module 'vue' {
DataTable: typeof import('primevue/datatable')['default']
DataTabPanel: typeof import('./src/features/data-processing/components/DataTabPanel.vue')['default']
DefaultLayout: typeof import('./src/core/layouts/DefaultLayout.vue')['default']
Dialog: typeof import('primevue/dialog')['default']
Dropdown: typeof import('primevue/dropdown')['default']
FileUpload: typeof import('primevue/fileupload')['default']
Header: typeof import('./src/shared/components/Header.vue')['default']
InputText: typeof import('primevue/inputtext')['default']
ItemEditor: typeof import('./src/features/wikibase-schema/components/ItemEditor.vue')['default']
LanguageDropZone: typeof import('./src/features/data-processing/components/LanguageDropZone.vue')['default']
MainContent: typeof import('./src/shared/components/MainContent.vue')['default']
OpenProject: typeof import('./src/features/project-management/pages/OpenProject.vue')['default']
Paginator: typeof import('primevue/paginator')['default']
Expand All @@ -43,10 +41,12 @@ declare module 'vue' {
SchemaTabPanel: typeof import('./src/features/wikibase-schema/components/SchemaTabPanel.vue')['default']
Select: typeof import('primevue/select')['default']
Sidebar: typeof import('./src/shared/components/Sidebar.vue')['default']
SingleQualifierEditor: typeof import('./src/features/wikibase-schema/components/SingleQualifierEditor.vue')['default']
SingleReferenceEditor: typeof import('./src/features/wikibase-schema/components/SingleReferenceEditor.vue')['default']
StatementConfigEditor: typeof import('./src/features/wikibase-schema/components/StatementConfigEditor.vue')['default']
StatementEditor: typeof import('./src/features/wikibase-schema/components/StatementEditor.vue')['default']
StatementManager: typeof import('./src/features/wikibase-schema/components/StatementManager.vue')['default']
StatementPreview: typeof import('./src/features/wikibase-schema/components/StatementPreview.vue')['default']
StatementsEditor: typeof import('./src/features/wikibase-schema/components/StatementsEditor.vue')['default']
Tab: typeof import('primevue/tab')['default']
TabList: typeof import('primevue/tablist')['default']
TabPanel: typeof import('primevue/tabpanel')['default']
Expand Down
115 changes: 53 additions & 62 deletions frontend/src/features/data-processing/components/ColumnPalette.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
const projectStore = useProjectStore()
const dragDropStore = useDragDropStore()

const { convertProjectColumnsToColumnInfo } = useColumnConversion()
const { formatDataTypeDisplayName, generateColumnTooltip, getDataTypeIcon, getDataTypeSeverity } =
useColumnDataTypeIndicators()

Expand All @@ -12,10 +11,6 @@ const { triggerDragStartValidation } = useValidation()
// Sample data visibility state (hidden by default)
const showSampleData = ref(false)

const dataColumns = computed(() => {
return convertProjectColumnsToColumnInfo(projectStore.columns, projectStore.data)
})

const handleDragStart = (event: DragEvent, dataCol: ColumnInfo) => {
if (!event.dataTransfer) return

Expand Down Expand Up @@ -50,7 +45,7 @@ const formatSampleValues = (sampleValues: string[]) => {
</script>

<template>
<div class="column-palette h-full p-4">
<div class="column-palette p-4 bg-slate-50 border border-slate-200 rounded-lg shadow-sm">
<!-- Header with toggle switch -->
<div class="flex justify-between items-center mb-4">
<div>
Expand All @@ -72,66 +67,62 @@ const formatSampleValues = (sampleValues: string[]) => {
</div>

<!-- Content Area -->
<div>
<!-- Column chips -->
<div class="flex flex-wrap gap-3">
<!-- Column chips -->
<div class="flex gap-3">
<div
v-for="col in projectStore.columnsForSchema"
:key="col.name"
:data-testid="'column-chip'"
:class="{
'opacity-50': dragDropStore.isDragging && dragDropStore.draggedColumn?.name === col.name,
'cursor-grab': !dragDropStore.isDragging,
'cursor-grabbing':
dragDropStore.isDragging && dragDropStore.draggedColumn?.name === col.name,
}"
class="column-chip transition-opacity duration-200 hover:-translate-y-0.5 active:translate-y-0"
draggable="true"
@dragstart="handleDragStart($event, col)"
@dragend="handleDragEnd"
>
<div
v-for="col in dataColumns"
:key="col.name"
:data-testid="'column-chip'"
:class="{
'opacity-50':
dragDropStore.isDragging && dragDropStore.draggedColumn?.name === col.name,
'cursor-grab': !dragDropStore.isDragging,
'cursor-grabbing':
dragDropStore.isDragging && dragDropStore.draggedColumn?.name === col.name,
}"
class="column-chip transition-opacity duration-200 hover:-translate-y-0.5 active:translate-y-0"
draggable="true"
@dragstart="handleDragStart($event, col)"
@dragend="handleDragEnd"
class="bg-white border border-slate-300 rounded-lg p-3 shadow-sm hover:shadow-md hover:border-blue-300 transition-all duration-200"
:title="generateColumnTooltip(col)"
>
<div class="flex items-center gap-2">
<i
:class="getDataTypeIcon(col.dataType)"
class="text-surface-600 text-xs"
:title="`${formatDataTypeDisplayName(col.dataType)} column`"
/>
<span class="font-small text-surface-900">{{ col.name }}</span>
<Tag
:value="formatDataTypeDisplayName(col.dataType)"
:severity="getDataTypeSeverity(col.dataType)"
class="text-xs"
:title="`Database type: ${col.dataType}`"
/>
<i
v-if="col.nullable"
class="pi pi-question-circle text-surface-400 text-xs"
title="This column allows null values"
/>
</div>

<div
v-if="showSampleData && col.sampleValues && col.sampleValues.length > 0"
class="text-xs text-surface-600"
data-testid="sample-values"
:title="`Sample values: ${col.sampleValues.slice(0, 5).join(', ')}${col.sampleValues.length > 5 ? '...' : ''}`"
>
{{ formatSampleValues(col.sampleValues) }}
</div>

<div
class="bg-white border border-surface-300 rounded-lg p-3 shadow-sm hover:shadow-md transition-shadow"
:title="generateColumnTooltip(col)"
v-if="col.uniqueCount !== undefined"
class="text-xs text-surface-500 mt-1"
:title="`This column has ${col.uniqueCount.toLocaleString()} unique values`"
>
<div class="flex items-center gap-2 mb-2">
<i
:class="getDataTypeIcon(col.dataType)"
class="text-surface-600 text-sm"
:title="`${formatDataTypeDisplayName(col.dataType)} column`"
/>
<span class="font-medium text-surface-900">{{ col.name }}</span>
<Chip
:label="formatDataTypeDisplayName(col.dataType)"
size="small"
:severity="getDataTypeSeverity(col.dataType)"
class="text-xs"
:title="`Database type: ${col.dataType}`"
/>
<i
v-if="col.nullable"
class="pi pi-question-circle text-surface-400 text-xs"
title="This column allows null values"
/>
</div>

<div
v-if="showSampleData && col.sampleValues && col.sampleValues.length > 0"
class="text-xs text-surface-600"
data-testid="sample-values"
:title="`Sample values: ${col.sampleValues.slice(0, 5).join(', ')}${col.sampleValues.length > 5 ? '...' : ''}`"
>
{{ formatSampleValues(col.sampleValues) }}
</div>

<div
v-if="col.uniqueCount !== undefined"
class="text-xs text-surface-500 mt-1"
:title="`This column has ${col.uniqueCount.toLocaleString()} unique values`"
>
{{ col.uniqueCount.toLocaleString() }} unique values
</div>
{{ col.uniqueCount.toLocaleString() }} unique values
</div>
</div>
</div>
Expand Down
Loading
Loading