diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2ed806a62..a226fc0a1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,7 +62,11 @@ repos: hooks: - id: frontend-lint name: "Lint and fix extralit-frontend files" - entry: bash -c 'cd extralit-frontend && npx eslint --fix --cache "$@" || true' + # pre-commit passes repo-relative paths (extralit-frontend/foo.vue). The trailing + # `--` is the placeholder $0 (otherwise `bash -c` assigns the first file to $0 and + # "$@" drops it); `${@#extralit-frontend/}` strips the prefix so paths resolve after + # the cd. `|| true` keeps lint advisory (non-blocking), matching `eslint . --quiet`. + entry: bash -c 'cd extralit-frontend && npx eslint --fix --cache "${@#extralit-frontend/}" || true' -- language: system files: '^extralit-frontend/.*\.(js|ts|vue)$' pass_filenames: true diff --git a/extralit-frontend/.eslintignore b/extralit-frontend/.eslintignore deleted file mode 100644 index 79aa66fbf..000000000 --- a/extralit-frontend/.eslintignore +++ /dev/null @@ -1,8 +0,0 @@ -node_modules/**/* -dist/**/* -.nuxt/**/* -e2e/**/* - -v1/domain/entities/document/Document.ts -v1/domain/usecases/get-extraction-completion-use-case.ts -components/base/base-render-table/* diff --git a/extralit-frontend/.eslintrc.js b/extralit-frontend/.eslintrc.js deleted file mode 100644 index b526a5c77..000000000 --- a/extralit-frontend/.eslintrc.js +++ /dev/null @@ -1,101 +0,0 @@ -module.exports = { - root: true, - env: { - node: true, - browser: true, - jest: true, - }, - extends: [ - "eslint:recommended", - "plugin:@intlify/vue-i18n/recommended", - "plugin:prettier/recommended", - "plugin:nuxt/recommended", - ], - plugins: ["vue"], - settings: { - "vue-i18n": { - localeDir: "./translation/*.js", - }, - }, - rules: { - // Formatting is advisory here (parity with the *.ts override and the separate - // `npm run format` step); keeps `lint --quiet` focused on real correctness rules. - "prettier/prettier": "warn", - "no-console": process.env.NODE_ENV === "production" ? "error" : "off", - "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off", - "prefer-const": "warn", - "prefer-arrow-callback": "warn", - "no-unused-vars": ["warn", { ignoreRestSiblings: true }], - "@intlify/vue-i18n/no-raw-text": "off", - "@intlify/vue-i18n/no-v-html": "off", - "@intlify/vue-i18n/no-missing-keys": "warn", - "vue/attributes-order": [ - "warn", - { - order: [ - "DEFINITION", - "LIST_RENDERING", - "CONDITIONALS", - "RENDER_MODIFIERS", - "GLOBAL", - "UNIQUE", - "TWO_WAY_BINDING", - "OTHER_DIRECTIVES", - "OTHER_ATTR", - "EVENTS", - "CONTENT", - ], - alphabetical: false, - }, - ], - }, - globals: { - $nuxt: true, - vi: true, - // Nuxt 4 auto-imports, hand-declared to satisfy no-undef. This list can drift; - // the real fix is adopting `@nuxt/eslint` (flat config), which auto-generates - // these globals from the build manifest. Tracked as follow-up, not done here. - defineNuxtPlugin: "readonly", - defineNuxtRouteMiddleware: "readonly", - definePageMeta: "readonly", - navigateTo: "readonly", - abortNavigation: "readonly", - useNuxtApp: "readonly", - useRuntimeConfig: "readonly", - useRoute: "readonly", - useRouter: "readonly", - useState: "readonly", - useCookie: "readonly", - useHead: "readonly", - useSeoMeta: "readonly", - useError: "readonly", - createError: "readonly", - clearError: "readonly", - showError: "readonly", - }, - parser: "vue-eslint-parser", - parserOptions: { - parser: "@typescript-eslint/parser", - ecmaVersion: 2022, - sourceType: "module", - }, - overrides: [ - { - files: ["**/*.ts"], - extends: ["plugin:@typescript-eslint/recommended", "prettier"], - parser: "@typescript-eslint/parser", - plugins: ["@typescript-eslint", "prettier"], - parserOptions: { project: ["./tsconfig.json"] }, - rules: { - "prettier/prettier": ["warn"], - quotes: ["warn", "double"], - semi: ["warn", "always"], - "import/no-named-as-default-member": 0, - "no-useless-constructor": 0, - "space-before-function-paren": 0, - "no-throw-literal": 0, - "no-new": 0, - }, - }, - ], -}; diff --git a/extralit-frontend/.prettierignore b/extralit-frontend/.prettierignore new file mode 100644 index 000000000..26dd47d02 --- /dev/null +++ b/extralit-frontend/.prettierignore @@ -0,0 +1,11 @@ +# Dependencies and build output / generated dirs (prettier has no implicit ignores +# beyond node_modules; without this it tries to parse generated .vue files in .nuxt*). +node_modules +dist +.output +.nuxt +.nuxt-stale-root +.vercel + +# Lockfiles and generated data +package-lock.json diff --git a/extralit-frontend/components/base/BaseSvgIcon.vue b/extralit-frontend/components/base/BaseSvgIcon.vue index 843280a53..80492c076 100644 --- a/extralit-frontend/components/base/BaseSvgIcon.vue +++ b/extralit-frontend/components/base/BaseSvgIcon.vue @@ -47,9 +47,7 @@ const svg = computed(() => { // Strip the intrinsic width/height off the root so ours win (viewBox // keeps the aspect ratio), then inject the requested dimensions. - markup = markup - .replace(/(]*?)\swidth="[^"]*"/i, "$1") - .replace(/(]*?)\sheight="[^"]*"/i, "$1"); + markup = markup.replace(/(]*?)\swidth="[^"]*"/i, "$1").replace(/(]*?)\sheight="[^"]*"/i, "$1"); const sized: string[] = []; if (width) sized.push(`width="${width}"`); if (height) sized.push(`height="${height}"`); diff --git a/extralit-frontend/components/base/base-breadcrumbs/BaseBreadcrumbs.vue b/extralit-frontend/components/base/base-breadcrumbs/BaseBreadcrumbs.vue index 3146ffd37..869aee2ac 100644 --- a/extralit-frontend/components/base/base-breadcrumbs/BaseBreadcrumbs.vue +++ b/extralit-frontend/components/base/base-breadcrumbs/BaseBreadcrumbs.vue @@ -17,18 +17,10 @@ :is-last-breadcrumb="index === filteredBreadcrumbs.length - 1" /> - + {{ breadcrumb.name }} - + {{ breadcrumb.name }} @@ -39,7 +31,10 @@ class="breadcrumbs__copy" @click.prevent=" $copyToClipboard( - filteredBreadcrumbs.slice(-2).map(breadcrumb => breadcrumb.name).join('/') + filteredBreadcrumbs + .slice(-2) + .map((breadcrumb) => breadcrumb.name) + .join('/') ) " > diff --git a/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue b/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue index c160074bb..6b0f6696b 100644 --- a/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue +++ b/extralit-frontend/components/base/base-breadcrumbs/DatasetBreadcrumbDropdown.vue @@ -2,50 +2,45 @@ @@ -95,28 +90,26 @@ export default { if (!this.selectedWorkspace) { return this.allDatasets; } - return this.allDatasets.filter(dataset => dataset.workspaceId === this.selectedWorkspace.id); + return this.allDatasets.filter((dataset) => dataset.workspaceId === this.selectedWorkspace.id); }, selectedDataset(): Dataset | null { - return this.datasets.find(d => d.id === this.datasetId) || null; + return this.datasets.find((d) => d.id === this.datasetId) || null; }, selectedDatasetName(): string { - return this.selectedDataset?.name || this.$t('Select dataset'); + return this.selectedDataset?.name || this.$t("Select dataset"); }, selectedDatasetId: { get(): string | null { return this.datasetId; }, set(datasetId: string | null) { - const dataset = this.datasets.find(d => d.id === datasetId) || null; + const dataset = this.datasets.find((d) => d.id === datasetId) || null; this.onDatasetChange(dataset); this.visibleDropdown = false; - } + }, }, datasetsFilteredBySearchText(): Dataset[] { - return this.datasets.filter((dataset) => - dataset.name.toLowerCase().includes(this.searchText.toLowerCase()) - ); + return this.datasets.filter((dataset) => dataset.name.toLowerCase().includes(this.searchText.toLowerCase())); }, }, methods: { @@ -130,14 +123,14 @@ export default { path: `/dataset/${dataset.id}/annotation-mode`, query: { ...this.$route.query, - workspace: this.selectedWorkspace?.name - } + workspace: this.selectedWorkspace?.name, + }, }); } }, onDatasetSelectionChange(datasetId: string) { this.selectedDatasetId = datasetId; - } + }, }, }; @@ -149,7 +142,7 @@ export default { &__header { display: flex; align-items: center; - padding: $base-space / 2; + padding: $base-space * 0.5; color: var(--fg-lighter); cursor: pointer; border-radius: $border-radius-s; @@ -193,7 +186,7 @@ export default { } &__items { - padding: $base-space / 2; + padding: $base-space * 0.5; } &__item { @@ -268,7 +261,7 @@ export default { margin: 0; .base-search__input { - padding: $base-space / 2; + padding: $base-space * 0.5; border-radius: $border-radius-s; border: 1px solid var(--border-field); background: var(--bg-field); @@ -290,4 +283,4 @@ export default { flex: 1; } } - \ No newline at end of file + diff --git a/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue b/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue index bda6bcd5c..9af9eea1e 100644 --- a/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue +++ b/extralit-frontend/components/base/base-breadcrumbs/WorkspaceBreadcrumbDropdown.vue @@ -2,50 +2,45 @@ @@ -83,18 +78,18 @@ export default { return this.workspaceStore.get().selectedWorkspace; }, selectedWorkspaceName(): string { - return this.selectedWorkspace?.name || this.$t('Select workspace'); + return this.selectedWorkspace?.name || this.$t("Select workspace"); }, selectedWorkspaceId: { get(): string | null { return this.selectedWorkspace?.id || null; }, set(workspaceId: string | null) { - const workspace = this.workspaces.find(w => w.id === workspaceId) || null; + const workspace = this.workspaces.find((w) => w.id === workspaceId) || null; this.workspaceStore.saveSelectedWorkspace(workspace); this.onWorkspaceChange(workspace); this.visibleDropdown = false; - } + }, }, workspacesFilteredBySearchText(): Workspace[] { return this.workspaces.filter((workspace) => @@ -110,26 +105,26 @@ export default { // Navigate to home page (dataset selection) when workspace changes // This ensures users always go to the dataset selection view when switching workspaces const targetRoute = { - path: '/', - query: workspace ? { workspace: workspace.name } : {} + path: "/", + query: workspace ? { workspace: workspace.name } : {}, }; // Only navigate if we're not already on the home page - if (this.$route.path !== '/') { + if (this.$route.path !== "/") { this.$router.push(targetRoute); } // Emit breadcrumb link update event with workspace information - this.$emit('workspace-change', { + this.$emit("workspace-change", { workspace, workspaceId: workspace?.id || null, workspaceName: workspace?.name || null, - link: targetRoute + link: targetRoute, }); }, onWorkspaceSelectionChange(workspaceId: string) { this.selectedWorkspaceId = workspaceId; - } + }, }, }; @@ -141,7 +136,7 @@ export default { &__header { display: flex; align-items: center; - padding: $base-space / 2; + padding: $base-space * 0.5; color: var(--fg-lighter); cursor: pointer; border-radius: $border-radius-s; @@ -185,7 +180,7 @@ export default { } &__items { - padding: $base-space / 2; + padding: $base-space * 0.5; } &__item { @@ -260,7 +255,7 @@ export default { margin: 0; .base-search__input { - padding: $base-space / 2; + padding: $base-space * 0.5; border-radius: $border-radius-s; border: 1px solid var(--border-field); background: var(--bg-field); @@ -282,5 +277,4 @@ export default { flex: 1; } } - - \ No newline at end of file + diff --git a/extralit-frontend/components/base/base-button/BaseButton.vue b/extralit-frontend/components/base/base-button/BaseButton.vue index f68edcee3..89e3dd11f 100644 --- a/extralit-frontend/components/base/base-button/BaseButton.vue +++ b/extralit-frontend/components/base/base-button/BaseButton.vue @@ -244,7 +244,7 @@ export default { &:hover, &:active, &.active { - background-color: hsl(from var(--color-danger) h s calc(l - 4)); + background-color: color-mix(in srgb, var(--color-danger), black 6%); } &.outline { background: none; @@ -256,8 +256,8 @@ export default { &:hover, &:active, &.active { - color: hsl(from var(--color-danger) h s calc(l - 4)); - border-color: hsl(var(--color-danger) calc(l - 4)); + color: color-mix(in srgb, var(--color-danger), black 6%); + border-color: color-mix(in srgb, var(--color-danger), black 6%); } } &.light { diff --git a/extralit-frontend/components/base/base-collpasable-panel/BaseCollapsablePanel.vue b/extralit-frontend/components/base/base-collpasable-panel/BaseCollapsablePanel.vue index 67c68993c..9b4d99d51 100644 --- a/extralit-frontend/components/base/base-collpasable-panel/BaseCollapsablePanel.vue +++ b/extralit-frontend/components/base/base-collpasable-panel/BaseCollapsablePanel.vue @@ -58,6 +58,12 @@ export default { &__header { overflow: visible; color: var(--fg-secondary); + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: $base-space $base-space * 2; + @include font-size(13px); &__container { width: 100%; @@ -70,12 +76,6 @@ export default { } } - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; - padding: $base-space $base-space * 2; - @include font-size(13px); :deep(p) { text-transform: uppercase; } diff --git a/extralit-frontend/components/base/base-flow-modal/BaseFlowModal.vue b/extralit-frontend/components/base/base-flow-modal/BaseFlowModal.vue index 0d01dd752..3a152029c 100644 --- a/extralit-frontend/components/base/base-flow-modal/BaseFlowModal.vue +++ b/extralit-frontend/components/base/base-flow-modal/BaseFlowModal.vue @@ -87,7 +87,6 @@ \ No newline at end of file + diff --git a/extralit-frontend/components/features/import/analysis/ImportAnalysisTable.vue b/extralit-frontend/components/features/import/analysis/ImportAnalysisTable.vue index 509268352..b8a760646 100644 --- a/extralit-frontend/components/features/import/analysis/ImportAnalysisTable.vue +++ b/extralit-frontend/components/features/import/analysis/ImportAnalysisTable.vue @@ -3,7 +3,7 @@
-

