Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
aa93c74
kiro task list finalize
JonnyTran Aug 3, 2025
4b17188
1. Enhance backend ImportHistory API for Recent Imports
JonnyTran Aug 3, 2025
1fa121c
Merge branch 'develop' into feat/import-history-sidebar
JonnyTran Aug 3, 2025
d3acb6d
3. Create ImportHistory Dataset Builder
JonnyTran Aug 3, 2025
d6bbc6f
4.1 Create RecentImports component
JonnyTran Aug 3, 2025
40ed823
4.2 Create RecentImportCard component
JonnyTran Aug 3, 2025
fbf2a32
4.3 Create RecentImports view model
JonnyTran Aug 3, 2025
796bb61
5.1 Create ImportHistoryDataPreview component
JonnyTran Aug 3, 2025
b078172
6. Create Import Configuration Page
JonnyTran Aug 3, 2025
21485a6
7.1 Modify DatasetConfiguration component props
JonnyTran Aug 3, 2025
61415f4
[ ] 7.3 Update DatasetConfiguration view model
JonnyTran Aug 4, 2025
5ea7c6e
7.3 Update DatasetConfiguration view model
JonnyTran Aug 4, 2025
358b3f5
8.1 Modify home page sidebar
JonnyTran Aug 4, 2025
2df5d6e
8.1 Modify home page sidebar
JonnyTran Aug 4, 2025
79526b4
fix: selects first when workspaces loads
JonnyTran Aug 4, 2025
f4e6efe
fix: use BaseInputContainer as input-container
JonnyTran Aug 4, 2025
1335c83
fix RecentImports workspaces on load issue
JonnyTran Aug 4, 2025
24769eb
latest
JonnyTran Aug 4, 2025
5a06587
fix ImportHistoryListItem
JonnyTran Aug 4, 2025
52afa3d
8.2 Update home page view model
JonnyTran Aug 4, 2025
cf8022f
fix: importData.getRawData is not a function
JonnyTran Aug 4, 2025
bf1728c
fix errors
JonnyTran Aug 4, 2025
d81c89f
fix useLanguageDirection.ts
JonnyTran Aug 4, 2025
facad44
fix styling
JonnyTran Aug 4, 2025
cbf223a
fix styling
JonnyTran Aug 4, 2025
c499554
10.1 Style Recent Imports components
JonnyTran Aug 4, 2025
3d16b2c
Improve dataset import: smarter schema/type inference and question su…
JonnyTran Aug 4, 2025
7b43bfd
Add float32 and boolean types to features and subsets
JonnyTran Aug 4, 2025
6492085
Add import-history dataset import endpoint and frontend support
JonnyTran Aug 4, 2025
ab8e9e3
latest
JonnyTran Aug 5, 2025
2b797fe
latest
JonnyTran Aug 5, 2025
68a0e45
skip creating record metadata for all fieldName
JonnyTran Aug 5, 2025
f1c2fb6
added metadata selector
JonnyTran Aug 5, 2025
311c23a
Update metadata fields in dataset configuration and improve metadata …
JonnyTran Aug 5, 2025
a403136
fix emit tag
JonnyTran Aug 5, 2025
e2b4a19
Updated BaseModal component to allow closing by default and enhanced …
JonnyTran Aug 5, 2025
c338169
Refactor ImportHistoryDetails component for simplified data handling
JonnyTran Aug 5, 2025
1001258
Enhance import history data structure and processing
JonnyTran Aug 5, 2025
fb81a04
Refactor BaseSimpleTable and ImportHistoryDataPreview components for …
JonnyTran Aug 5, 2025
f38a2e8
Refactor Import History components for improved functionality and use…
JonnyTran Aug 5, 2025
1d42f26
Refactor import history data structures and interfaces for consistency
JonnyTran Aug 6, 2025
27e3a23
styling
JonnyTran Aug 6, 2025
1210eaf
fix tests
JonnyTran Aug 6, 2025
5da9f65
Enhance ImportAnalysisTable component with improved testing patterns …
JonnyTran Aug 6, 2025
c176770
fix tests
JonnyTran Aug 6, 2025
5e544e6
update codecov and tests
JonnyTran Aug 6, 2025
8dccc09
passing tests
JonnyTran Aug 6, 2025
bac7d82
recent import tests
JonnyTran Aug 6, 2025
ebedcd1
fix tests
JonnyTran Aug 6, 2025
9ee9e3b
Add tests for ImportHistory configuration workflow and components
JonnyTran Aug 6, 2025
07f8dbf
fix test
JonnyTran Aug 6, 2025
6313c91
fixed all tests
JonnyTran Aug 6, 2025
ff96ac3
added editable question configuration card
JonnyTran Aug 6, 2025
14940cd
renameQuestion
JonnyTran Aug 6, 2025
f5b156c
Merge branch 'develop' into feat/import-history-sidebar
JonnyTran Aug 6, 2025
0c58271
renameQuestion name, not just title
JonnyTran Aug 6, 2025
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div>
<p v-if="legend" class="questions__title --body3 --light" v-text="legend" :aria-label="legend" />
<p v-if="legend" class="questions__title --body3 --light" :aria-label="legend" v-text="legend" />
<div class="questions" role="list" aria-label="List of annotation questions">
<div
v-for="(question, index) in questions"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,22 @@
{{ $t("datasetCreation.fields") }}
<div v-if="dataset.subsets.length > 1" class="config-form__subset">
{{ $t("datasetCreation.subset") }}:
<DatasetConfigurationSelector
class="config-form__selector"
:options="dataset.subsets"
:value="dataset.selectedSubset.name"
@onValueChange="$emit('change-subset', $event)"
>
<DatasetConfigurationSelector class="config-form__selector" :options="dataset.subsets"
:value="dataset.selectedSubset.name" @onValueChange="$emit('change-subset', $event)">
<template slot="optionsIntro">
<span class="config-form__selector__intro">{{ $t("datasetCreation.selectSubset") }}</span>
</template>
</DatasetConfigurationSelector>
</div>
</div>
<div class="config-form__col__content">
<draggable
class="config-form__draggable-area"
:list="dataset.selectedSubset.fields"
:group="{ name: 'fields' }"
ghost-class="config-form__ghost"
:disabled="isFocused"
>
<draggable class="config-form__draggable-area" :list="dataset.selectedSubset.fields"
:group="{ name: 'fields' }" ghost-class="config-form__ghost" :disabled="isFocused">
<transition-group class="config-form__draggable-area-wrapper" type="transition" :css="false">
<DatasetConfigurationField
v-for="field in dataset.selectedSubset.fields.filter((f) => f.name !== dataset.mappings.external_id)"
:key="field.name"
:field="field"
:available-types="
availableFieldTypes.filter((a) => a.value === 'no mapping' || a.value === field.originalType.value)
"
@is-focused="isFocused = $event"
/>
:key="field.name" :field="field" :available-types="availableFieldTypes.filter((a) => a.value === 'no mapping' || a.value === field.originalType.value)
" @is-focused="isFocused = $event" />
</transition-group>
</draggable>
</div>
Expand All @@ -46,11 +32,8 @@
{{ $t("datasetCreation.metadata") }}
</div>
<div class="config-form__col__content config-form__col__content--metadata">
<DatasetConfigurationMetadataSelector
:available-fields="availableMetadataFields"
:selected-fields="selectedMetadataFields"
@onSelectionChange="updateMetadataSelection"
/>
<DatasetConfigurationMetadataSelector :available-fields="availableMetadataFields"
:selected-fields="selectedMetadataFields" @onSelectionChange="updateMetadataSelection" />
</div>
</div>
</div>
Expand All @@ -60,51 +43,34 @@
{{ $t("datasetCreation.questionsTitle") }}
<DatasetConfigurationAddQuestion
:options="['text', 'label_selection', 'multi_label_selection', 'rating', 'ranking', 'span']"
@add-question="addQuestion($event)"
/>
@add-question="addQuestion($event)" />
</div>
<div class="config-form__col__content --questions">
<draggable
v-if="dataset.selectedSubset.questions.length"
class="config-form__draggable-area"
ghost-class="config-form__ghost"
:list="dataset.selectedSubset.questions"
:group="{ name: 'questions' }"
:disabled="isFocused"
>
<draggable v-if="dataset.selectedSubset.questions.length" class="config-form__draggable-area"
ghost-class="config-form__ghost" :list="dataset.selectedSubset.questions" :group="{ name: 'questions' }"
:disabled="isFocused">
<transition-group class="config-form__draggable-area-wrapper" type="transition" :css="false">
<DatasetConfigurationQuestion
v-for="question in dataset.selectedSubset.questions"
:key="question.name"
:selectedSubset="dataset.selectedSubset"
:question="question"
:remove-is-allowed="true"
:available-types="availableQuestionTypes"
@change-type="onTypeIsChanged(question.name, $event)"
@is-focused="isFocused = $event"
/>
<DatasetConfigurationQuestion v-for="(question, index) in dataset.selectedSubset.questions"
:key="`question-${index}`" :selectedSubset="dataset.selectedSubset" :question="question"
:remove-is-allowed="true" :available-types="availableQuestionTypes"
@change-type="onTypeIsChanged(index, $event)" @is-focused="isFocused = $event" />
</transition-group>
</draggable>
</div>
</div>
<div class="config-form__button-area">
<BaseButton class="primary" @click.prevent="visibleDatasetCreationDialog = !visibleDatasetCreationDialog">{{
$t("datasetCreation.button")
}}</BaseButton>
<DatasetConfigurationDialog
v-if="visibleDatasetCreationDialog"
:dataset="dataset"
:is-loading="isLoading"
@close-dialog="visibleDatasetCreationDialog = false"
@create-dataset="createDataset"
/>
}}</BaseButton>
<DatasetConfigurationDialog v-if="visibleDatasetCreationDialog" :dataset="dataset" :is-loading="isLoading"
@close-dialog="visibleDatasetCreationDialog = false" @create-dataset="createDataset" />
</div>
</div>
</div>
</section>
</template>

