Skip to content

Commit c77ffbb

Browse files
authored
refactor(wikibase-schema-editor): harmonize component structure (#129)
1 parent baec06f commit c77ffbb

23 files changed

Lines changed: 1807 additions & 1938 deletions

.kiro/specs/wikibase-schema-editor/tasks.md

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -200,66 +200,9 @@
200200
- Update validation to trigger on dragstart events
201201
- _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7_
202202

203-
- [ ] 34. Implement autosave functionality in schema store
203+
- [x] 34. Implement autosave functionality in schema store
204204
- Write tests for immediate Pinia store updates on all schema changes
205205
- Write tests for isDirty flag management and lastSyncedAt tracking
206206
- Write tests for automatic store persistence without manual save operations
207207
- Implement autosave mutations in schema store for all schema operations
208208
- _Requirements: 10.1, 10.2, 10.8_
209-
210-
- [ ] 35. Remove save/cancel buttons from TermsEditor component
211-
- Write tests for immediate term mapping updates without save buttons
212-
- Write tests for automatic language selection persistence
213-
- Remove save/cancel buttons from TermsEditor interface
214-
- Update TermsEditor to use autosave store mutations
215-
- _Requirements: 3.6, 3.7, 10.5_
216-
217-
- [ ] 36. Remove save/cancel buttons from StatementEditor component
218-
- Write tests for immediate statement configuration updates without save buttons
219-
- Write tests for automatic property selection and rank changes persistence
220-
- Remove save/cancel buttons from StatementEditor interface
221-
- Update StatementEditor to use autosave store mutations
222-
- _Requirements: 4.7, 4.8, 5.6, 5.7, 10.5_
223-
224-
- [ ] 37. Remove save/cancel buttons from QualifiersEditor component
225-
- Write tests for immediate qualifier updates without save buttons
226-
- Write tests for automatic qualifier property and value mapping persistence
227-
- Remove save/cancel buttons from QualifiersEditor interface
228-
- Update QualifiersEditor to use autosave store mutations
229-
- _Requirements: 6.6, 6.7, 10.5_
230-
231-
- [ ] 38. Remove save/cancel buttons from ReferencesEditor component
232-
- Write tests for immediate reference updates without save buttons
233-
- Write tests for automatic reference property and value mapping persistence
234-
- Remove save/cancel buttons from ReferencesEditor interface
235-
- Update ReferencesEditor to use autosave store mutations
236-
- _Requirements: 7.6, 7.7, 10.5_
237-
238-
- [ ] 39. Ensure existing manual backend sync works with autosave store state
239-
- Write tests to verify existing backend sync functionality works with autosave-enabled store
240-
- Write tests to confirm manual "Save to Server" button continues to work as currently implemented
241-
- Verify existing persistence layer correctly reads from autosave-updated Pinia store state
242-
- Ensure no changes to existing manual backend synchronization behavior
243-
- _Requirements: 10.3, 10.4_
244-
245-
- [ ] 40. Update drag-and-drop operations to use autosave
246-
- Write tests for immediate store updates on column drop operations
247-
- Write tests for automatic mapping persistence without confirmation dialogs
248-
- Update all drop zone handlers to use autosave store mutations
249-
- Remove any remaining save/cancel patterns from drag-and-drop workflows
250-
- _Requirements: 10.1, 10.2, 10.5_
251-
252-
- [ ] 41. Update schema selection workflow for autosave
253-
- Write tests for autosave enablement when loading existing schemas
254-
- Write tests for autosave enablement when creating new schemas
255-
- Update SchemaSelector to initialize autosave behavior on schema selection
256-
- Ensure seamless transition from selection to autosave-enabled editor
257-
- _Requirements: 9.4, 9.5, 9.8_
258-
259-
- [ ] 42. Update all existing test cases for autosave behavior
260-
- Update TermsEditor tests to expect immediate store updates instead of save button interactions
261-
- Update StatementEditor tests to expect automatic persistence instead of manual save operations
262-
- Update QualifiersEditor and ReferencesEditor tests for autosave behavior
263-
- Update drag-and-drop tests to expect immediate store persistence
264-
- Update schema store tests to validate autosave mutations and dirty flag management
265-
- _Requirements: 10.1, 10.2, 10.5, 10.8_

frontend/.eslintrc-auto-import.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"SchemaCompletenessResult": true,
5050
"SchemaDragDropContext": true,
5151
"SchemaRequest": true,
52+
"ShallowRef": true,
5253
"Slot": true,
5354
"Slots": true,
5455
"StatementRank": true,
@@ -106,6 +107,7 @@
106107
"getActivePinia": true,
107108
"getCurrentInstance": true,
108109
"getCurrentScope": true,
110+
"getCurrentWatcher": true,
109111
"h": true,
110112
"ignorableWatch": true,
111113
"inject": true,
@@ -115,6 +117,7 @@
115117
"isReactive": true,
116118
"isReadonly": true,
117119
"isRef": true,
120+
"isShallow": true,
118121
"makeDestructurable": true,
119122
"mapActions": true,
120123
"mapGetters": true,
@@ -218,7 +221,6 @@
218221
"useClipboardItems": true,
219222
"useCloned": true,
220223
"useColorMode": true,
221-
"useColumnConversion": true,
222224
"useColumnDataTypeIndicators": true,
223225
"useColumnGeneration": true,
224226
"useConfirm": true,

frontend/auto-imports.d.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ declare global {
4545
const getActivePinia: typeof import('pinia')['getActivePinia']
4646
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
4747
const getCurrentScope: typeof import('vue')['getCurrentScope']
48+
const getCurrentWatcher: typeof import('vue')['getCurrentWatcher']
4849
const h: typeof import('vue')['h']
4950
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
5051
const inject: typeof import('vue')['inject']
@@ -54,6 +55,7 @@ declare global {
5455
const isReactive: typeof import('vue')['isReactive']
5556
const isReadonly: typeof import('vue')['isReadonly']
5657
const isRef: typeof import('vue')['isRef']
58+
const isShallow: typeof import('vue')['isShallow']
5759
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
5860
const mapActions: typeof import('pinia')['mapActions']
5961
const mapGetters: typeof import('pinia')['mapGetters']
@@ -157,7 +159,6 @@ declare global {
157159
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
158160
const useCloned: typeof import('@vueuse/core')['useCloned']
159161
const useColorMode: typeof import('@vueuse/core')['useColorMode']
160-
const useColumnConversion: typeof import('./src/features/data-processing/composables/useColumnConversion')['useColumnConversion']
161162
const useColumnDataTypeIndicators: typeof import('./src/features/data-processing/composables/useColumnDataTypeIndicators')['useColumnDataTypeIndicators']
162163
const useColumnGeneration: typeof import('./src/features/data-processing/composables/useColumnGeneration')['useColumnGeneration']
163164
const useConfirm: typeof import('primevue/useconfirm')['useConfirm']
@@ -217,7 +218,6 @@ declare global {
217218
const useInterval: typeof import('@vueuse/core')['useInterval']
218219
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
219220
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
220-
const useLanguageDropZone: typeof import('./src/features/data-processing/composables/useLanguageDropZone')['useLanguageDropZone']
221221
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
222222
const useLink: typeof import('vue-router')['useLink']
223223
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
@@ -270,7 +270,6 @@ declare global {
270270
const useSchemaApi: typeof import('./src/features/wikibase-schema/composables/useSchemaApi')['useSchemaApi']
271271
const useSchemaBuilder: typeof import('./src/features/wikibase-schema/composables/useSchemaBuilder')['useSchemaBuilder']
272272
const useSchemaCompletenessValidation: typeof import('./src/features/wikibase-schema/composables/useSchemaCompletenessValidation')['useSchemaCompletenessValidation']
273-
const useSchemaDropZone: typeof import('./src/features/wikibase-schema/composables/useSchemaDropZone')['useSchemaDropZone']
274273
const useSchemaPersistence: typeof import('./src/features/wikibase-schema/composables/useSchemaPersistence')['useSchemaPersistence']
275274
const useSchemaSelection: typeof import('./src/features/wikibase-schema/composables/useSchemaSelection')['useSchemaSelection']
276275
const useSchemaStore: typeof import('./src/features/wikibase-schema/stores/schema.store')['useSchemaStore']
@@ -356,7 +355,7 @@ declare global {
356355
// for type re-export
357356
declare global {
358357
// @ts-ignore
359-
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
358+
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
360359
import('vue')
361360
// @ts-ignore
362361
export type { FileUploadUploaderEvent } from 'primevue/fileupload'
@@ -443,6 +442,7 @@ declare module 'vue' {
443442
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
444443
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
445444
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
445+
readonly getCurrentWatcher: UnwrapRef<typeof import('vue')['getCurrentWatcher']>
446446
readonly h: UnwrapRef<typeof import('vue')['h']>
447447
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
448448
readonly inject: UnwrapRef<typeof import('vue')['inject']>
@@ -452,6 +452,7 @@ declare module 'vue' {
452452
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
453453
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
454454
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
455+
readonly isShallow: UnwrapRef<typeof import('vue')['isShallow']>
455456
readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']>
456457
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
457458
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
@@ -555,7 +556,6 @@ declare module 'vue' {
555556
readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']>
556557
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
557558
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
558-
readonly useColumnConversion: UnwrapRef<typeof import('./src/features/data-processing/composables/useColumnConversion')['useColumnConversion']>
559559
readonly useColumnDataTypeIndicators: UnwrapRef<typeof import('./src/features/data-processing/composables/useColumnDataTypeIndicators')['useColumnDataTypeIndicators']>
560560
readonly useColumnGeneration: UnwrapRef<typeof import('./src/features/data-processing/composables/useColumnGeneration')['useColumnGeneration']>
561561
readonly useConfirm: UnwrapRef<typeof import('primevue/useconfirm')['useConfirm']>

frontend/components.d.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ declare module 'vue' {
1212
Button: typeof import('primevue/button')['default']
1313
Card: typeof import('primevue/card')['default']
1414
Chip: typeof import('primevue/chip')['default']
15+
ClaimEditor: typeof import('./src/features/wikibase-schema/components/ClaimEditor.vue')['default']
1516
Column: typeof import('primevue/column')['default']
1617
ColumnPalette: typeof import('./src/features/data-processing/components/ColumnPalette.vue')['default']
1718
ConfirmDialog: typeof import('primevue/confirmdialog')['default']
@@ -20,13 +21,10 @@ declare module 'vue' {
2021
DataTable: typeof import('primevue/datatable')['default']
2122
DataTabPanel: typeof import('./src/features/data-processing/components/DataTabPanel.vue')['default']
2223
DefaultLayout: typeof import('./src/core/layouts/DefaultLayout.vue')['default']
23-
Dialog: typeof import('primevue/dialog')['default']
24-
Dropdown: typeof import('primevue/dropdown')['default']
2524
FileUpload: typeof import('primevue/fileupload')['default']
2625
Header: typeof import('./src/shared/components/Header.vue')['default']
2726
InputText: typeof import('primevue/inputtext')['default']
2827
ItemEditor: typeof import('./src/features/wikibase-schema/components/ItemEditor.vue')['default']
29-
LanguageDropZone: typeof import('./src/features/data-processing/components/LanguageDropZone.vue')['default']
3028
MainContent: typeof import('./src/shared/components/MainContent.vue')['default']
3129
OpenProject: typeof import('./src/features/project-management/pages/OpenProject.vue')['default']
3230
Paginator: typeof import('primevue/paginator')['default']
@@ -43,10 +41,12 @@ declare module 'vue' {
4341
SchemaTabPanel: typeof import('./src/features/wikibase-schema/components/SchemaTabPanel.vue')['default']
4442
Select: typeof import('primevue/select')['default']
4543
Sidebar: typeof import('./src/shared/components/Sidebar.vue')['default']
44+
SingleQualifierEditor: typeof import('./src/features/wikibase-schema/components/SingleQualifierEditor.vue')['default']
45+
SingleReferenceEditor: typeof import('./src/features/wikibase-schema/components/SingleReferenceEditor.vue')['default']
4646
StatementConfigEditor: typeof import('./src/features/wikibase-schema/components/StatementConfigEditor.vue')['default']
4747
StatementEditor: typeof import('./src/features/wikibase-schema/components/StatementEditor.vue')['default']
48+
StatementManager: typeof import('./src/features/wikibase-schema/components/StatementManager.vue')['default']
4849
StatementPreview: typeof import('./src/features/wikibase-schema/components/StatementPreview.vue')['default']
49-
StatementsEditor: typeof import('./src/features/wikibase-schema/components/StatementsEditor.vue')['default']
5050
Tab: typeof import('primevue/tab')['default']
5151
TabList: typeof import('primevue/tablist')['default']
5252
TabPanel: typeof import('primevue/tabpanel')['default']

frontend/src/features/data-processing/components/ColumnPalette.vue

Lines changed: 53 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
const projectStore = useProjectStore()
33
const dragDropStore = useDragDropStore()
44
5-
const { convertProjectColumnsToColumnInfo } = useColumnConversion()
65
const { formatDataTypeDisplayName, generateColumnTooltip, getDataTypeIcon, getDataTypeSeverity } =
76
useColumnDataTypeIndicators()
87
@@ -12,10 +11,6 @@ const { triggerDragStartValidation } = useValidation()
1211
// Sample data visibility state (hidden by default)
1312
const showSampleData = ref(false)
1413
15-
const dataColumns = computed(() => {
16-
return convertProjectColumnsToColumnInfo(projectStore.columns, projectStore.data)
17-
})
18-
1914
const handleDragStart = (event: DragEvent, dataCol: ColumnInfo) => {
2015
if (!event.dataTransfer) return
2116
@@ -50,7 +45,7 @@ const formatSampleValues = (sampleValues: string[]) => {
5045
</script>
5146

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

7469
<!-- Content Area -->
75-
<div>
76-
<!-- Column chips -->
77-
<div class="flex flex-wrap gap-3">
70+
<!-- Column chips -->
71+
<div class="flex gap-3">
72+
<div
73+
v-for="col in projectStore.columnsForSchema"
74+
:key="col.name"
75+
:data-testid="'column-chip'"
76+
:class="{
77+
'opacity-50': dragDropStore.isDragging && dragDropStore.draggedColumn?.name === col.name,
78+
'cursor-grab': !dragDropStore.isDragging,
79+
'cursor-grabbing':
80+
dragDropStore.isDragging && dragDropStore.draggedColumn?.name === col.name,
81+
}"
82+
class="column-chip transition-opacity duration-200 hover:-translate-y-0.5 active:translate-y-0"
83+
draggable="true"
84+
@dragstart="handleDragStart($event, col)"
85+
@dragend="handleDragEnd"
86+
>
7887
<div
79-
v-for="col in dataColumns"
80-
:key="col.name"
81-
:data-testid="'column-chip'"
82-
:class="{
83-
'opacity-50':
84-
dragDropStore.isDragging && dragDropStore.draggedColumn?.name === col.name,
85-
'cursor-grab': !dragDropStore.isDragging,
86-
'cursor-grabbing':
87-
dragDropStore.isDragging && dragDropStore.draggedColumn?.name === col.name,
88-
}"
89-
class="column-chip transition-opacity duration-200 hover:-translate-y-0.5 active:translate-y-0"
90-
draggable="true"
91-
@dragstart="handleDragStart($event, col)"
92-
@dragend="handleDragEnd"
88+
class="bg-white border border-slate-300 rounded-lg p-3 shadow-sm hover:shadow-md hover:border-blue-300 transition-all duration-200"
89+
:title="generateColumnTooltip(col)"
9390
>
91+
<div class="flex items-center gap-2">
92+
<i
93+
:class="getDataTypeIcon(col.dataType)"
94+
class="text-surface-600 text-xs"
95+
:title="`${formatDataTypeDisplayName(col.dataType)} column`"
96+
/>
97+
<span class="font-small text-surface-900">{{ col.name }}</span>
98+
<Tag
99+
:value="formatDataTypeDisplayName(col.dataType)"
100+
:severity="getDataTypeSeverity(col.dataType)"
101+
class="text-xs"
102+
:title="`Database type: ${col.dataType}`"
103+
/>
104+
<i
105+
v-if="col.nullable"
106+
class="pi pi-question-circle text-surface-400 text-xs"
107+
title="This column allows null values"
108+
/>
109+
</div>
110+
111+
<div
112+
v-if="showSampleData && col.sampleValues && col.sampleValues.length > 0"
113+
class="text-xs text-surface-600"
114+
data-testid="sample-values"
115+
:title="`Sample values: ${col.sampleValues.slice(0, 5).join(', ')}${col.sampleValues.length > 5 ? '...' : ''}`"
116+
>
117+
{{ formatSampleValues(col.sampleValues) }}
118+
</div>
119+
94120
<div
95-
class="bg-white border border-surface-300 rounded-lg p-3 shadow-sm hover:shadow-md transition-shadow"
96-
:title="generateColumnTooltip(col)"
121+
v-if="col.uniqueCount !== undefined"
122+
class="text-xs text-surface-500 mt-1"
123+
:title="`This column has ${col.uniqueCount.toLocaleString()} unique values`"
97124
>
98-
<div class="flex items-center gap-2 mb-2">
99-
<i
100-
:class="getDataTypeIcon(col.dataType)"
101-
class="text-surface-600 text-sm"
102-
:title="`${formatDataTypeDisplayName(col.dataType)} column`"
103-
/>
104-
<span class="font-medium text-surface-900">{{ col.name }}</span>
105-
<Chip
106-
:label="formatDataTypeDisplayName(col.dataType)"
107-
size="small"
108-
:severity="getDataTypeSeverity(col.dataType)"
109-
class="text-xs"
110-
:title="`Database type: ${col.dataType}`"
111-
/>
112-
<i
113-
v-if="col.nullable"
114-
class="pi pi-question-circle text-surface-400 text-xs"
115-
title="This column allows null values"
116-
/>
117-
</div>
118-
119-
<div
120-
v-if="showSampleData && col.sampleValues && col.sampleValues.length > 0"
121-
class="text-xs text-surface-600"
122-
data-testid="sample-values"
123-
:title="`Sample values: ${col.sampleValues.slice(0, 5).join(', ')}${col.sampleValues.length > 5 ? '...' : ''}`"
124-
>
125-
{{ formatSampleValues(col.sampleValues) }}
126-
</div>
127-
128-
<div
129-
v-if="col.uniqueCount !== undefined"
130-
class="text-xs text-surface-500 mt-1"
131-
:title="`This column has ${col.uniqueCount.toLocaleString()} unique values`"
132-
>
133-
{{ col.uniqueCount.toLocaleString() }} unique values
134-
</div>
125+
{{ col.uniqueCount.toLocaleString() }} unique values
135126
</div>
136127
</div>
137128
</div>

0 commit comments

Comments
 (0)