{{ isAnalyzing ? 'Analyzing import status...' : 'Loading...' }}

+

{{ isAnalyzing ? "Analyzing import status..." : "Loading..." }}

@@ -12,9 +12,7 @@

Analysis Failed

{{ errorMessage }}

- - Retry Analysis - + Retry Analysis
@@ -83,13 +81,9 @@ import type { ImportAnalysisResponse, ImportStatus, DocumentImportAnalysis, -} from '~/v1/domain/entities/import/ImportAnalysis'; -import { - type AnalysisTableRow, - type TableColumn, - type CellComponent, -} from '../types'; -import { useImportAnalysisTableViewModel } from './useImportAnalysisTableViewModel'; +} from "~/v1/domain/entities/import/ImportAnalysis"; +import { type AnalysisTableRow, type TableColumn, type CellComponent } from "../types"; +import { useImportAnalysisTableViewModel } from "./useImportAnalysisTableViewModel"; import { Workspace } from "~/v1/domain/entities/workspace/Workspace"; import { TableData } from "~/v1/domain/entities/table/TableData"; @@ -152,8 +146,8 @@ export default { // Get analysis info if available const analysisInfo = this.analysisResult?.documents?.[reference]; - const currentStatus = documentActions[reference] || analysisInfo?.status || 'add'; - const originalStatus = analysisInfo?.status || 'add'; + const currentStatus = documentActions[reference] || analysisInfo?.status || "add"; + const originalStatus = analysisInfo?.status || "add"; const validationErrors = analysisInfo?.validation_errors || []; // Use pre-processed file paths from ImportFileUpload.vue @@ -173,8 +167,8 @@ export default { canToggle: this.canToggleStatus(originalStatus) && !this.isAnalyzing, }; - Object.keys(row).forEach(key => { - if (!['reference', 'title', 'authors', 'author', 'year', 'filePaths'].includes(key)) { + Object.keys(row).forEach((key) => { + if (!["reference", "title", "authors", "author", "year", "filePaths"].includes(key)) { rowData[key] = row[key]; } }); @@ -184,17 +178,17 @@ export default { }, tableData(): AnalysisTableRow[] { - const filteredData = 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 { - return this.allTableData.filter(row => !row.filePaths || row.filePaths.length === 0).length; + return this.allTableData.filter((row) => !row.filePaths || row.filePaths.length === 0).length; }, referencesWithPdfsCount(): number { - return this.allTableData.filter(row => row.filePaths && row.filePaths.length > 0).length; + return this.allTableData.filter((row) => row.filePaths && row.filePaths.length > 0).length; }, filteredDataframeData(): TableData | null { @@ -244,9 +238,9 @@ export default { // Add dynamic columns from dataframe schema if (this.dataframeData?.schema?.fields) { - const excludedFields = ['reference', 'title', 'authors', 'author', 'year', 'filePaths', 'type']; + const excludedFields = ["reference", "title", "authors", "author", "year", "filePaths", "type"]; - this.dataframeData.schema.fields.forEach(field => { + this.dataframeData.schema.fields.forEach((field) => { if (!excludedFields.includes(field.name)) { columns.push({ field: field.name, @@ -277,12 +271,12 @@ export default { headerFilterParams: { values: { "": "All", - "add": "Add", - "update": "Update", - "skip": "Skip", - "ignore": "Ignore", - "failed": "Failed" - } + add: "Add", + update: "Update", + skip: "Skip", + ignore: "Ignore", + failed: "Failed", + }, }, } ); @@ -324,7 +318,7 @@ export default { // Count from dataframe data - only include references with PDFs this.dataframeData.data.forEach((row: Record) => { const reference = row.reference || row.key; - const finalAction = documentActions[reference] || 'add'; + const finalAction = documentActions[reference] || "add"; const filePaths = row.filePaths || []; const hasFiles = filePaths.length > 0; @@ -347,8 +341,7 @@ export default { }, shouldShowEditableTable() { - return (!this.dataframeData || this.dataframeData.data.length === 0) - && this.allPdfFileNames.length > 0; + return (!this.dataframeData || this.dataframeData.data.length === 0) && this.allPdfFileNames.length > 0; }, allPdfFileNames() { @@ -360,8 +353,7 @@ export default { unmappedPdfFiles() { const assigned = new Set(); this.editableTableData.forEach((row: any) => { - (Array.isArray(row.files) ? row.files : row.files ? [row.files] : []) - .forEach((f: string) => assigned.add(f)); + (Array.isArray(row.files) ? row.files : row.files ? [row.files] : []).forEach((f: string) => assigned.add(f)); }); return (this.allPdfFileNames as string[]).filter((n: string) => !assigned.has(n)); }, @@ -406,8 +398,8 @@ export default { const current = cell.getValue() || []; const currentSet = new Set(Array.isArray(current) ? current : [current]); const values = [...allFiles] - .filter(f => !usedFiles.has(f) || currentSet.has(f)) - .map(f => ({ label: f, value: f })); + .filter((f) => !usedFiles.has(f) || currentSet.has(f)) + .map((f) => ({ label: f, value: f })); return { values, multiselect: true, @@ -423,8 +415,11 @@ export default { } const files = Array.isArray(value) ? value : [value]; const [first, ...rest] = files; - const suffix = rest.length > 0 ? ` +${rest.length}` : ''; - return `${first}${suffix}`; + const suffix = + rest.length > 0 + ? ` +${rest.length}` + : ""; + return `${first}${suffix}`; }, }, ]; @@ -447,7 +442,7 @@ export default { // Reset local document actions when new analysis data arrives this.localDocumentActions = {}; // Emit the analysis complete event - this.$emit('analysis-complete', newData); + this.$emit("analysis-complete", newData); // Emit initial update this.emitUpdate(); } @@ -479,7 +474,7 @@ export default { return `${files}`; } const fileCount = files.split(", ").filter((f: string) => f.trim().length > 0).length; - return `${fileCount} file${fileCount !== 1 ? 's' : ''}`; + return `${fileCount} file${fileCount !== 1 ? "s" : ""}`; }, statusFormatter(cell: CellComponent) { @@ -489,10 +484,10 @@ export default { const statusClass = `status-${status}`; const statusText = this.getStatusText(status); - const toggleIcon = canToggle ? '' : ''; + const toggleIcon = canToggle ? '' : ""; return ` -
+
${statusText} ${toggleIcon} @@ -521,7 +516,7 @@ export default { update: "Update", skip: "Skip", ignore: "Ignore", - failed: "Failed" + failed: "Failed", }; return statusMap[status] || status; }, @@ -564,8 +559,8 @@ export default { formatColumnTitle(fieldName: string) { // Convert field names to readable titles return fieldName - .replace(/([A-Z])/g, ' $1') - .replace(/^./, str => str.toUpperCase()) + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()) .trim(); }, @@ -577,7 +572,7 @@ export default { if (this.shouldShowEditableTable) { this.editableTableData.forEach((row: any) => { const reference = row.reference?.trim(); - const filesArray = Array.isArray(row.files) ? row.files : (row.files ? [row.files] : []); + const filesArray = Array.isArray(row.files) ? row.files : row.files ? [row.files] : []; if (!reference || filesArray.length === 0) return; confirmedDocuments[reference] = { document_create: { @@ -599,39 +594,45 @@ export default { })), }; }); - // Handle analysis data case (preferred) - } else if (this.analysisResult && this.analysisResult.documents && Object.keys(this.analysisResult.documents).length > 0) { - Object.entries(this.analysisResult.documents).forEach(([reference, docInfo]: [string, DocumentImportAnalysis]) => { - const finalAction = documentActions[reference] || docInfo.status; - const hasFiles = docInfo.associated_files && docInfo.associated_files.length > 0; - - // Only include references with PDFs - if (!hasFiles) { - return; - } + // Handle analysis data case (preferred) + } else if ( + this.analysisResult && + this.analysisResult.documents && + Object.keys(this.analysisResult.documents).length > 0 + ) { + Object.entries(this.analysisResult.documents).forEach( + ([reference, docInfo]: [string, DocumentImportAnalysis]) => { + const finalAction = documentActions[reference] || docInfo.status; + const hasFiles = docInfo.associated_files && docInfo.associated_files.length > 0; + + // Only include references with PDFs + if (!hasFiles) { + return; + } - // Only include documents that will be processed (add or update) - if (finalAction === "add" || finalAction === "update") { - // Ensure metadata is included in document_create - const documentCreate = { - ...docInfo.document_create, - metadata: { - source: "bib_import", - collections: [this.workspace?.name || "default"], - ...docInfo.document_create.metadata, - }, - }; + // Only include documents that will be processed (add or update) + if (finalAction === "add" || finalAction === "update") { + // Ensure metadata is included in document_create + const documentCreate = { + ...docInfo.document_create, + metadata: { + source: "bib_import", + collections: [this.workspace?.name || "default"], + ...docInfo.document_create.metadata, + }, + }; - confirmedDocuments[reference] = { - document_create: documentCreate, - associated_files: docInfo.associated_files || [], - }; + confirmedDocuments[reference] = { + document_create: documentCreate, + associated_files: docInfo.associated_files || [], + }; + } } - }); + ); } else if (this.dataframeData && this.dataframeData.data.length > 0) { this.dataframeData.data.forEach((row: Record) => { const reference = row.reference || row.key || `row_${Math.random()}`; - const finalAction = documentActions[reference] || 'add'; + const finalAction = documentActions[reference] || "add"; const filePaths = row.filePaths || []; const hasFiles = filePaths.length > 0; @@ -646,7 +647,7 @@ export default { document_create: { reference, title: row.title, - authors: Array.isArray(row.authors) ? row.authors : (row.authors ? [row.authors] : undefined), + authors: Array.isArray(row.authors) ? row.authors : row.authors ? [row.authors] : undefined, year: row.year ? String(row.year) : undefined, journal: row.journal, volume: row.volume, @@ -654,7 +655,7 @@ export default { doi: row.doi, url: row.url, abstract: row.abstract, - keywords: Array.isArray(row.keywords) ? row.keywords : (row.keywords ? [row.keywords] : undefined), + keywords: Array.isArray(row.keywords) ? row.keywords : row.keywords ? [row.keywords] : undefined, pmid: row.pmid, workspace_id: this.workspace?.id, metadata: { @@ -664,7 +665,7 @@ export default { }, associated_files: filePaths.map((filename: string) => ({ filename, - size: this.getFileSize(filename) || 0 + size: this.getFileSize(filename) || 0, })), }; } @@ -706,17 +707,18 @@ export default { initializeEditableTable() { const pdfFiles = this.allPdfFileNames as string[]; - this.editableTableData = pdfFiles.length > 0 - ? pdfFiles.map((filename: string) => ({ - reference: null, - title: null, - files: [filename], - })) - : Array.from({ length: 3 }, () => ({ - reference: null, - title: null, - files: [], - })); + this.editableTableData = + pdfFiles.length > 0 + ? pdfFiles.map((filename: string) => ({ + reference: null, + title: null, + files: [filename], + })) + : Array.from({ length: 3 }, () => ({ + reference: null, + title: null, + files: [], + })); this.emitUpdate(); }, @@ -779,9 +781,9 @@ export default { { name: "abstract", type: "string" }, { name: "file", type: "string" }, ], - primaryKey: ["reference"] + primaryKey: ["reference"], }, - data: dataRows + data: dataRows, }; } diff --git a/extralit-frontend/components/features/import/file-upload/CsvColumnSelection.vue b/extralit-frontend/components/features/import/file-upload/CsvColumnSelection.vue index 163e37ad5..b422db99a 100644 --- a/extralit-frontend/components/features/import/file-upload/CsvColumnSelection.vue +++ b/extralit-frontend/components/features/import/file-upload/CsvColumnSelection.vue @@ -11,7 +11,11 @@