<script>
<script lang="ts">
import { useDatasetConfigurationForm } from "./useDatasetConfigurationForm";
import { MetadataCreation } from "~/v1/domain/entities/hub/MetadataCreation";

Expand All @@ -125,7 +91,7 @@ export default {
computed: {
getMaxNumberInNames() {
return Math.max(
...this.dataset.selectedSubset.questions.map((question) => {
...this.dataset.selectedSubset.questions.map((question: any) => {
const numberInName = question.name.split("_").pop();
return parseInt(numberInName) || 0;
})
Expand All @@ -137,46 +103,48 @@ export default {
return this.dataset.availableFields;
}
// Fallback to field names from the dataset
return this.dataset.selectedSubset.fields.map((f) => f.name);
return this.dataset.selectedSubset.fields.map((f: any) => f.name);
},
},
methods: {
createDataset() {
this.create(this.dataset);
},
generateName(type, number) {
generateName(type: string, number: string | number): string {
const typeName = this.$t(`config.questionId.${type}`);
return `${typeName}_${parseInt(number) || 0}`;
return `${typeName}_${parseInt(number as string) || 0}`;
},
addQuestion(type) {
addQuestion(type: string) {
const questionName = this.generateName(type, this.getMaxNumberInNames + 1);
this.dataset.selectedSubset.addQuestion(questionName, {
type,
});
},
onTypeIsChanged(oldName, type) {
const numberInName = oldName.split("_").pop();
const index = this.dataset.selectedSubset.questions.findIndex((q) => q.name === oldName);
this.dataset.selectedSubset.removeQuestion(oldName);
onTypeIsChanged(questionIndex: number, type: any) {
const question = this.dataset.selectedSubset.questions[questionIndex];
if (!question) return;

const numberInName = question.name.split("_").pop();
this.dataset.selectedSubset.removeQuestion(question.name);
const newQuestionName = this.generateName(type.value, numberInName);
this.dataset.selectedSubset.addQuestion(
newQuestionName,
{
type: type.value,
},
index !== -1 ? index : undefined
questionIndex
);
},
updateMetadataSelection(selectedFields) {
updateMetadataSelection(selectedFields: string[]) {
this.selectedMetadataFields = selectedFields;
this.updateDatasetMetadata(selectedFields);
},
updateDatasetMetadata(selectedFields) {
updateDatasetMetadata(selectedFields: string[]) {
// Clear existing metadata
this.dataset.selectedSubset.metadata.length = 0;

// Add selected fields as metadata
selectedFields.forEach((fieldName) => {
selectedFields.forEach((fieldName: string) => {
const metadata = MetadataCreation.from(fieldName, "terms");
if (metadata) {
this.dataset.selectedSubset.metadata.push(metadata);
Expand All @@ -187,7 +155,7 @@ export default {
mounted() {
if (this.dataset.importHistoryId) {
const defaultMetadataFields = ["reference", "doi", "pmid"];
const availableDefaults = this.availableMetadataFields.filter((field) => defaultMetadataFields.includes(field));
const availableDefaults = this.availableMetadataFields.filter((field: string) => defaultMetadataFields.includes(field));
this.updateMetadataSelection(availableDefaults);
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</DatasetConfigurationCard>
</template>

<script>
<script lang="ts">
export default {
props: {
field: {
Expand All @@ -29,5 +29,6 @@ export default {
prop: "type",
event: "change",
},
methods: {},
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
:available-types="availableTypes"
@is-focused="$emit('is-focused', $event)"
@change-type="$emit('change-type', $event)"
@name-changed="updateQuestionName"
>
<BaseButton class="config-card__remove" @click="remove" v-if="removeIsAllowed"><svgicon name="close" /></BaseButton>
<BaseButton v-if="removeIsAllowed" class="config-card__remove" @click="remove"><svgicon name="close" /></BaseButton>

<template v-if="noMapping">
<DatasetConfigurationLabels
Expand Down Expand Up @@ -34,9 +35,9 @@
<span class="separator"></span>
<DatasetConfigurationColumnSelector
v-if="showColumnSelector"
v-model="question.column"
class="config-card__type"
:options="selectedSubset.columns"
v-model="question.column"
/>
<BaseCheckbox
class="config-card__required"
Expand All @@ -48,7 +49,7 @@
</DatasetConfigurationCard>
</template>

<script>
<script lang="ts">
import "assets/icons/close";
export default {
props: {
Expand Down Expand Up @@ -85,6 +86,15 @@ export default {
remove() {
this.selectedSubset.removeQuestion(this.question.name);
},
updateQuestionName(newName: string) {
try {
// Rename the question in the subset - this will replace the question object
this.selectedSubset.renameQuestion(this.question.name, newName);
} catch (error) {
console.error('Failed to rename question:', error.message);
// You could show a user-friendly error message here
}
},
},
};
</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,34 @@
<div class="config-card">
<div class="config-card__content" :class="item?.type">
<h3 class="config-card__title">
<svgicon class="config-card__icon" width="6" name="draggable" color="var(--bg-opacity-20)" />{{ item.name }}
<svgicon class="config-card__icon" width="6" name="draggable" color="var(--bg-opacity-20)" />
<input
v-if="isEditingName"
ref="nameInput"
v-model="editableName"
class="config-card__title-input"
@blur="finishEditing"
@keydown.enter="finishEditing"
@keydown.escape="cancelEditing"
/>
<span
v-else
class="config-card__title-text"
:class="{ 'config-card__title-text--editable': configType === 'question' }"
@dblclick="configType === 'question' ? startEditing() : null"
>{{ item.name }}</span
>
<span v-if="item.primitiveType" class="config-card__primitive-type">{{ item.primitiveType }}</span>
</h3>
<slot name="header" />
<div class="config-card__row">
<DatasetConfigurationChipsSelector
:id="item.name"
v-model="item.type"
:type="configType"
class="config-card__type"
:options="availableTypes"
@onValueChange="$emit('change-type', $event)"
v-model="item.type"
/>
</div>
<slot></slot>
Expand All @@ -23,7 +39,7 @@
</div>
</template>

<script>
<script lang="ts">
import "assets/icons/draggable";
export default {
props: {
Expand All @@ -44,6 +60,12 @@ export default {
default: false,
},
},
data() {
return {
isEditingName: false,
editableName: "",
};
},
computed: {
hasNoMapping() {
return this.item.type.value === "no mapping";
Expand All @@ -53,6 +75,27 @@ export default {
prop: "type",
event: "change",
},
methods: {
startEditing() {
if (this.configType !== "question") return;
this.isEditingName = true;
this.editableName = this.item.name;
this.$nextTick(() => {
this.$refs.nameInput.focus();
this.$refs.nameInput.select();
});
},
finishEditing() {
if (this.editableName.trim() && this.editableName !== this.item.name) {
this.$emit("name-changed", this.editableName.trim());
}
this.isEditingName = false;
},
cancelEditing() {
this.isEditingName = false;
this.editableName = this.item.name;
},
},
};
</script>

Expand Down Expand Up @@ -94,6 +137,35 @@ $no-mapping-color: hsl(0, 0%, 50%);
font-weight: 500;
@include font-size(14px);
}
&__title-text {
&--editable {
cursor: pointer;
&:hover {
background: var(--bg-opacity-4);
border-radius: 2px;
padding: 1px 2px;
margin: -1px -2px;
}
}
}
&__title-input {
background: transparent;
border: none;
outline: none;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
color: inherit;
padding: 1px 2px;
margin: -1px -2px;
border-radius: 2px;
background: var(--bg-accent-grey-2);
min-width: 100px;
&:focus {
background: var(--bg-accent-grey-2);
box-shadow: 0 0 0 1px var(--bg-opacity-20);
}
}
&__icon {
position: absolute;
left: 6px;
Expand Down
Loading
Loading