Browse Source

feat: template management

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/760/head
Pranav C 3 years ago
parent
commit
10d7ccca23
  1. 32
      packages/nc-gui/components/templates/categories.vue
  2. 2
      packages/nc-gui/components/templates/detailed.vue
  3. 82
      packages/nc-gui/components/templates/editor.vue
  4. 10
      packages/nc-gui/components/templates/gradientGenerator.vue
  5. 2
      packages/nc-gui/components/templates/list.vue
  6. 25
      packages/nc-gui/components/templates/templatesModal.vue
  7. 18
      packages/nc-gui/store/index.js
  8. 59
      packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts

32
packages/nc-gui/components/templates/categories.vue

@ -22,12 +22,24 @@
</v-list>
<!-- v-if="counter > 4"-->
<v-btn
color="primary"
class="ml-4"
color="grey"
x-small
outlined
@click="showTemplateEditor"
>
Add new template
New template
</v-btn>
<v-text-field
v-if="$store.state.templateC >4"
v-model="t"
outlined
dense
type="password"
class="caption mt-4 ml-1 mr-3"
hide-details
/>
</div>
</template>
@ -40,6 +52,14 @@ export default {
categories: []
}),
computed: {
counterLoc: {
get() {
return this.$store.state.templateE
},
set(c) {
this.$store.commit('mutTemplateE', c)
}
},
category: {
get() {
return this.value
@ -48,12 +68,12 @@ export default {
this.$emit('input', v)
}
},
counterLoc: {
t: {
get() {
return this.counter
return this.$store.state.template
},
set(v) {
this.$emit('update:counter', v)
set(t) {
this.$store.commit('mutTemplate', t)
}
}
},

2
packages/nc-gui/components/templates/detailed.vue

@ -29,7 +29,7 @@
<templat-editor
:id="templateId"
:view-mode="counter < 5 && viewMode"
:view-mode="$store.state.templateE < 5 && viewMode"
:template-data.sync="templateData"
@saved="onSaved"
/>

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

@ -1,7 +1,7 @@
<template>
<div>
<v-toolbar v-if="!viewMode">
<v-text-field
<v-toolbar v-if="!viewMode" class="elevation-0">
<!-- <v-text-field
v-model="url"
clearable
placeholder="Enter template url"
@ -9,7 +9,7 @@
hide-details
dense
@keydown.enter="loadUrl"
/>
/>-->
<!-- <v-btn outlined class='ml-1' @click='loadUrl'> Load URL</v-btn>-->
<v-spacer />
@ -17,10 +17,10 @@
mdi-information-outline
</v-icon>
<v-icon class="mr-3" @click="openUrl">
<!-- <v-icon class="mr-3" @click="openUrl">
mdi-web
</v-icon>
<v-tooltip bottom>
</v-icon>-->
<!-- <v-tooltip bottom>
<template #activator="{on}">
<v-icon
class="mr-3"
@ -31,23 +31,29 @@
</v-icon>
</template>
<span class="caption">Reset template</span>
</v-tooltip>
</v-tooltip>-->
<v-icon
<v-btn small outlined class="mr-1" @click="project = {tables : []}">
<v-icon small>
mdi-close
</v-icon> Reset
</v-btn>
<!-- <v-icon
:color="$store.getters['github/isAuthorized'] ? '' : 'error'"
class="mr-3"
@click="githubConfigForm = !githubConfigForm"
>
mdi-github
</v-icon>
<v-icon class="mr-3" @click="createTablesDialog = true">
mdi-plus
</v-icon>
</v-icon>-->
<v-btn small outlined class="mr-1">
<v-icon small @click="createTablesDialog = true">
mdi-plus
</v-icon>
New table
</v-btn>
<!-- <v-btn outlined small class='mr-1' @click='submitTemplate'> Submit Template</v-btn>-->
<v-btn color="primary" outlined small class="mr-1" @click="saveTemplate">
{{ id || localId ? 'Update' : 'Create' }}
Template
{{ id || localId ? 'Update in' :'Submit to' }} NocoDB
</v-btn>
</v-toolbar>
<v-container class="text-center">
@ -422,8 +428,6 @@
@click="counter++"
/>
</div>
<v-text-field v-if="counter > 4" v-model="token" outlined dense label="Token" />
</div>
</v-card-text>
</v-card>
@ -556,11 +560,17 @@ export default {
LinkToAnotherRecord: 'blue lighten-5',
Rollup: 'pink lighten-5',
Lookup: 'green lighten-5'
},
counter: 0
}
}),
computed: {
counter: {
get() {
return this.$store.state.templateC
},
set(c) {
this.$store.commit('mutTemplateC', c)
}
},
updateFilename() {
return this.url && this.url.split('/').pop()
},
@ -828,7 +838,7 @@ export default {
this.copyJSON()
break
case 's':
await this.submitTemplate()
await this.saveTemplate()
break
case 'arrowup':
this.expansionPanel = this.expansionPanel ? --this.expansionPanel : this.project.tables.length - 1
@ -862,18 +872,14 @@ export default {
this.$toast.info('Please fill all the required column!').goAway(5000)
return
}
this.$clipboard(JSON.stringify(this.projectTemplate, null, 2))
// const el = document.createElement('textarea')
// el.value = JSON.stringify(this.projectTemplate, null, 2)
// debugger
// el.setAttribute('readonly', '')
// el.style = { position: 'absolute', left: '-9999px' }
// document.body.appendChild(el)
// el.select()
// document.execCommand('copy')
// document.body.removeChild(el)
const el = document.createElement('textarea')
el.addEventListener('focusin', e => e.stopPropagation())
el.value = JSON.stringify(this.projectTemplate, null, 2)
el.style = { position: 'absolute', left: '-9999px' }
document.body.appendChild(el)
el.select()
document.execCommand('copy')
document.body.removeChild(el)
this.$toast.success('Successfully copied JSON data to clipboard!').goAway(3000)
return true
},
@ -1025,12 +1031,14 @@ export default {
if (this.id || this.localId) {
await this.$axios.put(`${process.env.NC_API_URL}/api/v1/nc/templates/${this.id || this.localId}`, this.projectTemplate, {
params: {
token: this.token
token: this.$store.state.template
}
})
this.$toast.success('Template updated successfully').goAway(3000)
} else if (!this.token) {
if (!this.copyJSON()) { return }
} else if (!this.$store.state.template) {
if (!this.copyJSON()) {
return
}
this.$toast.info('Initiating Github for template').goAway(3000)
const res = await this.$axios.post(`${process.env.NC_API_URL}/api/v1/projectTemplateCreate`, this.projectTemplate)
@ -1040,7 +1048,7 @@ export default {
} else {
const res = await this.$axios.post(`${process.env.NC_API_URL}/api/v1/nc/templates`, this.projectTemplate, {
params: {
token: this.token
token: this.$store.state.template
}
})
this.localId = res.data.id

10
packages/nc-gui/components/templates/gradientGenerator.vue

@ -7,8 +7,9 @@
>
Click to change gradient
</div>
<input v-model="color1" :style="{color:color1} " type="color">
<input v-model="color2" :style="{color:color2} " type="color">
<input v-model="color1" type="color">
<input v-model="color2" type="color">
</div>
</template>
@ -24,7 +25,8 @@ export default {
computed: {
color1: {
get() {
return this.value && this.value.split(',')[1]
const val = this.value && this.value.split(',')[1].trim()
return val
},
set(v) {
const gradient = 'linear-gradient(' + this.angle + 'deg, ' + v + ', ' + this.color2 + ')'
@ -33,7 +35,7 @@ export default {
},
color2: {
get() {
return this.value && this.value.split(',')[2].slice(0, -1)
return this.value && this.value.split(',')[2].slice(0, -1).trim()
},
set(v) {
const gradient = 'linear-gradient(' + this.angle + 'deg, ' + this.color1 + ', ' + v + ')'

2
packages/nc-gui/components/templates/list.vue

@ -25,11 +25,11 @@
md="6"
lg="4"
xl="3"
@click="openTemplate(template.id)"
>
<v-card
height="100%"
class="mx-auto"
@click="openTemplate(template.id)"
>
<v-img
:src="template.image_url"

25
packages/nc-gui/components/templates/templatesModal.vue

@ -1,7 +1,7 @@
<template>
<div>
<span class="caption font-weight-bold" @click="templatesModal = true">Templates</span>
<v-dialog v-model="templatesModal" v-if="templatesModal">
<span v-ripple class="caption font-weight-bold pointer" @click="templatesModal = true">Templates</span>
<v-dialog v-if="templatesModal" v-model="templatesModal">
<v-card>
<project-templates modal @import="importTemplate" />
</v-card>
@ -11,6 +11,7 @@
<script>
import ProjectTemplates from '~/components/templates/list'
export default {
name: 'TemplatesModal',
components: { ProjectTemplates },
@ -18,17 +19,29 @@ export default {
templatesModal: false
}),
methods: {
importTemplate(template) {
async importTemplate(template) {
try {
this.$store.dispatch('sqlMgr/ActSqlOp', [{
const res = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
// todo: extract based on active
dbAlias: 'db', // this.nodes.dbAlias,
env: '_noco'
}, 'xcModelsCreateFromTemplate', {
template
}])
this.$toast.success('Template imported successfully').goAway(3000);
this.templatesModal = false;
if (res && res.tables && res.tables.length) {
this.$toast.success(`Imported ${res.tables.length} tables successfully`).goAway(3000)
// await this.$router.push({
// query: {
// ...(this.$route.query || {}),
// type: 'table',
// name: res.tables[0]._tn
// }
// })
} else {
this.$toast.success('Template imported successfully').goAway(3000)
}
this.templatesModal = false
} catch (e) {
this.$toast.error(e.message).goAway(3000)
}

18
packages/nc-gui/store/index.js

@ -1,13 +1,23 @@
export const state = () => ({
paidUser: false
paidUser: false,
template: null,
templateC: 0,
templateE: 0
})
export const mutations = {
mutTemplate(state, v) {
state.template = v
},
mutTemplateC(state, c) {
state.templateC = c
},
mutTemplateE(state, e) {
state.templateE = e
}
}
export const actions = {
}
export const actions = {}
export const strict = false
/**

59
packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts

@ -4064,6 +4064,7 @@ export default class NcMetaMgr {
return { data: { list: procedures } };
}
// todo: transaction
protected async xcModelsCreateFromTemplate(args, req) {
const template = args.args.template;
@ -4075,6 +4076,7 @@ export default class NcMetaMgr {
d => d?.meta?.dbAlias === dbAlias
);
const result = { tables: [], relations: [] };
const apiBuilder = this.app?.projectBuilders
?.find(pb => pb.id === projectId)
@ -4089,7 +4091,6 @@ export default class NcMetaMgr {
for (const table of parser.tables) {
console.log(table);
// create table and trigger listener
const out = await this.projectMgr
.getSqlMgr({ id: projectId })
@ -4112,35 +4113,37 @@ export default class NcMetaMgr {
}
});
}
result.tables.push({ tn: table.tn, _tn: table._tn });
}
// create relations
for (const relation of parser.relations) {
if (relation.type === 'real') {
const outrel = await this.projectMgr
.getSqlMgr({ id: projectId })
.handleRequest('relationCreate', {
...args,
args: relation
});
if (this.listener) {
await this.listener({
req: {
const outrel = await this.projectMgr
.getSqlMgr({ id: projectId })
.handleRequest('relationCreate', {
...args,
args: relation,
api: 'relationCreate'
},
res: outrel,
user: req.user,
ctx: {
req
}
});
}
args: relation
});
if (this.listener) {
await this.listener({
req: {
...args,
args: relation,
api: 'relationCreate'
},
res: outrel,
user: req.user,
ctx: {
req
}
});
}
} else {
const outrel = await this.xcVirtualRelationCreate(
{...args, args: relation},
{ ...args, args: relation },
req
);
if (this.listener) {
@ -4157,8 +4160,8 @@ export default class NcMetaMgr {
}
});
}
}
result.relations.push({});
}
//create m2m relations
@ -4186,13 +4189,15 @@ export default class NcMetaMgr {
}
});
}
result.relations.push({ mm: true });
}
// add virtual columns
for(const [tn, vColumns] of Object.entries(parser.virtualColumns)){
for (const [tn, vColumns] of Object.entries(parser.virtualColumns)) {
const meta = apiBuilder.getMeta(tn);
meta.v = meta.v || [];
meta.v.push(...vColumns)
meta.v.push(...vColumns);
const res = await this.xcModelSet({
...args,
@ -4200,11 +4205,11 @@ export default class NcMetaMgr {
meta,
tn
}
})
});
await this.listener({
req: {
...args,
args:{
args: {
meta,
tn
},
@ -4218,7 +4223,7 @@ export default class NcMetaMgr {
});
}
return result;
}
protected async xcExportAsCsv(args, _req, res: express.Response) {

Loading…
Cancel
Save