Browse Source

wip(gui-v2): more actions draft

pull/2838/head
Wing-Kam Wong 2 years ago
parent
commit
b48b0afb98
  1. 367
      packages/nc-gui-v2/components/smartsheet-toolbar/MoreActions.vue

367
packages/nc-gui-v2/components/smartsheet-toolbar/MoreActions.vue

@ -1,324 +1,91 @@
<script>
import FileSaver from 'file-saver'
import { ExportTypes } from 'nocodb-sdk'
import { NOCO } from '~/lib/constants'
import DropOrSelectFileModal from '~/components/import/DropOrSelectFileModal'
import ColumnMappingModal from '~/components/project/spreadsheet/components/importExport/ColumnMappingModal'
import CSVTemplateAdapter from '~/components/import/templateParsers/CSVTemplateAdapter'
import { UITypes } from '~/components/project/spreadsheet/helpers/uiTypes'
import WebhookModal from '~/components/project/tableTabs/webhook/WebhookModal'
import WebhookSlider from '~/components/project/tableTabs/webhook/WebhookSlider'
export default {
name: 'ExportImport',
components: {
WebhookSlider,
ColumnMappingModal,
DropOrSelectFileModal,
},
props: {
meta: Object,
nodes: Object,
selectedView: Object,
publicViewId: String,
queryParams: Object,
isView: Boolean,
reqPayload: Object,
},
emits: ['reload', 'showAdditionalFeatOverlay'],
data() {
return {
importModal: false,
columnMappingModal: false,
parsedCsv: {},
webhookModal: false,
}
},
methods: {
async onCsvFileSelection(file) {
const reader = new FileReader()
reader.onload = async (e) => {
const templateGenerator = new CSVTemplateAdapter(file.name, e.target.result)
await templateGenerator.init()
templateGenerator.parseData()
this.parsedCsv.columns = templateGenerator.getColumns()
this.parsedCsv.data = templateGenerator.getData()
this.columnMappingModal = true
this.importModal = false
}
reader.readAsText(file)
},
async extractCsvData() {
return Promise.all(
this.data.map(async (r) => {
const row = {}
for (const col of this.availableColumns) {
if (col.virtual) {
let prop, cn
if (col.mm || (col.lk && col.lk.type === 'mm')) {
const tn = col.mm ? col.mm.rtn : col.lk.ltn
const title = col.mm ? col.mm._rtn : col.lk._ltn
await this.$store.dispatch('meta/ActLoadMeta', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn,
})
prop = `${title}MMList`
cn = col.lk
? col.lk._lcn
: (
this.$store.state.meta.metas[tn].columns.find((c) => c.pv) ||
this.$store.state.meta.metas[tn].columns.find((c) => c.pk) ||
{}
).title
row[col.title] = r.row[prop] && r.row[prop].map((r) => cn && r[cn])
} else if (col.hm || (col.lk && col.lk.type === 'hm')) {
const tn = col.hm ? col.hm.table_name : col.lk.ltn
const title = col.hm ? col.hm.title : col.lk._ltn
await this.$store.dispatch('meta/ActLoadMeta', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn,
})
prop = `${title}List`
cn = col.lk
? col.lk._lcn
: (
this.$store.state.meta.metas[tn].columns.find((c) => c.pv) ||
this.$store.state.meta.metas[tn].columns.find((c) => c.pk)
).title
row[col.title] = r.row[prop] && r.row[prop].map((r) => cn && r[cn])
} else if (col.bt || (col.lk && col.lk.type === 'bt')) {
const tn = col.bt ? col.bt.rtn : col.lk.ltn
const title = col.bt ? col.bt._rtn : col.lk._ltn
await this.$store.dispatch('meta/ActLoadMeta', {
env: this.nodes.env,
dbAlias: this.nodes.dbAlias,
tn,
})
<script setup lang="ts">
import MdiFlashOutlineIcon from '~icons/mdi/flash-outline'
import MdiDownloadOutlineIcon from '~icons/mdi/download-outline'
import MdiUploadOutlineIcon from '~icons/mdi/upload-outline'
import MdiViewListOutlineIcon from '~icons/mdi/view-list-outline'
import MdiHookIcon from '~icons/mdi/hook'
const { isUIAllowed } = useUIPermission()
// TODO:: identify based on meta
const isView = ref(false)
function exportCsv() {
// TODO
}
prop = `${title}Read`
cn = col.lk
? col.lk._lcn
: (
this.$store.state.meta.metas[tn].columns.find((c) => c.pv) ||
this.$store.state.meta.metas[tn].columns.find((c) => c.pk) ||
{}
).title
row[col.title] = r.row[prop] && r.row[prop] && cn && r.row[prop][cn]
} else {
row[col.title] = r.row[col.title]
}
} else if (col.uidt === 'Attachment') {
let data = []
try {
if (typeof r.row[col.title] === 'string') {
data = JSON.parse(r.row[col.title])
} else if (r.row[col.title]) {
data = r.row[col.title]
}
} catch {}
row[col.title] = (data || []).map((a) => `${a.title}(${a.url})`)
} else {
row[col.title] = r.row[col.title]
}
}
return row
}),
)
},
async exportCsv() {
let offset = 0
let c = 1
function importCsv() {
// TODO
}
try {
while (!isNaN(offset) && offset > -1) {
let res
if (this.publicViewId) {
res = await this.$api.public.csvExport(this.publicViewId, ExportTypes.CSV, {
responseType: 'blob',
query: {
fields:
this.queryParams &&
this.queryParams.fieldsOrder &&
this.queryParams.fieldsOrder.filter((c) => this.queryParams.showFields[c]),
offset,
sortArrJson: JSON.stringify(
this.reqPayload &&
this.reqPayload.sorts &&
this.reqPayload.sorts.map(({ fk_column_id, direction }) => ({
direction,
fk_column_id,
})),
),
filterArrJson: JSON.stringify(this.reqPayload && this.reqPayload.filters),
},
headers: {
'xc-password': this.reqPayload && this.reqPayload.password,
},
})
} else {
res = await this.$api.dbViewRow.export(
NOCO,
this.projectName,
this.meta.title,
this.selectedView.title,
ExportTypes.CSV,
{
responseType: 'blob',
query: {
offset,
},
},
)
}
const { data } = res
function openSharedViewModal() {
// TODO:
}
offset = +res.headers['nc-export-offset']
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
FileSaver.saveAs(blob, `${this.meta.title}_exported_${c++}.csv`)
if (offset > -1) {
this.$toast.info('Downloading more files').goAway(3000)
} else {
this.$toast.success('Successfully exported all table data').goAway(3000)
}
}
} catch (e) {
console.log(e)
this.$toast.error(e.message).goAway(3000)
}
},
async importData(columnMappings) {
try {
const data = this.parsedCsv.data
for (let i = 0, progress = 0; i < data.length; i += 500) {
const batchData = data.slice(i, i + 500).map((row) =>
columnMappings.reduce((res, col) => {
// todo: parse data
if (col.enabled && col.destCn) {
const v = this.meta && this.meta.columns.find((c) => c.title === col.destCn)
let input = row[col.sourceCn]
// parse potential boolean values
if (v.uidt === UITypes.Checkbox) {
input = input.replace(/["']/g, '').toLowerCase().trim()
if (input === 'false' || input === 'no' || input === 'n') {
input = '0'
} else if (input === 'true' || input === 'yes' || input === 'y') {
input = '1'
}
} else if (v.uidt === UITypes.Number) {
if (input === '') {
input = null
}
} else if (v.uidt === UITypes.SingleSelect || v.uidt === UITypes.MultiSelect) {
if (input === '') {
input = null
}
}
res[col.destCn] = input
}
return res
}, {}),
)
await this.$api.dbTableRow.bulkCreate(NOCO, this.projectName, this.meta.title, batchData)
progress += batchData.length
this.$store.commit('loader/MutMessage', `Importing data : ${progress}/${data.length}`)
this.$store.commit('loader/MutProgress', Math.round((100 * progress) / data.length))
}
this.columnMappingModal = false
this.$store.commit('loader/MutClear')
this.$emit('reload')
this.$toast.success('Successfully imported table data').goAway(3000)
} catch (e) {
this.$toast.error(e.message).goAway(3000)
}
},
},
function openWebhookModal() {
// TODO:
}
</script>
<template>
<div>
<v-menu open-on-hover bottom offset-y transition="slide-y-transition">
<template #activator="{ on }">
<v-btn
v-t="['c:actions']"
outlined
class="nc-actions-menu-btn caption px-2 nc-remove-border font-weight-medium"
small
text
v-on="on"
>
<v-icon small color="#777"> mdi-flash-outline </v-icon>
<!-- More -->
<a-menu offset-y transition="slide-y-transition" mode="horizontal">
<a-sub-menu key="sub1">
<template #icon>
<setting-outlined />
</template>
<template #title>
<span class="flex items-center gap-2">
<MdiFlashOutlineIcon />
{{ $t('general.more') }}
<v-icon small color="#777"> mdi-menu-down </v-icon>
</v-btn>
</span>
</template>
<v-list dense>
<v-list-item v-t="['a:actions:download-csv']" dense @click="exportCsv">
<v-list-item-title>
<v-icon small class="mr-1"> mdi-download-outline </v-icon>
<span class="caption">
<a-menu-item key="action:downloadCSV" v-t="['c:actions']" @click="exportCsv">
<span class="flex items-center gap-2">
<MdiDownloadOutlineIcon class="text-primary" />
<!-- Download as CSV -->
{{ $t('activity.downloadCSV') }}
</span>
</v-list-item-title>
</v-list-item>
<v-list-item v-if="_isUIAllowed('csvImport') && !isView" v-t="['a:actions:upload-csv']" dense @click="importModal = true">
<v-list-item-title>
<v-icon small class="mr-1" color=""> mdi-upload-outline </v-icon>
<span class="caption">
</a-menu-item>
<a-menu-item
v-if="isUIAllowed('csvImport') && !isView"
key="action:uploadCSV"
v-t="['a:actions:upload-csv']"
@click="importCsv"
>
<span class="flex items-center gap-2">
<MdiUploadOutlineIcon class="text-primary" />
<!-- Upload CSV -->
{{ $t('activity.uploadCSV') }}
</span>
</a-menu-item>
<span class="caption grey--text">(<x-icon small color="grey lighten-2"> mdi-alpha </x-icon> version)</span>
</v-list-item-title>
</v-list-item>
<v-list-item
v-if="_isUIAllowed('SharedViewList') && !isView"
<a-menu-item
v-if="isUIAllowed('SharedViewList') && !isView"
key="action:listSharedView"
v-t="['a:actions:shared-view-list']"
dense
@click="$emit('showAdditionalFeatOverlay', 'shared-views')"
@click="openSharedViewModal"
>
<v-list-item-title>
<v-icon small class="mr-1" color=""> mdi-view-list-outline </v-icon>
<span class="caption">
<span class="flex items-center gap-2">
<MdiViewListOutlineIcon class="text-primary" />
<!-- Shared View List -->
{{ $t('activity.listSharedView') }}
</span>
</v-list-item-title>
</v-list-item>
<v-list-item v-if="_isUIAllowed('webhook') && !isView" v-t="['c:actions:webhook']" dense @click="webhookModal = true">
<v-list-item-title>
<v-icon small class="mr-1" color=""> mdi-hook </v-icon>
<span class="caption"> Webhooks </span>
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<DropOrSelectFileModal v-model="importModal" accept=".csv" text="CSV" @file="onCsvFileSelection" />
<ColumnMappingModal
v-if="columnMappingModal && meta"
v-model="columnMappingModal"
:meta="meta"
:import-data-columns="parsedCsv.columns"
:parsed-csv="parsedCsv"
@import="importData"
/>
<!-- <webhook-modal v-if="webhookModal" v-model="webhookModal" :meta="meta" /> -->
<WebhookSlider v-model="webhookModal" :meta="meta" />
</div>
</a-menu-item>
<a-menu-item
v-if="isUIAllowed('webhook') && !isView"
key="action:webhooks"
v-t="['c:actions:webhook']"
@click="openWebhookModal"
>
<span class="flex items-center gap-2">
<MdiHookIcon class="text-primary" />
<!-- TODO: i18n -->
Webhooks
</span>
</a-menu-item>
</a-sub-menu>
</a-menu>
</template>
<style scoped></style>

Loading…
Cancel
Save