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-list>
<!-- v-if="counter > 4"--> <!-- v-if="counter > 4"-->
<v-btn <v-btn
color="primary" class="ml-4"
color="grey"
x-small
outlined outlined
@click="showTemplateEditor" @click="showTemplateEditor"
> >
Add new template New template
</v-btn> </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> </div>
</template> </template>
@ -40,6 +52,14 @@ export default {
categories: [] categories: []
}), }),
computed: { computed: {
counterLoc: {
get() {
return this.$store.state.templateE
},
set(c) {
this.$store.commit('mutTemplateE', c)
}
},
category: { category: {
get() { get() {
return this.value return this.value
@ -48,12 +68,12 @@ export default {
this.$emit('input', v) this.$emit('input', v)
} }
}, },
counterLoc: { t: {
get() { get() {
return this.counter return this.$store.state.template
}, },
set(v) { set(t) {
this.$emit('update:counter', v) this.$store.commit('mutTemplate', t)
} }
} }
}, },

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

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

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

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

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

@ -7,8 +7,9 @@
> >
Click to change gradient Click to change gradient
</div> </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> </div>
</template> </template>
@ -24,7 +25,8 @@ export default {
computed: { computed: {
color1: { color1: {
get() { get() {
return this.value && this.value.split(',')[1] const val = this.value && this.value.split(',')[1].trim()
return val
}, },
set(v) { set(v) {
const gradient = 'linear-gradient(' + this.angle + 'deg, ' + v + ', ' + this.color2 + ')' const gradient = 'linear-gradient(' + this.angle + 'deg, ' + v + ', ' + this.color2 + ')'
@ -33,7 +35,7 @@ export default {
}, },
color2: { color2: {
get() { get() {
return this.value && this.value.split(',')[2].slice(0, -1) return this.value && this.value.split(',')[2].slice(0, -1).trim()
}, },
set(v) { set(v) {
const gradient = 'linear-gradient(' + this.angle + 'deg, ' + this.color1 + ', ' + 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" md="6"
lg="4" lg="4"
xl="3" xl="3"
@click="openTemplate(template.id)"
> >
<v-card <v-card
height="100%" height="100%"
class="mx-auto" class="mx-auto"
@click="openTemplate(template.id)"
> >
<v-img <v-img
:src="template.image_url" :src="template.image_url"

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

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<span class="caption font-weight-bold" @click="templatesModal = true">Templates</span> <span v-ripple class="caption font-weight-bold pointer" @click="templatesModal = true">Templates</span>
<v-dialog v-model="templatesModal" v-if="templatesModal"> <v-dialog v-if="templatesModal" v-model="templatesModal">
<v-card> <v-card>
<project-templates modal @import="importTemplate" /> <project-templates modal @import="importTemplate" />
</v-card> </v-card>
@ -11,6 +11,7 @@
<script> <script>
import ProjectTemplates from '~/components/templates/list' import ProjectTemplates from '~/components/templates/list'
export default { export default {
name: 'TemplatesModal', name: 'TemplatesModal',
components: { ProjectTemplates }, components: { ProjectTemplates },
@ -18,17 +19,29 @@ export default {
templatesModal: false templatesModal: false
}), }),
methods: { methods: {
importTemplate(template) { async importTemplate(template) {
try { try {
this.$store.dispatch('sqlMgr/ActSqlOp', [{ const res = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
// todo: extract based on active // todo: extract based on active
dbAlias: 'db', // this.nodes.dbAlias, dbAlias: 'db', // this.nodes.dbAlias,
env: '_noco' env: '_noco'
}, 'xcModelsCreateFromTemplate', { }, 'xcModelsCreateFromTemplate', {
template 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) { } catch (e) {
this.$toast.error(e.message).goAway(3000) this.$toast.error(e.message).goAway(3000)
} }

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

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

Loading…
Cancel
Save