mirror of https://github.com/nocodb/nocodb
Wing-Kam Wong
2 years ago
69 changed files with 56344 additions and 1264 deletions
@ -0,0 +1,128 @@ |
|||||||
|
/* eslint-disable */ |
||||||
|
// import assign from "nano-assign";
|
||||||
|
// import sqlAutoCompletions from "./sqlAutoCompletions";
|
||||||
|
// import {ext} from "vee-validate/dist/rules.esm";
|
||||||
|
|
||||||
|
|
||||||
|
export default { |
||||||
|
name: "CustomMonacoEditor", |
||||||
|
|
||||||
|
props: { |
||||||
|
value: { |
||||||
|
default: "", |
||||||
|
type: String |
||||||
|
}, |
||||||
|
theme: { |
||||||
|
type: String, |
||||||
|
default: "vs-dark" |
||||||
|
}, |
||||||
|
lang: {type:String, default: 'typescript'}, |
||||||
|
readOnly:Boolean, |
||||||
|
minimap:Boolean, |
||||||
|
}, |
||||||
|
|
||||||
|
model: { |
||||||
|
event: "change" |
||||||
|
}, |
||||||
|
watch: { |
||||||
|
value(newVal) { |
||||||
|
if (newVal !== this.editor.getValue()) { |
||||||
|
if (typeof newVal === 'object') { |
||||||
|
this.editor.setValue(JSON.stringify(newVal, 0, 2)); |
||||||
|
} else { |
||||||
|
this.editor.setValue(newVal); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
mounted() { |
||||||
|
this.$nextTick(() => { |
||||||
|
if (this.amdRequire) { |
||||||
|
this.amdRequire(["vs/editor/editor.main"], () => { |
||||||
|
this.initMonaco(window.monaco); |
||||||
|
}); |
||||||
|
} else { |
||||||
|
// ESM format so it can't be resolved by commonjs `require` in eslint
|
||||||
|
// eslint-disable import/no-unresolved
|
||||||
|
const monaco = require("monaco-editor"); |
||||||
|
// monaco.editor.defineTheme('monokai', require('./Cobalt.json'))
|
||||||
|
// monaco.editor.setTheme('monokai')
|
||||||
|
|
||||||
|
this.monaco = monaco; |
||||||
|
this.initMonaco(monaco); |
||||||
|
} |
||||||
|
}); |
||||||
|
}, |
||||||
|
unmounted() { |
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
beforeDestroy() { |
||||||
|
this.editor && this.editor.dispose(); |
||||||
|
}, |
||||||
|
|
||||||
|
methods: { |
||||||
|
resizeLayout() { |
||||||
|
this.editor.layout(); |
||||||
|
}, |
||||||
|
initMonaco(monaco) { |
||||||
|
const code = this.value; |
||||||
|
const model = monaco.editor.createModel(code, this.lang ||"json"); |
||||||
|
|
||||||
|
this.editor = monaco.editor.create(this.$el, { |
||||||
|
model: model, |
||||||
|
theme: this.theme, minimap: { |
||||||
|
enabled: this.minimap |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
this.editor.onDidChangeModelContent(event => { |
||||||
|
const value = this.editor.getValue(); |
||||||
|
if (this.value !== value) { |
||||||
|
this.$emit("change", value, event); |
||||||
|
} |
||||||
|
}); |
||||||
|
this.editor.updateOptions({ readOnly: this.readOnly }) |
||||||
|
}, |
||||||
|
|
||||||
|
getMonaco() { |
||||||
|
return this.editor; |
||||||
|
}, |
||||||
|
getMonacoModule() { |
||||||
|
return this.monaco; |
||||||
|
}, |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
render(h) { |
||||||
|
return h("div"); |
||||||
|
}, |
||||||
|
created() { |
||||||
|
}, |
||||||
|
destroyed() { |
||||||
|
} |
||||||
|
}; |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* |
||||||
|
*/ |
@ -0,0 +1,129 @@ |
|||||||
|
/* eslint-disable */ |
||||||
|
// import assign from "nano-assign";
|
||||||
|
// import sqlAutoCompletions from "./sqlAutoCompletions";
|
||||||
|
// import {ext} from "vee-validate/dist/rules.esm";
|
||||||
|
|
||||||
|
|
||||||
|
export default { |
||||||
|
name: "MonacoHandlebarEditor", |
||||||
|
|
||||||
|
props: { |
||||||
|
value: { |
||||||
|
default: "", |
||||||
|
type: String |
||||||
|
}, |
||||||
|
theme: { |
||||||
|
type: String, |
||||||
|
default: "vs-dark" |
||||||
|
}, |
||||||
|
}, |
||||||
|
model: { |
||||||
|
event: "change" |
||||||
|
}, |
||||||
|
watch: { |
||||||
|
value(newVal) { |
||||||
|
if (newVal !== this.editor.getValue()) { |
||||||
|
if (typeof newVal === 'object') { |
||||||
|
this.editor.setValue(JSON.stringify(newVal, 0, 2)); |
||||||
|
} else { |
||||||
|
this.editor.setValue(newVal); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
mounted() { |
||||||
|
this.$nextTick(() => { |
||||||
|
if (this.amdRequire) { |
||||||
|
this.amdRequire(["vs/editor/editor.main"], () => { |
||||||
|
this.initMonaco(window.monaco); |
||||||
|
}); |
||||||
|
} else { |
||||||
|
// ESM format so it can't be resolved by commonjs `require` in eslint
|
||||||
|
// eslint-disable import/no-unresolved
|
||||||
|
const monaco = require("monaco-editor"); |
||||||
|
// monaco.editor.defineTheme('monokai', require('./Cobalt.json'))
|
||||||
|
// monaco.editor.setTheme('monokai')
|
||||||
|
|
||||||
|
this.monaco = monaco; |
||||||
|
|
||||||
|
// this.completionItemProvider = monaco.languages.registerCompletionItemProvider("sql", {
|
||||||
|
// async provideCompletionItems(model, position) {
|
||||||
|
// // console.log(sqlAutoCompletions(monaco).actions[0])
|
||||||
|
// console.log(model === vm.editor,model,vm.editor)
|
||||||
|
// return model === vm.editor.getModel() ? {suggestions: await vm.getLiveSuggestionsList(model, position)} : {};
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
this.initMonaco(monaco); |
||||||
|
} |
||||||
|
}); |
||||||
|
}, |
||||||
|
unmounted() { |
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
beforeDestroy() { |
||||||
|
this.editor && this.editor.dispose(); |
||||||
|
}, |
||||||
|
|
||||||
|
methods: { |
||||||
|
initMonaco(monaco) { |
||||||
|
const typescriptCode = this.value; |
||||||
|
const model = monaco.editor.createModel(typescriptCode); |
||||||
|
|
||||||
|
|
||||||
|
this.editor = monaco.editor.create(this.$el, { |
||||||
|
model: model, |
||||||
|
theme: this.theme |
||||||
|
}); |
||||||
|
|
||||||
|
this.editor.onDidChangeModelContent(event => { |
||||||
|
const value = this.editor.getValue(); |
||||||
|
if (this.value !== value) { |
||||||
|
this.$emit("change", value, event); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
getMonaco() { |
||||||
|
return this.editor; |
||||||
|
}, |
||||||
|
getMonacoModule() { |
||||||
|
return this.monaco; |
||||||
|
}, |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
render(h) { |
||||||
|
return h("div"); |
||||||
|
}, |
||||||
|
created() { |
||||||
|
}, |
||||||
|
destroyed() { |
||||||
|
} |
||||||
|
}; |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* |
||||||
|
*/ |
@ -0,0 +1,272 @@ |
|||||||
|
<template> |
||||||
|
<div class="nc-container" :class="{active:modal}" @click="modal=false"> |
||||||
|
<div class="nc-snippet elevation-3 pa-4" @click.stop> |
||||||
|
<h3 class="font-weight-medium mb-4"> |
||||||
|
Code Snippet |
||||||
|
</h3> |
||||||
|
|
||||||
|
<v-icon class="nc-snippet-close" @click="modal=false"> |
||||||
|
mdi-close |
||||||
|
</v-icon> |
||||||
|
|
||||||
|
<div v-if="modal"> |
||||||
|
<v-tabs v-model="tab" height="30" show-arrows @change="client=null"> |
||||||
|
<v-tab |
||||||
|
v-for="{lang} in langs" |
||||||
|
:key="lang" |
||||||
|
v-t="['c:snippet:tab', {lang}]" |
||||||
|
class="caption" |
||||||
|
> |
||||||
|
{{ lang }} |
||||||
|
</v-tab> |
||||||
|
</v-tabs> |
||||||
|
<div class="nc-snippet-wrapper mt-4"> |
||||||
|
<div class="nc-snippet-actions d-flex"> |
||||||
|
<v-btn |
||||||
|
v-t="['c:snippet:copy', {client: langs[tab].clients && (client || langs[tab].clients[0]), lang: langs[tab ||0].lang}]" |
||||||
|
color="primary" |
||||||
|
class="rounded caption " |
||||||
|
@click="copyToClipboard" |
||||||
|
> |
||||||
|
<v-icon small> |
||||||
|
mdi-clipboard-outline |
||||||
|
</v-icon> |
||||||
|
Copy To Clipboard |
||||||
|
</v-btn> |
||||||
|
<div |
||||||
|
v-if="langs[tab].clients" |
||||||
|
class=" ml-2 d-flex align-center" |
||||||
|
> |
||||||
|
<v-menu bottom offset-y> |
||||||
|
<template #activator="{on}"> |
||||||
|
<v-btn class="caption text-uppercase" color="primary" v-on="on"> |
||||||
|
{{ client || langs[tab].clients[0] }} |
||||||
|
<v-icon small> |
||||||
|
mdi-chevron-down |
||||||
|
</v-icon> |
||||||
|
</v-btn> |
||||||
|
</template> |
||||||
|
<v-list dense> |
||||||
|
<v-list-item |
||||||
|
v-for="c in langs[tab].clients" |
||||||
|
:key="c" |
||||||
|
dense |
||||||
|
@click="client = c" |
||||||
|
> |
||||||
|
<v-list-item-title class="text-uppercase"> |
||||||
|
{{ c }} |
||||||
|
</v-list-item-title> |
||||||
|
</v-list-item> |
||||||
|
</v-list> |
||||||
|
</v-menu> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<custom-monaco-editor |
||||||
|
hide-line-num |
||||||
|
:theme="$store.state.windows.darkTheme ? 'vs-dark' : 'vs-light'" |
||||||
|
style="min-height:500px;max-width: 100%" |
||||||
|
:value="code" |
||||||
|
read-only |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import HTTPSnippet from 'httpsnippet' |
||||||
|
import CustomMonacoEditor from '~/components/monaco/CustomMonacoEditor' |
||||||
|
import { copyTextToClipboard } from '~/helpers/xutils' |
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'CodeSnippet', |
||||||
|
components: { CustomMonacoEditor }, |
||||||
|
props: { |
||||||
|
meta: Object, |
||||||
|
view: Object, |
||||||
|
filters: [Object, Array], |
||||||
|
sorts: [Object, Array], |
||||||
|
fileds: [Object, Array], |
||||||
|
queryParams: Object, |
||||||
|
value: Boolean |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
tab: 0, |
||||||
|
client: null, |
||||||
|
langs: [ |
||||||
|
{ |
||||||
|
lang: 'shell', |
||||||
|
clients: ['curl', 'wget'] |
||||||
|
}, |
||||||
|
{ |
||||||
|
lang: 'javascript', |
||||||
|
clients: ['axios', 'fetch', 'jquery', 'xhr'] |
||||||
|
}, |
||||||
|
{ |
||||||
|
lang: 'node', |
||||||
|
clients: ['axios', 'fetch', 'request', 'native', 'unirest'] |
||||||
|
}, |
||||||
|
{ |
||||||
|
lang: 'nocodb-sdk', |
||||||
|
clients: ['javascript', 'node'] |
||||||
|
}, |
||||||
|
{ |
||||||
|
lang: 'php' |
||||||
|
}, |
||||||
|
{ |
||||||
|
lang: 'python', |
||||||
|
clients: ['python3', |
||||||
|
'requests'] |
||||||
|
}, |
||||||
|
{ |
||||||
|
lang: 'ruby' |
||||||
|
}, |
||||||
|
{ |
||||||
|
lang: 'java' |
||||||
|
}, |
||||||
|
{ |
||||||
|
lang: 'c' |
||||||
|
} |
||||||
|
] |
||||||
|
}), |
||||||
|
computed: { |
||||||
|
modal: { |
||||||
|
get() { |
||||||
|
return this.value |
||||||
|
}, |
||||||
|
set(v) { |
||||||
|
this.$emit('input', v) |
||||||
|
} |
||||||
|
}, |
||||||
|
apiUrl() { |
||||||
|
return new URL(`/api/v1/db/data/noco/${this.projectId}/${this.meta.title}/views/${this.view.title}`, (this.$store.state.project.projectInfo && this.$store.state.project.projectInfo.ncSiteUrl) || '/').href |
||||||
|
}, |
||||||
|
snippet() { |
||||||
|
return new HTTPSnippet({ |
||||||
|
method: 'GET', |
||||||
|
headers: [ |
||||||
|
{ name: 'xc-auth', value: this.$store.state.users.token, comment: 'JWT Auth token' } |
||||||
|
], |
||||||
|
url: this.apiUrl, |
||||||
|
queryString: Object.entries(this.queryParams || {}).map(([name, value]) => { |
||||||
|
return { |
||||||
|
name, value: String(value) |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
}, |
||||||
|
code() { |
||||||
|
if (this.langs[this.tab].lang === 'nocodb-sdk') { |
||||||
|
return `${ |
||||||
|
this.client === 'node' |
||||||
|
? 'const { Api } require("nocodb-sdk");' |
||||||
|
: 'import { Api } from "nocodb-sdk";' |
||||||
|
} |
||||||
|
|
||||||
|
const api = new Api({ |
||||||
|
baseURL: ${JSON.stringify(this.apiUrl)}, |
||||||
|
headers: { |
||||||
|
"xc-auth": ${JSON.stringify(this.$store.state.users.token)} |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
api.dbViewRow.list( |
||||||
|
"noco", |
||||||
|
${JSON.stringify(this.projectName)}, |
||||||
|
${JSON.stringify(this.meta.title)}, |
||||||
|
${JSON.stringify(this.view.title)}, ${JSON.stringify(this.queryParams, null, 4)}).then(function (data) { |
||||||
|
console.log(data); |
||||||
|
}).catch(function (error) { |
||||||
|
console.error(error); |
||||||
|
});` |
||||||
|
} |
||||||
|
|
||||||
|
return this.snippet.convert(this.langs[this.tab].lang, this.client || (this.langs[this.tab].clients && this.langs[this.tab].clients[0]), {}) |
||||||
|
} |
||||||
|
}, |
||||||
|
mounted() { |
||||||
|
(document.querySelector('[data-app]') || this.$root.$el).append(this.$el) |
||||||
|
}, |
||||||
|
|
||||||
|
destroyed() { |
||||||
|
this.$el.parentNode && this.$el.parentNode.removeChild(this.$el) |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
copyToClipboard() { |
||||||
|
copyTextToClipboard(this.code) |
||||||
|
this.$toast.success('Code copied to clipboard successfully.').goAway(3000) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.nc-snippet-wrapper { |
||||||
|
position: relative; |
||||||
|
border: 1px solid #7773; |
||||||
|
border-radius: 4px; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
.nc-snippet-actions { |
||||||
|
position: absolute; |
||||||
|
right: 10px; |
||||||
|
bottom: 10px; |
||||||
|
z-index: 99999; |
||||||
|
} |
||||||
|
|
||||||
|
.nc-container { |
||||||
|
position: fixed; |
||||||
|
pointer-events: none; |
||||||
|
width: 100vw; |
||||||
|
height: 100vh; |
||||||
|
z-index: 9999; |
||||||
|
right: 0; |
||||||
|
top: 0; |
||||||
|
|
||||||
|
.nc-snippet { |
||||||
|
background-color: var(--v-backgroundColorDefault-base); |
||||||
|
height: 100%; |
||||||
|
width: max(50%, 700px); |
||||||
|
position: absolute; |
||||||
|
bottom: 0; |
||||||
|
top: 0; |
||||||
|
right: min(-50%, -700px); |
||||||
|
transition: .3s right; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
&.active { |
||||||
|
pointer-events: all; |
||||||
|
|
||||||
|
& > .nc-snippet { |
||||||
|
right: 0 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.nc-snippet-close { |
||||||
|
position: absolute; |
||||||
|
right: 16px; |
||||||
|
top: 16px; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
::v-deep { |
||||||
|
.v-tabs { |
||||||
|
height: 100%; |
||||||
|
|
||||||
|
.v-tabs-items { |
||||||
|
height: calc(100% - 30px); |
||||||
|
|
||||||
|
.v-window__container { |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.v-slide-group__prev--disabled { |
||||||
|
display: none |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,539 @@ |
|||||||
|
<template> |
||||||
|
<v-form v-if="hook" ref="form" v-model="valid" class="mx-4" lazy-validation> |
||||||
|
<v-card-title> |
||||||
|
<a class="pointer mr-1" @click="$emit('backToList')"> |
||||||
|
<v-icon>mdi-arrow-left-bold</v-icon> |
||||||
|
</a> |
||||||
|
|
||||||
|
<v-spacer /> |
||||||
|
{{ meta.title }} : {{ hook.title || 'Webhook' }} |
||||||
|
<v-spacer /> |
||||||
|
<div style="width: 24px;height: 24px" /> |
||||||
|
</v-card-title> |
||||||
|
|
||||||
|
<div class="mx-4 d-flex m-2"> |
||||||
|
<v-spacer /> |
||||||
|
<v-btn |
||||||
|
outlined |
||||||
|
tooltip="Save" |
||||||
|
small |
||||||
|
:disabled="loading || !valid || !hook.event" |
||||||
|
@click.prevent="$refs.webhookTest.testWebhook()" |
||||||
|
> |
||||||
|
Test webhook |
||||||
|
</v-btn> |
||||||
|
<v-btn |
||||||
|
outlined |
||||||
|
tooltip="Save" |
||||||
|
color="primary" |
||||||
|
small |
||||||
|
:disabled="loading || !valid || !hook.event" |
||||||
|
@click.prevent="saveHooks" |
||||||
|
> |
||||||
|
<v-icon small left> |
||||||
|
save |
||||||
|
</v-icon> |
||||||
|
<!-- Save --> |
||||||
|
{{ $t("general.save") }} |
||||||
|
</v-btn> |
||||||
|
</div> |
||||||
|
|
||||||
|
<v-card-text> |
||||||
|
<v-text-field |
||||||
|
v-model="hook.title" |
||||||
|
class="caption" |
||||||
|
outlined |
||||||
|
dense |
||||||
|
:label="$t('general.title')" |
||||||
|
required |
||||||
|
:rules="[(v) => !!v || `${$t('general.required')}`]" |
||||||
|
/> |
||||||
|
|
||||||
|
<webhook-event |
||||||
|
:event.sync="hook.event" |
||||||
|
:operation.sync="hook.operation" |
||||||
|
/> |
||||||
|
|
||||||
|
<v-card class="mb-8 nc-filter-wrapper"> |
||||||
|
<v-card-text> |
||||||
|
<v-checkbox |
||||||
|
v-model="hook.condition" |
||||||
|
dense |
||||||
|
hide-details |
||||||
|
class="mt-1" |
||||||
|
label="On Condition" |
||||||
|
/> |
||||||
|
|
||||||
|
<column-filter |
||||||
|
v-if="hook.condition" |
||||||
|
:key="key" |
||||||
|
ref="filter" |
||||||
|
v-model="filters" |
||||||
|
:meta="meta" |
||||||
|
:field-list="fieldList" |
||||||
|
dense |
||||||
|
style="max-width: 100%" |
||||||
|
:hook-id="hook.id" |
||||||
|
web-hook |
||||||
|
/> |
||||||
|
</v-card-text> |
||||||
|
</v-card> |
||||||
|
|
||||||
|
<v-select |
||||||
|
v-model="hook.notification.type" |
||||||
|
outlined |
||||||
|
dense |
||||||
|
:label="$t('general.notification')" |
||||||
|
required |
||||||
|
:items="notificationList" |
||||||
|
:rules="[(v) => !!v || `${$t('general.required')}`]" |
||||||
|
class="caption" |
||||||
|
:prepend-inner-icon="notificationIcon[hook.notification.type]" |
||||||
|
@change="onNotTypeChange" |
||||||
|
> |
||||||
|
<template #item="{ item }"> |
||||||
|
<v-list-item-icon> |
||||||
|
<v-icon small> |
||||||
|
{{ notificationIcon[item] }} |
||||||
|
</v-icon> |
||||||
|
</v-list-item-icon> |
||||||
|
<v-list-item-title> |
||||||
|
{{ item }} |
||||||
|
</v-list-item-title> |
||||||
|
</template> |
||||||
|
</v-select> |
||||||
|
|
||||||
|
<template v-if="hook.notification.type === 'URL'"> |
||||||
|
<http-webhook v-model="notification" /> |
||||||
|
</template> |
||||||
|
|
||||||
|
<template v-if="hook.notification.type === 'Slack'"> |
||||||
|
<v-combobox |
||||||
|
v-if="slackChannels" |
||||||
|
v-model="notification.channels" |
||||||
|
:rules="[(v) => !!v || `${$t('general.required')}`]" |
||||||
|
:items="slackChannels" |
||||||
|
item-text="channel" |
||||||
|
label="Select Slack channels" |
||||||
|
multiple |
||||||
|
outlined |
||||||
|
dense |
||||||
|
class="caption" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
<template v-if="hook.notification.type === 'Microsoft Teams'"> |
||||||
|
<v-combobox |
||||||
|
v-if="teamsChannels" |
||||||
|
v-model="notification.channels" |
||||||
|
:rules="[(v) => !!v || `${$t('general.required')}`]" |
||||||
|
:items="teamsChannels" |
||||||
|
item-text="channel" |
||||||
|
label="Select Teams channels" |
||||||
|
multiple |
||||||
|
outlined |
||||||
|
dense |
||||||
|
class="caption" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
<template v-if="hook.notification.type === 'Discord'"> |
||||||
|
<v-combobox |
||||||
|
v-if="discordChannels" |
||||||
|
v-model="notification.channels" |
||||||
|
:rules="[(v) => !!v || `${$t('general.required')}`]" |
||||||
|
:items="discordChannels" |
||||||
|
item-text="channel" |
||||||
|
label="Select Discord channels" |
||||||
|
multiple |
||||||
|
outlined |
||||||
|
dense |
||||||
|
class="caption" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
<template v-if="hook.notification.type === 'Mattermost'"> |
||||||
|
<v-combobox |
||||||
|
v-if="mattermostChannels" |
||||||
|
v-model="notification.channels" |
||||||
|
:rules="[(v) => !!v || `${$t('general.required')}`]" |
||||||
|
:items="mattermostChannels" |
||||||
|
item-text="channel" |
||||||
|
label="Select Mattermost channels" |
||||||
|
multiple |
||||||
|
outlined |
||||||
|
dense |
||||||
|
class="caption" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
|
||||||
|
<template v-if="inputs[hook.notification.type] && notification"> |
||||||
|
<template v-for="input in inputs[hook.notification.type]"> |
||||||
|
<v-textarea |
||||||
|
v-if="input.type === 'LongText'" |
||||||
|
:key="input.key" |
||||||
|
v-model="notification[input.key]" |
||||||
|
class="caption" |
||||||
|
dense |
||||||
|
outlined |
||||||
|
:label="input.label" |
||||||
|
:rules="[ |
||||||
|
(v) => |
||||||
|
!input.required || !!v || `${$t('general.required')}`, |
||||||
|
]" |
||||||
|
/> |
||||||
|
<v-text-field |
||||||
|
v-else |
||||||
|
:key="input.key" |
||||||
|
v-model="notification[input.key]" |
||||||
|
class="caption" |
||||||
|
dense |
||||||
|
outlined |
||||||
|
:label="input.label" |
||||||
|
:rules="[ |
||||||
|
(v) => |
||||||
|
!input.required || !!v || `${$t('general.required')}`, |
||||||
|
]" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
</template> |
||||||
|
</v-card-text> |
||||||
|
|
||||||
|
<v-card-text> |
||||||
|
<span class="caption grey--text"> |
||||||
|
<em>Available context variables are |
||||||
|
<strong>data and user</strong></em> |
||||||
|
<v-tooltip top> |
||||||
|
<template #activator="{ on }"> |
||||||
|
<v-icon |
||||||
|
small |
||||||
|
color="grey" |
||||||
|
class="ml-2" |
||||||
|
v-on="on" |
||||||
|
>mdi-information</v-icon> |
||||||
|
</template> |
||||||
|
<span class="caption"> |
||||||
|
<strong>data</strong> : Row data <br> |
||||||
|
<strong>user</strong> : User information<br> |
||||||
|
</span> |
||||||
|
</v-tooltip> |
||||||
|
<br> |
||||||
|
<a |
||||||
|
href="https://docs.nocodb.com/developer-resources/webhooks/" |
||||||
|
> |
||||||
|
<!--Document Reference--> |
||||||
|
{{ $t("labels.docReference") }} |
||||||
|
</a> |
||||||
|
</span> |
||||||
|
|
||||||
|
<webhooks-test |
||||||
|
ref="webhookTest" |
||||||
|
class="mt-3" |
||||||
|
:model-id="meta.id" |
||||||
|
hide-test-btn |
||||||
|
:hook="{ |
||||||
|
...hook, |
||||||
|
filters, |
||||||
|
notification: { |
||||||
|
...hook.notification, |
||||||
|
payload: notification, |
||||||
|
}, |
||||||
|
}" |
||||||
|
/> |
||||||
|
</v-card-text> |
||||||
|
</v-form> |
||||||
|
<span v-else /> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import WebhooksTest from '~/components/project/tableTabs/webhook/webhooksTest' |
||||||
|
import WebhookEvent from '~/components/project/tableTabs/webhook/webhookEvent' |
||||||
|
import HttpWebhook from '~/components/project/tableTabs/webhook/httpWebhook' |
||||||
|
import ColumnFilter from '~/components/project/spreadsheet/components/columnFilter' |
||||||
|
export default { |
||||||
|
name: 'WebhookEditor', |
||||||
|
components: { ColumnFilter, HttpWebhook, WebhookEvent, WebhooksTest }, |
||||||
|
props: { |
||||||
|
meta: Object |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
notification: {}, |
||||||
|
hook: { |
||||||
|
notification: { |
||||||
|
type: 'URL' |
||||||
|
} |
||||||
|
}, |
||||||
|
valid: false, |
||||||
|
apps: {}, |
||||||
|
slackChannels: null, |
||||||
|
teamsChannels: null, |
||||||
|
discordChannels: null, |
||||||
|
mattermostChannels: null, |
||||||
|
enableCondition: false, |
||||||
|
notificationList: [ |
||||||
|
'URL', |
||||||
|
'Email', |
||||||
|
'Slack', |
||||||
|
'Microsoft Teams', |
||||||
|
'Discord', |
||||||
|
'Mattermost', |
||||||
|
'Twilio', |
||||||
|
'Whatsapp Twilio' |
||||||
|
], |
||||||
|
filters: [], |
||||||
|
notificationIcon: { |
||||||
|
URL: 'mdi-link', |
||||||
|
Email: 'mdi-email', |
||||||
|
Slack: 'mdi-slack', |
||||||
|
'Microsoft Teams': 'mdi-microsoft-teams', |
||||||
|
Discord: 'mdi-discord', |
||||||
|
Mattermost: 'mdi-chat', |
||||||
|
'Whatsapp Twilio': 'mdi-whatsapp', |
||||||
|
Twilio: 'mdi-cellphone-message' |
||||||
|
}, |
||||||
|
inputs: { |
||||||
|
Email: [ |
||||||
|
{ |
||||||
|
key: 'to', |
||||||
|
label: 'To Address', |
||||||
|
placeholder: 'To Address', |
||||||
|
type: 'SingleLineText', |
||||||
|
required: true |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: 'subject', |
||||||
|
label: 'Subject', |
||||||
|
placeholder: 'Subject', |
||||||
|
type: 'SingleLineText', |
||||||
|
required: true |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: 'body', |
||||||
|
label: 'Body', |
||||||
|
placeholder: 'Body', |
||||||
|
type: 'LongText', |
||||||
|
required: true |
||||||
|
} |
||||||
|
], |
||||||
|
Slack: [ |
||||||
|
{ |
||||||
|
key: 'body', |
||||||
|
label: 'Body', |
||||||
|
placeholder: 'Body', |
||||||
|
type: 'LongText', |
||||||
|
required: true |
||||||
|
} |
||||||
|
], |
||||||
|
'Microsoft Teams': [ |
||||||
|
{ |
||||||
|
key: 'body', |
||||||
|
label: 'Body', |
||||||
|
placeholder: 'Body', |
||||||
|
type: 'LongText', |
||||||
|
required: true |
||||||
|
} |
||||||
|
], |
||||||
|
Discord: [ |
||||||
|
{ |
||||||
|
key: 'body', |
||||||
|
label: 'Body', |
||||||
|
placeholder: 'Body', |
||||||
|
type: 'LongText', |
||||||
|
required: true |
||||||
|
} |
||||||
|
], |
||||||
|
Mattermost: [ |
||||||
|
{ |
||||||
|
key: 'body', |
||||||
|
label: 'Body', |
||||||
|
placeholder: 'Body', |
||||||
|
type: 'LongText', |
||||||
|
required: true |
||||||
|
} |
||||||
|
], |
||||||
|
Twilio: [ |
||||||
|
{ |
||||||
|
key: 'body', |
||||||
|
label: 'Body', |
||||||
|
placeholder: 'Body', |
||||||
|
type: 'LongText', |
||||||
|
required: true |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: 'to', |
||||||
|
label: 'Comma separated Mobile #', |
||||||
|
placeholder: 'Comma separated Mobile #', |
||||||
|
type: 'LongText', |
||||||
|
required: true |
||||||
|
} |
||||||
|
], |
||||||
|
'Whatsapp Twilio': [ |
||||||
|
{ |
||||||
|
key: 'body', |
||||||
|
label: 'Body', |
||||||
|
placeholder: 'Body', |
||||||
|
type: 'LongText', |
||||||
|
required: true |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: 'to', |
||||||
|
label: 'Comma separated Mobile #', |
||||||
|
placeholder: 'Comma separated Mobile #', |
||||||
|
type: 'LongText', |
||||||
|
required: true |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
|
||||||
|
}), |
||||||
|
created() { |
||||||
|
this.loadPluginList() |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async loadPluginList() { |
||||||
|
try { |
||||||
|
// const plugins = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginList']) |
||||||
|
const plugins = (await this.$api.plugin.list()).list |
||||||
|
// plugins.push(...plugins.splice(0, 3)) |
||||||
|
this.apps = plugins.reduce((o, p) => { |
||||||
|
p.tags = p.tags ? p.tags.split(',') : [] |
||||||
|
p.parsedInput = p.input && JSON.parse(p.input) |
||||||
|
o[p.title] = p |
||||||
|
return o |
||||||
|
}, {}) |
||||||
|
} catch (e) {} |
||||||
|
}, |
||||||
|
addNewHook() { |
||||||
|
this.onEventChange() |
||||||
|
this.$refs.form.resetValidation() |
||||||
|
}, |
||||||
|
async onNotTypeChange() { |
||||||
|
this.notification = {} |
||||||
|
if (this.hook.notification.type === 'Slack') { |
||||||
|
this.slackChannels = |
||||||
|
(this.apps && this.apps.Slack && this.apps.Slack.parsedInput) || [] |
||||||
|
} |
||||||
|
if (this.hook.notification.type === 'Microsoft Teams') { |
||||||
|
this.teamsChannels = |
||||||
|
(this.apps && |
||||||
|
this.apps['Microsoft Teams'] && |
||||||
|
this.apps['Microsoft Teams'].parsedInput) || |
||||||
|
[] |
||||||
|
} |
||||||
|
if (this.hook.notification.type === 'Discord') { |
||||||
|
this.discordChannels = |
||||||
|
(this.apps && this.apps.Discord && this.apps.Discord.parsedInput) || |
||||||
|
[] |
||||||
|
} |
||||||
|
if (this.hook.notification.type === 'Mattermost') { |
||||||
|
this.mattermostChannels = |
||||||
|
(this.apps && |
||||||
|
this.apps.Mattermost && |
||||||
|
this.apps.Mattermost.parsedInput) || |
||||||
|
[] |
||||||
|
} |
||||||
|
if (this.hook.notification.type === 'URL') { |
||||||
|
this.notification = this.notification || {} |
||||||
|
this.$set(this.notification, 'body', '{{ json data }}') |
||||||
|
} |
||||||
|
this.$nextTick(() => this.$refs.form.validate()) |
||||||
|
}, |
||||||
|
async onEventChange() { |
||||||
|
const { notification: { payload, type } = {}, ...hook } = this.hook |
||||||
|
|
||||||
|
this.hook = { |
||||||
|
...hook, |
||||||
|
notification: { |
||||||
|
type |
||||||
|
} |
||||||
|
} |
||||||
|
// this.enableCondition = !!(this.hook && this.hook.condition && Object.keys(this.hook.condition).length) |
||||||
|
await this.onNotTypeChange() |
||||||
|
this.notification = payload |
||||||
|
if (this.hook.notification.type === 'Slack') { |
||||||
|
this.notification.webhook_url = |
||||||
|
this.notification.webhook_url && |
||||||
|
this.notification.webhook_url.map(v => |
||||||
|
this.slackChannels.find(s => v.webhook_url === s.webhook_url) |
||||||
|
) |
||||||
|
} |
||||||
|
if (this.hook.notification.type === 'Microsoft Teams') { |
||||||
|
this.notification.webhook_url = |
||||||
|
this.notification.webhook_url && |
||||||
|
this.notification.webhook_url.map(v => |
||||||
|
this.teamsChannels.find(s => v.webhook_url === s.webhook_url) |
||||||
|
) |
||||||
|
} |
||||||
|
if (this.hook.notification.type === 'Discord') { |
||||||
|
this.notification.webhook_url = |
||||||
|
this.notification.webhook_url && |
||||||
|
this.notification.webhook_url.map(v => |
||||||
|
this.discordChannels.find(s => v.webhook_url === s.webhook_url) |
||||||
|
) |
||||||
|
} |
||||||
|
if (this.hook.notification.type === 'Mattermost') { |
||||||
|
this.notification.webhook_url = |
||||||
|
this.notification.webhook_url && |
||||||
|
this.notification.webhook_url.map(v => |
||||||
|
this.mattermostChannels.find(s => v.webhook_url === s.webhook_url) |
||||||
|
) |
||||||
|
} |
||||||
|
if (this.hook.notification.type === 'URL') { |
||||||
|
this.notification = this.notification || {} |
||||||
|
this.$set(this.notification, 'body', this.notification.body || '{{ json data }}') |
||||||
|
} |
||||||
|
}, |
||||||
|
async saveHooks() { |
||||||
|
if (!this.$refs.form.validate() || !this.valid || !this.hook.event) { |
||||||
|
return |
||||||
|
} |
||||||
|
this.loading = true |
||||||
|
try { |
||||||
|
let res |
||||||
|
if (this.hook.id) { |
||||||
|
res = await this.$api.dbTableWebhook.update(this.hook.id, { |
||||||
|
...this.hook, |
||||||
|
notification: { |
||||||
|
...this.hook.notification, |
||||||
|
payload: this.notification |
||||||
|
} |
||||||
|
}) |
||||||
|
} else { |
||||||
|
res = await this.$api.dbTableWebhook.create(this.meta.id, { |
||||||
|
...this.hook, |
||||||
|
notification: { |
||||||
|
...this.hook.notification, |
||||||
|
payload: this.notification |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
if (!this.hook.id && res) { |
||||||
|
this.hook.id = res.id |
||||||
|
} |
||||||
|
if (this.$refs.filter) { |
||||||
|
await this.$refs.filter.applyChanges(false, { |
||||||
|
hookId: this.hook.id |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
this.$toast |
||||||
|
.success('Webhook details updated successfully') |
||||||
|
.goAway(3000) |
||||||
|
} catch (e) { |
||||||
|
this.$toast.error(e.message).goAway(3000) |
||||||
|
} |
||||||
|
this.loading = false |
||||||
|
|
||||||
|
this.$e('a:webhook:add', { |
||||||
|
operation: this.hook.operation, |
||||||
|
condition: this.hook.condition, |
||||||
|
notification: this.hook.notification.type |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
/deep/ .nc-filter-wrapper label { |
||||||
|
font-size: 0.75rem !important; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,186 @@ |
|||||||
|
<template> |
||||||
|
<div> |
||||||
|
<v-card-title> |
||||||
|
Webhooks |
||||||
|
<v-spacer /> |
||||||
|
<v-btn |
||||||
|
outlined |
||||||
|
tooltip="Save" |
||||||
|
small |
||||||
|
@click.prevent="$emit('add')" |
||||||
|
> |
||||||
|
Create webhook |
||||||
|
</v-btn> |
||||||
|
</v-card-title> |
||||||
|
|
||||||
|
<div v-if="hooks " class="pa-4"> |
||||||
|
<template v-if=" hooks.length"> |
||||||
|
<v-card v-for="(hook,i) in hooks" :key="hook.id" class="elevation-0 backgroundColor nc-hook" @click="$emit('edit', hook)"> |
||||||
|
<div class="pa-4 "> |
||||||
|
<h4 class="nc-text"> |
||||||
|
{{ hook.title }} |
||||||
|
</h4> |
||||||
|
<div class="d-flex"> |
||||||
|
<!--Title--> |
||||||
|
<span class="caption textColor1--text">{{ $t("general.event") }} : {{ hook.event }} {{ |
||||||
|
hook.operation |
||||||
|
}}</span> |
||||||
|
<v-spacer /> |
||||||
|
<!--Notify Via--> |
||||||
|
<span class="caption textColor1--text">{{ |
||||||
|
$t("labels.notifyVia") |
||||||
|
}} : {{ hook.notification && hook.notification.type }} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<v-icon class="nc-hook-delete-icon" small @click.stop="deleteHook(hook,i)"> |
||||||
|
mdi-delete-outline |
||||||
|
</v-icon> |
||||||
|
</v-card> |
||||||
|
</template> |
||||||
|
<div v-else class="pa-4 backgroundColor caption textColor--text text--lighten-3"> |
||||||
|
Webhooks list is empty, create new webhook by clicking 'Create webhook' button. |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- <v-simple-table dense> |
||||||
|
<template #default> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th> |
||||||
|
<!–Title–> |
||||||
|
{{ $t("general.title") }} |
||||||
|
</th> |
||||||
|
<th> |
||||||
|
<!–Event–> |
||||||
|
{{ $t("general.event") }} |
||||||
|
</th> |
||||||
|
<th> |
||||||
|
<!–Condition–> |
||||||
|
{{ $t("general.condition") }} |
||||||
|
</th> |
||||||
|
<th> |
||||||
|
<!–Notify Via–> |
||||||
|
{{ $t("labels.notifyVia") }} |
||||||
|
</th> |
||||||
|
<th> |
||||||
|
<!–Action–> |
||||||
|
{{ $t("labels.action") }} |
||||||
|
</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
|
||||||
|
<tbody> |
||||||
|
<template v-if="hooks && hooks.length"> |
||||||
|
<tr v-for="(item, i) in hooks" :key="i"> |
||||||
|
<td>{{ item.title }}</td> |
||||||
|
<td>{{ item.event }} {{ item.operation }}</td> |
||||||
|
<td> |
||||||
|
<v-icon v-if="item.condition" color="success" small> |
||||||
|
mdi-check-bold |
||||||
|
</v-icon> |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
{{ item.notification && item.notification.type }} |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
<x-icon |
||||||
|
small |
||||||
|
color="error" |
||||||
|
@click.stop="deleteHook(item, i)" |
||||||
|
> |
||||||
|
mdi-delete |
||||||
|
</x-icon> |
||||||
|
<!– <x-icon small :color="loading || !valid || !hook.event ? 'grey' : 'primary'" |
||||||
|
@click.stop="(!loading && valid && hook.event) && saveHooks()">save |
||||||
|
</x-icon>–> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
</template> |
||||||
|
<tr> |
||||||
|
<td colspan="6" class="text-center py-5"> |
||||||
|
<!–:tooltip="$t('tooltip.saveChanges')"–> |
||||||
|
<x-btn |
||||||
|
v-ge="['hooks', 'add new']" |
||||||
|
outlined |
||||||
|
color="primary" |
||||||
|
small |
||||||
|
@click.prevent="$emit('add')" |
||||||
|
> |
||||||
|
<v-icon small left> |
||||||
|
mdi-plus |
||||||
|
</v-icon> |
||||||
|
<!–Add New Webhook–> |
||||||
|
{{ $t("activity.addWebhook") }} |
||||||
|
</x-btn> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
</tbody> |
||||||
|
</template> |
||||||
|
</v-simple-table>--> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
name: 'WebhookList', |
||||||
|
props: { meta: Object }, |
||||||
|
data: () => ({ |
||||||
|
hooks: null, loading: false |
||||||
|
}), |
||||||
|
mounted() { |
||||||
|
this.loadHooksList() |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async loadHooksList() { |
||||||
|
this.key++ |
||||||
|
this.loading = true |
||||||
|
|
||||||
|
const hooks = await this.$api.dbTableWebhook.list(this.meta.id) |
||||||
|
|
||||||
|
this.hooks = hooks.list.map((h) => { |
||||||
|
h.notification = h.notification && JSON.parse(h.notification) |
||||||
|
return h |
||||||
|
}) |
||||||
|
this.loading = false |
||||||
|
}, |
||||||
|
async deleteHook(item, i) { |
||||||
|
try { |
||||||
|
if (item.id) { |
||||||
|
await this.$api.dbTableWebhook.delete(item.id) |
||||||
|
this.hooks.splice(i, 1) |
||||||
|
} else { |
||||||
|
this.hooks.splice(i, 1) |
||||||
|
} |
||||||
|
this.$toast.success('Hook deleted successfully').goAway(3000) |
||||||
|
if (!this.hooks.length) { |
||||||
|
this.hook = null |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
this.$toast.error(e.message).goAway(3000) |
||||||
|
} |
||||||
|
|
||||||
|
this.$e('a:webhook:delete') |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.nc-hook { |
||||||
|
position: relative; |
||||||
|
|
||||||
|
.nc-hook-delete-icon { |
||||||
|
position: absolute; |
||||||
|
opacity: 0; |
||||||
|
transition: .3s opacity; |
||||||
|
right: 16px; |
||||||
|
top: 16px |
||||||
|
} |
||||||
|
|
||||||
|
&:hover .nc-hook-delete-icon { |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,53 @@ |
|||||||
|
<template> |
||||||
|
<v-dialog v-model="webhookModal" width="min(700px,90%)" overlay-opacity=".9"> |
||||||
|
<v-card |
||||||
|
v-if="webhookModal" |
||||||
|
width="100%" |
||||||
|
min-height="350px" |
||||||
|
class="pa-4" |
||||||
|
> |
||||||
|
<webhook-editor v-if="editOrAdd" ref="editor" :meta="meta" @backToList="editOrAdd = false" /> |
||||||
|
<webhook-list v-else :meta="meta" @edit="editHook" @add="editOrAdd = true" /> |
||||||
|
</v-card> |
||||||
|
</v-dialog> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
|
||||||
|
import WebhookList from '~/components/project/tableTabs/webhook/webhookList' |
||||||
|
import WebhookEditor from '~/components/project/tableTabs/webhook/webhookEditor' |
||||||
|
export default { |
||||||
|
name: 'WebhookModal', |
||||||
|
components: { WebhookEditor, WebhookList }, |
||||||
|
props: { |
||||||
|
meta: Object, |
||||||
|
value: Boolean |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
editOrAdd: false, |
||||||
|
activePage: 'role' |
||||||
|
}), |
||||||
|
computed: { |
||||||
|
webhookModal: { |
||||||
|
get() { |
||||||
|
return this.value |
||||||
|
}, |
||||||
|
set(v) { |
||||||
|
this.$emit('input', v) |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
editHook(hook) { |
||||||
|
this.editOrAdd = true |
||||||
|
this.$nextTick(() => { |
||||||
|
this.$refs.editor.hook = { ...hook } |
||||||
|
this.$refs.editor.onEventChange() |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
</style> |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,45 @@ |
|||||||
|
--- |
||||||
|
title: 'Code Snippets' |
||||||
|
description: 'Code Snippets' |
||||||
|
position: 540 |
||||||
|
category: 'Product' |
||||||
|
menuTitle: 'Code Snippets' |
||||||
|
--- |
||||||
|
|
||||||
|
## Overview |
||||||
|
|
||||||
|
Open a Project, Select a Table and Click `Get API Snippet` on the bottom right area. |
||||||
|
|
||||||
|
<img width="1335" alt="image" src="https://user-images.githubusercontent.com/35857179/166663362-43ffe3cc-1053-4bf6-a65e-e4b8bae69fcb.png"> |
||||||
|
|
||||||
|
A modal box will be shown with sample code snippet for List API. |
||||||
|
|
||||||
|
![image](https://user-images.githubusercontent.com/35857179/166663478-3f802012-7bdc-4265-9ffe-6e51c4bcf4cd.png) |
||||||
|
|
||||||
|
|
||||||
|
## Supported Snippet |
||||||
|
|
||||||
|
### Shell |
||||||
|
- cURL |
||||||
|
- wget |
||||||
|
### Javascript |
||||||
|
- Axios |
||||||
|
- Fetch |
||||||
|
- jQuery |
||||||
|
- XHR |
||||||
|
### Node |
||||||
|
- Axios |
||||||
|
- Fetch |
||||||
|
- Request |
||||||
|
- Native |
||||||
|
- Unirest |
||||||
|
### NocoDB SDK |
||||||
|
- Javascript |
||||||
|
- Node |
||||||
|
### PHP |
||||||
|
### Python |
||||||
|
- http.client |
||||||
|
- request |
||||||
|
### Ruby |
||||||
|
### Java |
||||||
|
### C |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue