Browse Source

refactor: template editor ui, template editor modal toggling

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/789/head
Pranav C 3 years ago
parent
commit
9c4a212fbc
  1. 64
      packages/nc-gui/components/import/excelImport.vue
  2. 6
      packages/nc-gui/components/import/templateParsers/ExcelTemplateAdapter.js
  3. 31
      packages/nc-gui/components/templates/createProjectFromTemplateBtn.vue
  4. 120
      packages/nc-gui/components/templates/editor.vue

64
packages/nc-gui/components/import/excelImport.vue

@ -1,3 +1,11 @@
<script>
import CreateProjectFromTemplateBtn from '~/components/templates/createProjectFromTemplateBtn'
import TemplateEditor from '~/components/templates/editor'
export default {
components: { TemplateEditor, CreateProjectFromTemplateBtn }
}
</script>
<template> <template>
<div class="pt-10"> <div class="pt-10">
<v-dialog v-model="dropOrUpload" max-width="600"> <v-dialog v-model="dropOrUpload" max-width="600">
@ -21,7 +29,7 @@
<div <div
class="nc-droppable d-flex align-center justify-center flex-column" class="nc-droppable d-flex align-center justify-center flex-column"
:style="{ :style="{
background : dragOver ? '#7774' : '' background : dragOver ? '#7772' : ''
}" }"
@click="$refs.file.click()" @click="$refs.file.click()"
@drop.prevent="dropHandler" @drop.prevent="dropHandler"
@ -76,7 +84,7 @@
<div class="d-flex"> <div class="d-flex">
<v-spacer /> <v-spacer />
<span class="caption pointer grey--text" @click="showMore = !showMore">{{ showMore ? 'Hide' : 'Show' }} more <span class="caption pointer grey--text" @click="showMore = !showMore">{{ showMore ? 'Hide' : 'Show' }} more
<v-icon small>mdi-menu-{{ showMore ? 'up':'down' }}</v-icon> <v-icon small>mdi-menu-{{ showMore ? 'up' : 'down' }}</v-icon>
</span> </span>
</div> </div>
<div class="mb-2 pt-2 nc-excel-import-options" :style="{ maxHeight: showMore ? '100px' : '0'}"> <div class="mb-2 pt-2 nc-excel-import-options" :style="{ maxHeight: showMore ? '100px' : '0'}">
@ -124,15 +132,24 @@
<span class="caption">Create template from Excel</span> <span class="caption">Create template from Excel</span>
</v-tooltip> </v-tooltip>
<v-dialog v-if="templateData" :value="true"> <v-dialog v-if="templateData" v-model="templateEditorModal" max-width="1000">
<v-card> <v-card>
<template-editor :project-template.sync="templateData"> <template-editor :project-template.sync="templateData" excel-import>
<template #toolbar> <template #toolbar="{valid}">
<h3 class="mt-2 grey--text">
Import Excel as Project : {{ filename }}
</h3>
<v-spacer /> <v-spacer />
<create-project-from-template-btn <create-project-from-template-btn
:template-data="templateData" :template-data="templateData"
:import-data="importData" :import-data="importData"
/> :valid="valid"
create-gql-text="Import as GQL Project"
create-rest-text="Import as REST Project"
>
Import Excel
</create-project-from-template-btn>
</template> </template>
</template-editor> </template-editor>
</v-card> </v-card>
@ -150,13 +167,14 @@ import ExcelTemplateAdapter from '~/components/import/templateParsers/ExcelTempl
export default { export default {
name: 'ExcelImport', name: 'ExcelImport',
components: { CreateProjectFromTemplateBtn, TemplateEditor }, components: {CreateProjectFromTemplateBtn, TemplateEditor},
props: { props: {
hideLabel: Boolean, hideLabel: Boolean,
value: Boolean value: Boolean
}, },
data() { data() {
return { return {
templateEditorModal:false,
valid: null, valid: null,
templateData: null, templateData: null,
importData: null, importData: null,
@ -165,7 +183,8 @@ export default {
showMore: false, showMore: false,
parserConfig: { parserConfig: {
maxRowsToParse: 500 maxRowsToParse: 500
} },
filename: ''
} }
}, },
computed: { computed: {
@ -187,10 +206,11 @@ export default {
this.$refs.file.click() this.$refs.file.click()
}, },
_change(file) { _change(event) {
const files = file.target.files const files = event.target.files
if (files && files[0]) { if (files && files[0]) {
this._file(files[0]) this._file(files[0])
event.target.value = ''
} }
}, },
async _file(file) { async _file(file) {
@ -199,11 +219,10 @@ export default {
const int = setInterval(() => { const int = setInterval(() => {
this.$store.commit('loader/MutMessage', `Loading excel file${'.'.repeat(++i % 4)}`) this.$store.commit('loader/MutMessage', `Loading excel file${'.'.repeat(++i % 4)}`)
}, 1000) }, 1000)
this.dropOrUpload = false this.dropOrUpload = false
const reader = new FileReader() const reader = new FileReader()
this.filename = file.name
reader.onload = async(e) => { reader.onload = async (e) => {
const ab = e.target.result const ab = e.target.result
await this.parseAndExtractData('file', ab, file.name) await this.parseAndExtractData('file', ab, file.name)
this.$store.commit('loader/MutMessage', null) this.$store.commit('loader/MutMessage', null)
@ -216,7 +235,8 @@ export default {
} }
reader.addEventListener('progress', handleEvent) reader.addEventListener('progress', handleEvent)
reader.onerror = () => { reader.onerror = (e) => {
console.log('error', e)
this.$store.commit('loader/MutClear') this.$store.commit('loader/MutClear')
} }
reader.readAsArrayBuffer(file) reader.readAsArrayBuffer(file)
@ -236,9 +256,11 @@ export default {
templateGenerator.parse() templateGenerator.parse()
this.templateData = templateGenerator.getTemplate() this.templateData = templateGenerator.getTemplate()
this.importData = templateGenerator.getData() this.importData = templateGenerator.getData()
this.templateEditorModal = true;
}, },
dropHandler(ev) { dropHandler(ev) {
this.dragOver = false;
console.log('File(s) dropped') console.log('File(s) dropped')
let file let file
if (ev.dataTransfer.items) { if (ev.dataTransfer.items) {
@ -250,12 +272,12 @@ export default {
file = ev.dataTransfer.files[0] file = ev.dataTransfer.files[0]
} }
if (file.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' && file.type !== 'application/vnd.ms-excel') { if (!file) return
return this.$toast.error('Dropped file is not an accepted file type. The accepted file types are .xlsx,.xls!').goAway(3000)
} if (!/\.xls[xm]?$/.test(file.name)) {
if (file) { return this.$toast.error('Dropped file is not an accepted file type. The accepted file types are .xlsx,.xls,.xlsm!').goAway(3000)
this._file(file)
} }
this._file(file)
}, },
dragOverHandler(ev) { dragOverHandler(ev) {
console.log('File(s) in drop zone') console.log('File(s) in drop zone')
@ -303,8 +325,8 @@ export default {
width: 100%; width: 100%;
} }
.nc-excel-import-options{ .nc-excel-import-options {
transition:.4s max-height; transition: .4s max-height;
overflow: hidden; overflow: hidden;
} }
</style> </style>

6
packages/nc-gui/components/import/templateParsers/ExcelTemplateAdapter.js

@ -105,12 +105,12 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
} }
} }
} else if (column.uidt === UITypes.Number) { } else if (column.uidt === UITypes.Number) {
if (rows.slice(1, 500).some((v) => { if (rows.slice(1, this.config.maxRowsToParse).some((v) => {
return v && v[col] && parseInt(+v[col]) !== +v[col] return v && v[col] && parseInt(+v[col]) !== +v[col]
})) { })) {
column.uidt = UITypes.Decimal column.uidt = UITypes.Decimal
} }
if (rows.slice(1, 500).every((v, i) => { if (rows.slice(1, this.config.maxRowsToParse).every((v, i) => {
const cellId = XLSX.utils.encode_cell({ const cellId = XLSX.utils.encode_cell({
c: range.s.c + col, c: range.s.c + col,
r: i + 2 r: i + 2
@ -123,7 +123,7 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
column.uidt = UITypes.Currency column.uidt = UITypes.Currency
} }
} else if (column.uidt === UITypes.DateTime) { } else if (column.uidt === UITypes.DateTime) {
if (rows.slice(1, 500).every((v, i) => { if (rows.slice(1, this.config.maxRowsToParse).every((v, i) => {
const cellId = XLSX.utils.encode_cell({ const cellId = XLSX.utils.encode_cell({
c: range.s.c + col, c: range.s.c + col,
r: i + 2 r: i + 2

31
packages/nc-gui/components/templates/createProjectFromTemplateBtn.vue

@ -9,7 +9,7 @@
x-large x-large
v-on="on" v-on="on"
> >
Use template <slot>Use template</slot>
<v-icon>mdi-menu-down</v-icon> <v-icon>mdi-menu-down</v-icon>
</v-btn> </v-btn>
</template> </template>
@ -19,7 +19,7 @@
<v-icon class="mr-1" :color="textColors[7]"> <v-icon class="mr-1" :color="textColors[7]">
mdi-code-json mdi-code-json
</v-icon> </v-icon>
Create REST Project {{ createRestText }}
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item dense class="py-2" @click="useTemplate('graphql')"> <v-list-item dense class="py-2" @click="useTemplate('graphql')">
@ -27,7 +27,7 @@
<v-icon class="mr-1" :color="textColors[3]"> <v-icon class="mr-1" :color="textColors[3]">
mdi-graphql mdi-graphql
</v-icon> </v-icon>
Create GQL Project {{ createGqlText }}
</v-list-item-title> </v-list-item-title>
</v-list-item> </v-list-item>
</v-list> </v-list>
@ -44,7 +44,23 @@ export default {
props: { props: {
loading: Boolean, loading: Boolean,
templateData: [Array, Object], templateData: [Array, Object],
importData: [Array, Object] importData: [Array, Object],
valid: {
default: true,
type: Boolean
},
validationErrorMsg: {
default: 'Please fill all the required values',
type: String
},
createGqlText: {
default: 'Create GQL Project',
type: String
},
createRestText: {
default: 'Create REST Project',
type: String
}
}, },
data() { data() {
return { return {
@ -76,6 +92,10 @@ export default {
}, },
methods: { methods: {
async useTemplate(projectType) { async useTemplate(projectType) {
if (!this.valid) {
return this.$toast.error(this.validationErrorMsg).goAway(3000)
}
// this.$emit('useTemplate', type) // this.$emit('useTemplate', type)
this.projectCreation = true this.projectCreation = true
@ -118,7 +138,8 @@ export default {
// this.$store.commit('project/MutProjectId', projectId) // this.$store.commit('project/MutProjectId', projectId)
this.$ncApis.setProjectId(projectId) this.$ncApis.setProjectId(projectId)
let total = 0; let progress = 0 let total = 0
let progress = 0
await Promise.all(Object.entries(this.importData).map(v => (async([table, data]) => { await Promise.all(Object.entries(this.importData).map(v => (async([table, data]) => {
await this.$store.dispatch('meta/ActLoadMeta', { await this.$store.dispatch('meta/ActLoadMeta', {

120
packages/nc-gui/components/templates/editor.vue

@ -1,7 +1,7 @@
<template> <template>
<div class="h-100"> <div class="h-100">
<v-toolbar v-if="!viewMode" class="elevation-0"> <v-toolbar v-if="!viewMode" class="elevation-0">
<slot name="toolbar"> <slot name="toolbar" :valid="valid">
<!-- <v-text-field <!-- <v-text-field
v-model="url" v-model="url"
clearable clearable
@ -87,7 +87,7 @@
</slot> </slot>
</v-toolbar> </v-toolbar>
<v-container class="text-center" style="height:calc(100% - 64px);overflow-y: auto"> <v-container class="text-center" style="height:calc(100% - 64px);overflow-y: auto">
<v-form ref="form"> <v-form ref="form" v-model="valid">
<v-row fluid class="justify-center"> <v-row fluid class="justify-center">
<v-col cols="12"> <v-col cols="12">
<v-card> <v-card>
@ -130,7 +130,7 @@
<v-spacer /> <v-spacer />
<v-icon v-if="!viewMode" class="flex-grow-0 mr-2" small color="grey" @click.stop="deleteTable(i)"> <v-icon v-if="!viewMode" class="flex-grow-0 mr-2" small color="grey" @click.stop="deleteTable(i)">
mdi-delete mdi-delete-outline
</v-icon> </v-icon>
</v-expansion-panel-header> </v-expansion-panel-header>
<v-expansion-panel-content> <v-expansion-panel-content>
@ -144,10 +144,10 @@
<v-simple-table v-if="table.columns.length" dense class="my-4"> <v-simple-table v-if="table.columns.length" dense class="my-4">
<thead> <thead>
<tr> <tr>
<th class="caption text-left pa-3"> <th class="caption text-left pa-1">
Column Name Column Name
</th> </th>
<th class="caption text-left pa-3" colspan="4"> <th class="caption text-left pa-1" colspan="4">
Column Type Column Type
</th> </th>
<th /> <th />
@ -157,7 +157,7 @@
</thead> </thead>
<tbody> <tbody>
<tr v-for="(col,j) in table.columns" :key="j" :data-exp="i"> <tr v-for="(col,j) in table.columns" :key="j" :data-exp="i">
<td class="pa-3 text-left" :style="{width:viewMode ? '33%' : ''}"> <td class="pa-1 text-left" :style="{width:viewMode ? '33%' : '15%'}">
<span v-if="viewMode" class="body-1 "> <span v-if="viewMode" class="body-1 ">
{{ col.cn }} {{ col.cn }}
</span> </span>
@ -225,12 +225,12 @@
<template v-else> <template v-else>
<td <td
class="pa-3 text-left" class="pa-1 text-left"
:colspan="isLookupOrRollup(col) || isRelation(col) || isSelect(col) ? (isRollup(col)? style="width:200px;max-width:200px"
1 :2) : 4"
> >
<v-autocomplete <v-autocomplete
:ref="`uidt_${table.tn}_${j}`" :ref="`uidt_${table.tn}_${j}`"
style="max-width: 200px"
:value="col.uidt" :value="col.uidt"
placeholder="Column Datatype" placeholder="Column Datatype"
outlined outlined
@ -253,8 +253,8 @@
</v-chip> </v-chip>
<span v-else class="caption">{{ name }}</span> <span v-else class="caption">{{ name }}</span>
</template> </template>
<template #selection="{item:{name}}"> <template #selection="{item:{name}} ">
<v-chip v-if="colors[name]" :color="colors[name]" small> <v-chip v-if="colors[name]" :color="colors[name]" small style="max-width: 100px">
{{ name }} {{ name }}
</v-chip> </v-chip>
<span v-else class="caption">{{ name }}</span> <span v-else class="caption">{{ name }}</span>
@ -265,7 +265,7 @@
<template <template
v-if="isRelation(col) || isLookupOrRollup(col)" v-if="isRelation(col) || isLookupOrRollup(col)"
> >
<td class="pa-3 text-left"> <td class="pa-1 text-left">
<v-autocomplete <v-autocomplete
:value="col.rtn" :value="col.rtn"
placeholder="Related table" placeholder="Related table"
@ -282,7 +282,7 @@
/> />
</td> </td>
<td v-if="isRelation(col)" class="pa-3"> <td v-if="isRelation(col)" class="pa-1">
<template v-if="col.uidt !== 'ForeignKey'"> <template v-if="col.uidt !== 'ForeignKey'">
<span <span
v-if="viewMode" v-if="viewMode"
@ -304,7 +304,7 @@
/> />
</template> </template>
</td> </td>
<td v-if="isLookupOrRollup(col)" class="pa-3"> <td v-if="isLookupOrRollup(col)" class="pa-1">
<span <span
v-if="viewMode" v-if="viewMode"
class="caption" class="caption"
@ -326,7 +326,7 @@
item-value="cn" item-value="cn"
/> />
</td> </td>
<td v-if="isRollup(col)" class="pa-3"> <td v-if="isRollup(col)" class="pa-1">
<span <span
v-if="viewMode" v-if="viewMode"
class="caption" class="caption"
@ -350,7 +350,7 @@
<template <template
v-if="isSelect(col)" v-if="isSelect(col)"
> >
<td class="pa-3 text-left" colspan="2"> <td class="pa-1 text-left" colspan="2">
<span <span
v-if="viewMode" v-if="viewMode"
class="caption" class="caption"
@ -367,7 +367,12 @@
/> />
</td> </td>
</template> </template>
<td> <td
v-if="!isRollup(col) "
:colspan="isLookupOrRollup(col) || isRelation(col) || isSelect(col) ? (isRollup(col)?
0 :1) : 3"
/>
<td style="max-width: 50px;width: 50px">
<v-icon <v-icon
v-if="!viewMode" v-if="!viewMode"
class="flex-grow-0" class="flex-grow-0"
@ -375,7 +380,7 @@
color="grey" color="grey"
@click.stop="deleteTableColumn(i,j, col, table)" @click.stop="deleteTableColumn(i,j, col, table)"
> >
mdi-delete mdi-delete-outline
</v-icon> </v-icon>
</td> </td>
</template> </template>
@ -439,40 +444,41 @@
> >
Click to change gradient Click to change gradient
</div>--> </div>-->
<template v-if="!excelImport">
<gradient-generator v-model="project.image_url" class=" d-100" /> <gradient-generator v-model="project.image_url" class=" d-100" />
<v-row> <v-row>
<v-col> <v-col>
<v-text-field <v-text-field
v-model="project.category" v-model="project.category"
:rules="[v => !!v || 'Category name required']" :rules="[v => !!v || 'Category name required']"
class="caption" class="caption"
outlined outlined
dense dense
label="Project Category" label="Project Category"
/> />
</v-col> </v-col>
<v-col> <v-col>
<v-text-field <v-text-field
v-model="project.tags" v-model="project.tags"
class="caption"
outlined
dense
label="Project Tags"
/>
</v-col>
</v-row>
<div>
<v-textarea
v-model="project.description"
class="caption" class="caption"
outlined outlined
dense dense
label="Project Tags" label="Project Description"
@click="counter++"
/> />
</v-col> </div>
</v-row> </template>
<div>
<v-textarea
v-model="project.description"
class="caption"
outlined
dense
label="Project Description"
@click="counter++"
/>
</div>
</div> </div>
</v-card-text> </v-card-text>
</v-card> </v-card>
@ -571,7 +577,8 @@ export default {
props: { props: {
id: [Number, String], id: [Number, String],
viewMode: Boolean, viewMode: Boolean,
projectTemplate: Object projectTemplate: Object,
excelImport: Boolean
}, },
data: () => ({ data: () => ({
loading: false, loading: false,
@ -1277,14 +1284,16 @@ export default {
if (oldVal === UITypes.LinkToAnotherRecord) { if (oldVal === UITypes.LinkToAnotherRecord) {
rTable = this.project.tables.find(t => t.tn === col.rtn) rTable = this.project.tables.find(t => t.tn === col.rtn)
if (col.type === 'hm') { if (rTable) {
index = rTable.columns.findIndex(c => c.uidt === UITypes.ForeignKey && c.rtn === table.tn) if (col.type === 'hm') {
} else if (col.type === 'mm') { index = rTable.columns.findIndex(c => c.uidt === UITypes.ForeignKey && c.rtn === table.tn)
index = rTable.columns.findIndex(c => c.uidt === UITypes.LinkToAnotherRecord && c.rtn === table.tn && c.type === 'mm') } else if (col.type === 'mm') {
index = rTable.columns.findIndex(c => c.uidt === UITypes.LinkToAnotherRecord && c.rtn === table.tn && c.type === 'mm')
}
} }
} else if (oldVal === UITypes.ForeignKey) { } else if (oldVal === UITypes.ForeignKey) {
rTable = this.project.tables.find(t => t.tn === col.rtn) rTable = this.project.tables.find(t => t.tn === col.rtn)
index = rTable.columns.findIndex(c => c.uidt === UITypes.LinkToAnotherRecord && c.rtn === table.tn && c.type === 'hm') if (rTable) { index = rTable.columns.findIndex(c => c.uidt === UITypes.LinkToAnotherRecord && c.rtn === table.tn && c.type === 'hm') }
} }
if (rTable && index > -1) { if (rTable && index > -1) {
rTable.columns.splice(index, 1) rTable.columns.splice(index, 1)
@ -1303,4 +1312,7 @@ export default {
</script> </script>
<style scoped> <style scoped>
/deep/ .v-select__selections{
flex-wrap: nowrap;
}
</style> </style>

Loading…
Cancel
Save