1
0
mirror of https://gitlab.com/bramw/baserow.git synced 2024-11-24 16:36:46 +00:00
bramw_baserow/web-frontend/modules/database/components/table/TableJSONImporter.vue
2024-07-10 17:12:47 +02:00

230 lines
6.1 KiB
Vue

<template>
<div>
<div class="control">
<template v-if="filename === ''">
<label class="control__label control__label--small">{{
$t('tableJSONImporter.fileLabel')
}}</label>
<div class="control__description">
{{ $t('tableJSONImporter.fileDescription') }}
<pre>
[
{
"to": "Tove",
"from": "Jani",
"heading": "Reminder",
"body": "Don't forget me this weekend!"
},
{
"to": "Bram",
"from": "Nigel",
"heading": "Reminder",
"body": "Don't forget about the export feature this week"
}
]
</pre
>
</div>
</template>
<div class="control__elements">
<div class="file-upload">
<input
v-show="false"
ref="file"
type="file"
accept=".json"
@change="select($event)"
/>
<Button
type="upload"
size="large"
icon="iconoir-cloud-upload"
class="file-upload__button"
:loading="state !== null"
@click.prevent="$refs.file.click($event)"
>
{{ $t('tableJSONImporter.chooseButton') }}
</Button>
<div v-if="state === null" class="file-upload__file">
{{ filename }}
</div>
<template v-else>
<ProgressBar
:value="fileLoadingProgress"
:show-value="state === 'loading'"
:status="
state === 'loading' ? $t('importer.loading') : stateTitle
"
/>
</template>
</div>
<div v-if="$v.filename.$error" class="error">
{{ $t('error.requiredField') }}
</div>
</div>
</div>
<div v-if="filename !== ''" class="control margin-top-2">
<label class="control__label control__label--small">{{
$t('tableJSONImporter.encodingLabel')
}}</label>
<div class="control__elements">
<CharsetDropdown
v-model="encoding"
:disabled="isDisabled"
@input="reload()"
></CharsetDropdown>
</div>
</div>
<Alert v-if="error !== ''" type="error">
<template #title> {{ $t('common.wrong') }} </template>
{{ error }}
</Alert>
</div>
</template>
<script>
import { required } from 'vuelidate/lib/validators'
import form from '@baserow/modules/core/mixins/form'
import CharsetDropdown from '@baserow/modules/core/components/helpers/CharsetDropdown'
import importer from '@baserow/modules/database/mixins/importer'
export default {
name: 'TableCSVImporter',
components: { CharsetDropdown },
mixins: [form, importer],
data() {
return {
encoding: 'utf-8',
filename: '',
rawData: null,
}
},
validations: {
filename: { required },
},
computed: {
isDisabled() {
return this.disabled || this.state !== null
},
},
methods: {
/**
* Method that is called when a file has been chosen. It will check if the file is
* not larger than 15MB. Otherwise it will take a long time and possibly a crash
* if so many entries have to be loaded into memory. If the file is valid, the
* contents will be loaded into memory and the reload method will be called which
* parses the content.
*/
select(event) {
if (event.target.files.length === 0) {
return
}
const file = event.target.files[0]
const maxSize =
parseInt(this.$config.BASEROW_MAX_IMPORT_FILE_SIZE_MB, 10) * 1024 * 1024
if (file.size > maxSize) {
this.filename = ''
this.handleImporterError(
this.$t('tableJSONImporter.limitFileSize', {
limit: this.$config.BASEROW_MAX_IMPORT_FILE_SIZE_MB,
})
)
} else {
this.state = 'loading'
this.$emit('changed')
this.filename = file.name
const reader = new FileReader()
reader.addEventListener('progress', (event) => {
this.fileLoadingProgress = (event.loaded / event.total) * 100
})
reader.addEventListener('load', (event) => {
this.rawData = event.target.result
this.fileLoadingProgress = 100
this.reload()
})
reader.readAsArrayBuffer(event.target.files[0])
}
},
async reload() {
let json
this.resetImporterState()
try {
const decoder = new TextDecoder(this.encoding)
this.state = 'parsing'
await this.$ensureRender()
const decoded = decoder.decode(this.rawData)
await this.$ensureRender()
json = JSON.parse(decoded)
} catch (error) {
this.handleImporterError(
this.$t('tableJSONImporter.processingError', {
error: error.message,
})
)
return
}
if (json.length === 0) {
this.handleImporterError(this.$t('tableJSONImporter.emptyError'))
return
}
if (!Array.isArray(json)) {
this.handleImporterError(this.$t('tableJSONImporter.arrayError'))
return
}
const limit = this.$config.INITIAL_TABLE_DATA_LIMIT
if (limit !== null && json.length > limit - 1) {
this.handleImporterError(
this.$t('tableJSONImporter.limitError', {
limit,
})
)
return
}
const header = []
const data = []
await this.$ensureRender()
json.forEach((entry) => {
const keys = Object.keys(entry)
const row = []
keys.forEach((key) => {
if (!header.includes(key)) {
header.push(key)
}
})
header.forEach((key) => {
const exists = Object.prototype.hasOwnProperty.call(entry, key)
const value = exists ? entry[key] : ''
row.push(value)
})
data.push(row)
})
const preparedHeader = this.prepareHeader(header, data)
const getData = () => {
return data
}
this.state = null
const previewData = this.getPreview(header, data)
this.$emit('getData', getData)
this.$emit('data', { header: preparedHeader, previewData })
},
},
}
</script>