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
9 changes: 8 additions & 1 deletion .kiro/specs/papers-library-importer/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@
- _Requirements: 4.1, 4.2, 4.4, 4.6, 4.7_

- [ ] 11. Add comprehensive error handling and validation
- [ ] 11.1 Implement robust error handling
- [x] 11.1 Implement robust error handling
- Implement retry mechanisms for network and storage failures
- Add workspace storage quota validation
- _Requirements: 5.1, 5.2, 5.3, 5.5_
Expand All @@ -247,3 +247,10 @@
- Add cleanup of temporary files and partial uploads
- _Requirements: 6.1, 6.2, 6.5, 6.6_

- [x] 11.3 Fix ImportFlow next button enablement logic
- Fix canGoNext computed property in ImportFlow.vue to check dataframeData instead of parsedEntries
- Update handleBibUpdate method to properly populate parsedEntries from dataframeData
- Ensure next button is enabled after successful bibliography upload regardless of PDF upload status
- Test that flexible upload order works correctly (bibliography first or PDFs first)
- _Requirements: 1.7, 7.3_

13 changes: 7 additions & 6 deletions extralit-frontend/components/features/import/ImportFlow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ export default {
// Step data
bibData: {
fileName: "",
parsedEntries: [],
dataframeData: null,
rawContent: "",
},
Expand Down Expand Up @@ -133,7 +132,9 @@ export default {
// 1. Both bibliography and PDFs are uploaded, OR
// 2. Only bibliography is uploaded (can import references without PDFs)
return (
this.bibData.parsedEntries.length > 0 &&
this.bibData.dataframeData &&
this.bibData.dataframeData.data &&
this.bibData.dataframeData.data.length > 0 &&
!this.hasError &&
!!this.workspace
);
Expand Down Expand Up @@ -200,7 +201,9 @@ export default {
// Allow flexible upload order - can proceed if bibliography is uploaded
// PDFs are optional for proceeding to analysis step
isValid =
this.bibData.parsedEntries.length > 0 &&
this.bibData.dataframeData &&
this.bibData.dataframeData.data &&
this.bibData.dataframeData.data.length > 0 &&
!this.hasError &&
!!this.workspace;
break;
Expand Down Expand Up @@ -240,7 +243,6 @@ export default {
handleBibUpdate(data) {
this.bibData = {
fileName: data.fileName || "",
parsedEntries: data.parsedEntries || [],
dataframeData: data.dataframeData || null,
rawContent: data.rawContent || "",
};
Expand Down Expand Up @@ -381,7 +383,7 @@ export default {
hasDataToLose() {
// Check if user has uploaded any data that would be lost on close
return (
this.bibData.parsedEntries.length > 0 ||
(this.bibData.dataframeData && this.bibData.dataframeData.data && this.bibData.dataframeData.data.length > 0) ||
this.pdfData.totalFiles > 0 ||
Object.keys(this.uploadData.confirmedDocuments).length > 0
);
Expand All @@ -397,7 +399,6 @@ export default {
// Reset all step data
this.bibData = {
fileName: "",
parsedEntries: [],
dataframeData: null,
rawContent: "",
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ import "assets/icons/chevron-down";
import type {
ImportAnalysisResponse,
ImportStatus,
DataframeData,
DocumentImportAnalysis,
} from '~/v1/domain/entities/import/ImportAnalysis';
import {
Expand All @@ -72,13 +71,14 @@ import {
} from '../types';
import { useImportAnalysisTableViewModel } from './useImportAnalysisTableViewModel';
import { Workspace } from "~/v1/domain/entities/workspace/Workspace";
import { TableData } from "~/v1/domain/entities/table/TableData";

export default {
name: "ImportAnalysisTable",

props: {
dataframeData: {
type: Object as () => DataframeData | null,
type: Object as () => TableData | null,
default: null,
},
pdfData: {
Expand Down Expand Up @@ -152,7 +152,6 @@ export default {
canToggle: this.canToggleStatus(originalStatus) && !this.isAnalyzing,
};

// Add all other dataframe fields dynamically
Object.keys(row).forEach(key => {
if (!['reference', 'title', 'authors', 'author', 'year', 'filePaths'].includes(key)) {
rowData[key] = row[key];
Expand All @@ -164,8 +163,9 @@ export default {
},

tableData(): AnalysisTableRow[] {
// Only show rows with matched PDFs
return this.allTableData.filter(row => row.filePaths && row.filePaths.length > 0);
const filteredData = this.allTableData.filter(row => row.filePaths && row.filePaths.length > 0);

return filteredData;
},

referencesWithoutPdfsCount(): number {
Expand All @@ -176,7 +176,7 @@ export default {
return this.allTableData.filter(row => row.filePaths && row.filePaths.length > 0).length;
},

filteredDataframeData(): DataframeData | null {
filteredDataframeData(): TableData | null {
if (!this.dataframeData) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ref, watch } from "vue";
import { useResolve } from "ts-injecty";
import { GetImportAnalysisUseCase } from "~/v1/domain/usecases/get-import-analysis-use-case";
import type { ImportAnalysisResponse, ImportStatus, DataframeData } from "~/v1/domain/entities/import/ImportAnalysis";
import type { ImportAnalysisResponse, ImportStatus } from "~/v1/domain/entities/import/ImportAnalysis";
import { Workspace } from "~/v1/domain/entities/workspace/Workspace";
import { TableData } from "~/v1/domain/entities/table/TableData";

export function useImportAnalysisTableViewModel(props: any) {
const isAnalyzing = ref(false);
Expand All @@ -23,7 +24,7 @@ export function useImportAnalysisTableViewModel(props: any) {
lastAnalysisKey.value = "";
};

const analyzeImport = async (workspace: Workspace, dataframeData: DataframeData, matchedFiles: any[]) => {
const analyzeImport = async (workspace: Workspace, dataframeData: TableData, matchedFiles: any[]) => {
if (!workspace || !dataframeData || dataframeData.data.length === 0) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
</template>

<script lang="ts">
import type { CSVConfig } from "~/v1/domain/services/IFileService";
import type { CSVConfig } from "~/v1/domain/services/IFileParsingService";

interface CsvData {
rawData: any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,14 @@
<div class="import-file-upload__content">
<div class="import-file-upload__main">
<!-- Table Upload Section -->
<TableUpload
:initial-data="bibData"
@update="handleBibUpdate"
/>
<TableUpload :initial-data="bibData" @update="handleBibUpdate" />

<!-- PDF Upload Section -->
<PdfUpload
:initial-data="pdfData"
:bibliography-entries="bibData.parsedEntries"
@update="handlePdfUpdate"
/>
<PdfUpload :initial-data="pdfData" :bibliography-entries="bibData.dataframeData" @update="handlePdfUpdate" />
</div>

<!-- Summary Sidebar -->
<ImportSummarySidebar
:bib-data="bibData"
:pdf-data="pdfData"
/>
<ImportSummarySidebar :bib-data="bibData" :pdf-data="pdfData" />
</div>
</div>
</template>
Expand All @@ -39,7 +29,6 @@ type ComponentData = {
isInitializing: boolean;
bibData: {
fileName: string;
parsedEntries: any[];
dataframeData: any;
rawContent: string;
};
Expand All @@ -65,7 +54,6 @@ export default {
type: Object,
default: () => ({
fileName: "",
parsedEntries: [],
dataframeData: null,
rawContent: "",
}),
Expand All @@ -88,7 +76,6 @@ export default {
// Bibliography data
bibData: {
fileName: "",
parsedEntries: [],
dataframeData: null,
rawContent: "",
},
Expand All @@ -102,15 +89,10 @@ export default {
};
},

mounted() {
// Initialize with existing data if provided
// Don't call initializeWithExistingData here since watchers with immediate: true will handle it
},

computed: {
isValid(): boolean {
return (
this.bibData.parsedEntries.length > 0 &&
this.bibData.dataframeData && this.bibData.dataframeData.data.length > 0 &&
this.pdfData.matchedFiles.length > 0
);
},
Expand All @@ -120,11 +102,11 @@ export default {
initialBibData: {
handler(newData: any, oldData: any) {
// Only initialize if data has actually changed and we're not already initializing
if (!this.isInitializing && newData && (newData.fileName || newData.parsedEntries.length > 0)) {
if (!this.isInitializing && newData && (newData.fileName || (newData.dataframeData && newData.dataframeData.data.length > 0))) {
// Check if the data is actually different to avoid unnecessary updates
const hasChanged = !oldData ||
newData.fileName !== oldData.fileName ||
newData.parsedEntries.length !== oldData.parsedEntries.length;
(newData.dataframeData?.data?.length || 0) !== (oldData.dataframeData?.data?.length || 0);

if (hasChanged) {
this.initializeWithExistingData();
Expand Down Expand Up @@ -159,7 +141,6 @@ export default {
handleBibUpdate(data: any): void {
this.bibData = {
fileName: data.fileName || "",
parsedEntries: data.parsedEntries || [],
dataframeData: data.dataframeData || null,
rawContent: data.rawContent || "",
};
Expand All @@ -172,15 +153,18 @@ export default {
unmatchedFiles: data.unmatchedFiles || [],
totalFiles: data.totalFiles || 0,
};

// Update dataframe data with matched file paths
this.updateDataframeWithFilePaths(data.matchedFiles || []);

this.emitPdfUpdate();
},

// Event emitters
emitBibUpdate(): void {
this.$emit("bib-update", {
isValid: this.bibData.parsedEntries.length > 0,
isValid: this.bibData.dataframeData && this.bibData.dataframeData.data.length > 0,
fileName: this.bibData.fileName,
parsedEntries: this.bibData.parsedEntries,
dataframeData: this.bibData.dataframeData,
rawContent: this.bibData.rawContent,
});
Expand All @@ -195,16 +179,55 @@ export default {
});
},

updateDataframeWithFilePaths(matchedFiles: any[]): void {
if (!this.bibData.dataframeData || !matchedFiles.length) {
return;
}

// Create a map of reference to file paths
const referenceToFiles = new Map<string, string[]>();

matchedFiles.forEach((matchedFile: any) => {
const reference = matchedFile.bibEntry?.reference;
const fileName = matchedFile.file?.name;

if (reference && fileName) {
if (!referenceToFiles.has(reference)) {
referenceToFiles.set(reference, []);
}
referenceToFiles.get(reference)!.push(fileName);
}
});
// Update the dataframe data with file paths
const updatedData = this.bibData.dataframeData.data.map((row: any) => {
const reference = row.reference || row.key;
const filePaths = referenceToFiles.get(reference) || [];

return {
...row,
filePaths: filePaths
};
});

// Update the dataframe data
this.bibData.dataframeData = {
...this.bibData.dataframeData,
data: updatedData
};

// Re-emit the bib update with the updated dataframe
this.emitBibUpdate();
},

// Initialize component with existing data when navigating back
initializeWithExistingData(): void {
// Set flag to prevent recursive updates
this.isInitializing = true;

// Initialize bibliography data
if (this.initialBibData && (this.initialBibData.fileName || this.initialBibData.parsedEntries.length > 0)) {
if (this.initialBibData && (this.initialBibData.fileName || (this.initialBibData.dataframeData && this.initialBibData.dataframeData.data.length > 0))) {
this.bibData = {
fileName: this.initialBibData.fileName || "",
parsedEntries: this.initialBibData.parsedEntries || [],
dataframeData: this.initialBibData.dataframeData || null,
rawContent: this.initialBibData.rawContent || "",
};
Expand Down Expand Up @@ -236,7 +259,6 @@ export default {
// Reset bibliography data
this.bibData = {
fileName: "",
parsedEntries: [],
dataframeData: null,
rawContent: "",
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

<div class="import-summary-sidebar__stats">
<!-- Bibliography Status -->
<div v-if="bibData.parsedEntries.length > 0" class="import-summary-sidebar__stat">
<div v-if="bibData.dataframeData && bibData.dataframeData.data.length > 0" class="import-summary-sidebar__stat">
<BaseIcon icon-name="document"
class="import-summary-sidebar__stat-icon import-summary-sidebar__stat-icon--bib" />
<span class="import-summary-sidebar__stat-text">{{ bibData.parsedEntries.length }} references
<span class="import-summary-sidebar__stat-text">{{ bibData.dataframeData.data.length }} references
found</span>
</div>

Expand Down Expand Up @@ -40,7 +40,6 @@ import "assets/icons/import";

interface BibliographyData {
fileName: string;
parsedEntries: any[];
dataframeData: any;
rawContent: string;
}
Expand All @@ -59,7 +58,6 @@ export default {
type: Object as () => BibliographyData,
default: () => ({
fileName: "",
parsedEntries: [],
dataframeData: null,
rawContent: "",
}),
Expand All @@ -76,7 +74,7 @@ export default {

computed: {
hasData(): boolean {
return this.bibData.parsedEntries.length > 0 || this.pdfData.totalFiles > 0;
return (this.bibData.dataframeData && this.bibData.dataframeData.data.length > 0) || this.pdfData.totalFiles > 0;
},
},
};
Expand Down
Loading
Loading