Skip to content

Commit

Permalink
feat: implement direct file upload
Browse files Browse the repository at this point in the history
Signed-off-by: Luka Trovic <luka@nextcloud.com>
  • Loading branch information
luka-nextcloud committed Feb 20, 2024
1 parent 441ff89 commit a6389b3
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 50 deletions.
4 changes: 2 additions & 2 deletions cypress/e2e/tables-import.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('Import csv', () => {
cy.uploadFile('test-import.csv', 'text/csv')
cy.loadTable('Tutorial')
cy.clickOnTableThreeDotMenu('Import')
cy.get('.modal__content button').contains('Select a file').click()
cy.get('.modal__content button').contains('Select from Files').click()
cy.get('.file-picker__files').contains('test-import').click()
cy.get('.file-picker button span').contains('Choose test-import.csv').click()
cy.get('.modal__content button').contains('Import').click()
Expand All @@ -32,7 +32,7 @@ describe('Import csv', () => {
it('Import csv with upload file button', () => {
cy.loadTable('Tutorial')
cy.clickOnTableThreeDotMenu('Import')
cy.get('.modal__content button').contains('Upload a file').click()
cy.get('.modal__content button').contains('Upload from device').click()
cy.get('input[type="file"]').selectFile('cypress/fixtures/test-import.csv', { force: true })
cy.get('.modal__content button').contains('Import').click()
cy.get('[data-cy="importResultColumnsFound"]').should('contain.text', '4')
Expand Down
2 changes: 1 addition & 1 deletion lib/Service/ImportService.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function import(?int $tableId, ?int $viewId, string $path, bool $createMi
$error = true;
}
}
elseif (file_exists($path)) {
elseif (\file_exists($path)) {
$spreadsheet = IOFactory::load($path);
$this->loop($spreadsheet->getActiveSheet());
} else {
Expand Down
124 changes: 77 additions & 47 deletions src/modules/modals/Import.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,49 @@
<div class="modal__content">
<div class="row">
<div class="col-4">
<h2>{{ t('tables', 'Import') }}</h2>
<h2>{{ t('tables', 'Import table') }}</h2>
</div>
</div>

<!-- Starting -->
<div v-if="!loading && result === null && !waitForReload">
<RowFormWrapper :title="t('tables', 'File')" :description="t('tables', 'Supported formats are xlsx, xls, html, xml and csv.')">
<div class="fix-col-4 space-T-small middle">
<NcButton :aria-label="t('tables', 'Select a file')" @click="pickFile">
<div class="row space-T">
{{ t('tables', 'Add data to the table from a file') }}
</div>
<RowFormWrapper>
<div v-if="importFileName.length" class="import-filename">
<IconFile :size="20" />{{ importFileName }}
</div>
<div class="fix-col-4 middle">
<NcButton :aria-label="t('tables', 'Select from Files')" @click="pickFile">
<template #icon>
<IconFolder :size="20" />
</template>
{{ t('tables', 'Select a file') }}
{{ t('tables', 'Select from Files') }}
</NcButton>
<input v-model="path" :class="{ missing: pathError }" :disabled="!!selectedUploadFile">
</div>

<div class="fix-col-4 space-T-small middle">
<NcButton :aria-label="t('tables', 'Upload a file')" @click="selectUploadFile">
<NcButton :aria-label="t('tables', 'Upload from device')" @click="selectUploadFile">
<template #icon>
<IconUpload :size="20" />
</template>
{{ t('tables', 'Upload a file') }}
{{ t('tables', 'Upload from device') }}
</NcButton>
<input ref="uploadFileInput"
type="file"
aria-hidden="true"
class="hidden-visually"
:accept="mimeTypes.join(',')"
@change="onUploadFileInputChange">
<input :value="selectedUploadFile ? selectedUploadFile.name : ''" disabled>
</div>
<div class="fix-col-4">
<p class="span">
{{ t('tables', 'Supported formats: xlsx, xls, csv, html, xml') }}
<br>
{{ t('tables', 'The first row if the file must contain column headings.') }}
</p>
</div>
</RowFormWrapper>

<RowFormWrapper :title="t('tables', 'Create missing columns')" :description="t('tables', 'Columns are identified by the titles. If there is no match, a new text-line column will be created.')">
<div class="row">
<div class="fix-col-2">
<NcCheckboxRadioSwitch :checked.sync="createMissingColumns" type="switch" :disabled="!canCreateMissingColumns">
{{ t('tables', 'Create missing columns') }}
Expand All @@ -46,26 +54,6 @@
<p v-if="(isElementView && !canManageTable(element)) || !canManageElement(element)" class="fix-col-2 span">
{{ t('tables', '⚠️ You don\'t have the permission to create columns.') }}
</p>
</RowFormWrapper>

<div class="information">
<RowFormWrapper :title="t('tables', 'Information')">
<div class="fix-col-4">
<p class="span">
{{ t('tables', 'The first row has to contain the column titles.') }}
</p>
</div>
<div class="fix-col-4">
<p class="span">
{{ t('tables', 'Note that imported data will be added to the table. Updating of existing rows is not possible at the moment.') }}
</p>
</div>
<div class="fix-col-4">
<p class="span">
{{ t('tables', 'The possible importing size depends on the system configuration and is only limited by execution time and memory.') }}
</p>
</div>
</RowFormWrapper>
</div>

<div class="row">
Expand All @@ -79,7 +67,14 @@

<!-- show results -->
<div v-if="!loading && result !== null && !waitForReload">
<RowFormWrapper v-if="result !== ''" :title="t('tables', 'Result')">
<RowFormWrapper v-if="result !== ''">
<div>
<h3 class="result-headline">
<IconCheck v-if="!result['errors_parsing_count'] && !result['errors_count']" :size="15" />
<IconAlert v-else :size="15" />
{{ t('tables', 'Imported from ') + importFileName }}
</h3>
</div>
<div class="fix-col-1">
{{ t('tables', 'Found columns') }}
</div>
Expand All @@ -104,18 +99,22 @@
<div class="fix-col-3" data-cy="importResultRowsInserted">
{{ result['inserted_rows_count'] }}
</div>
<div class="fix-col-1">
{{ t('tables', 'Value parsing errors') }}
</div>
<div class="fix-col-3" data-cy="importResultParsingErrors">
{{ result['errors_parsing_count'] }}
</div>
<div class="fix-col-1">
{{ t('tables', 'Row creation errors') }}
</div>
<div class="fix-col-3" data-cy="importResultRowErrors">
{{ result['errors_count'] }}
</div>
<template v-if="result['errors_parsing_count'] || result['errors_count']">
<div class="fix-col-1">
{{ t('tables', 'Value parsing errors') }}
</div>
<div class="fix-col-3 errors-count" data-cy="importResultParsingErrors">
{{ result['errors_parsing_count'] }}
<IconAlert :size="15" />
</div>
<div class="fix-col-1">
{{ t('tables', 'Row creation errors') }}
</div>
<div class="fix-col-3 errors-count" data-cy="importResultRowErrors">
{{ result['errors_count'] }}
<IconAlert :size="15" />
</div>
</template>
</RowFormWrapper>

<RowFormWrapper v-else :title="t('tables', 'Result')">
Expand All @@ -133,7 +132,7 @@

<!-- show loading -->
<div v-if="loading && !waitForReload">
<NcEmptyContent :name="t('tables', 'Importing...')" :description="t('tables', 'Please wait while we try our best to import your data. This might take some time, depending on the server configuration.')">
<NcEmptyContent :name="t('tables', 'Importing data from ') + importFileName" :description="t('tables', 'This might take a while...')">
<template #icon>
<NcIconTimerSand />
</template>
Expand All @@ -154,6 +153,9 @@ import RowFormWrapper from '../../shared/components/ncTable/partials/rowTypePart
import permissionsMixin from '../../shared/components/ncTable/mixins/permissionsMixin.js'
import IconFolder from 'vue-material-design-icons/Folder.vue'
import IconUpload from 'vue-material-design-icons/Upload.vue'
import IconFile from 'vue-material-design-icons/File.vue'
import IconCheck from 'vue-material-design-icons/Check.vue'
import IconAlert from 'vue-material-design-icons/Alert.vue'
import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
import { mapGetters } from 'vuex'
Expand All @@ -166,6 +168,9 @@ export default {
NcLoadingIcon,
IconFolder,
IconUpload,
IconFile,
IconCheck,
IconAlert,
NcModal,
NcButton,
NcCheckboxRadioSwitch,
Expand Down Expand Up @@ -218,6 +223,9 @@ export default {
getCreateMissingColumns() {
return this.canCreateMissingColumns && this.createMissingColumns
},
importFileName() {
return this.selectedUploadFile ? this.selectedUploadFile.name : this.path
},
},
watch: {
element() {
Expand Down Expand Up @@ -411,4 +419,26 @@ export default {
padding-top: calc(var(--default-grid-baseline) * 2);
}
.import-filename {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 12px;
padding-left: 12px;
padding-bottom: 16px;
}
.result-headline {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font-size: medium;
}
.errors-count {
display: flex;
gap: 4px;
}
</style>

0 comments on commit a6389b3

Please sign in to comment.