Browse Source

chore(gui-v2): fix types in components

pull/3044/head
braks 2 years ago
parent
commit
56148802e7
  1. 17
      packages/nc-gui-v2/components/cell/Rating.vue
  2. 11
      packages/nc-gui-v2/components/cell/TextArea.vue
  3. 38
      packages/nc-gui-v2/components/smartsheet-column/FormulaOptions.vue
  4. 6
      packages/nc-gui-v2/components/smartsheet/sidebar/toolbar/Reload.vue
  5. 9
      packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue
  6. 17
      packages/nc-gui-v2/components/virtual-cell/HasMany.vue
  7. 21
      packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue
  8. 3
      packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue
  9. 104
      packages/nc-gui-v2/components/webhook/Editor.vue

17
packages/nc-gui-v2/components/cell/Rating.vue

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject, useVModel } from '#imports' import { computed, inject } from '#imports'
import { ColumnInj } from '~/context' import { ColumnInj } from '~/context'
interface Props { interface Props {
@ -7,13 +7,11 @@ interface Props {
readOnly?: boolean readOnly?: boolean
} }
const props = withDefaults(defineProps<Props>(), { const { modelValue, readOnly } = defineProps<Props>()
modelValue: NaN,
})
const emits = defineEmits(['update:modelValue']) const emits = defineEmits(['update:modelValue'])
const column = inject(ColumnInj) const column = inject(ColumnInj)!
const ratingMeta = computed(() => { const ratingMeta = computed(() => {
return { return {
@ -23,15 +21,18 @@ const ratingMeta = computed(() => {
}, },
color: '#fcb401', color: '#fcb401',
max: 5, max: 5,
...(column?.value?.meta || {}), ...(column.value?.meta || {}),
} }
}) })
const vModel = useVModel(props, 'modelValue', emits) const vModel = computed({
get: () => modelValue ?? NaN,
set: (val) => emits('update:modelValue', val),
})
</script> </script>
<template> <template>
<a-rate v-model:value="vModel" :count="ratingMeta.max" :style="`color: ${ratingMeta.color}`" :disabled="props.readOnly"> <a-rate v-model:value="vModel" :count="ratingMeta.max" :style="`color: ${ratingMeta.color}`" :disabled="readOnly">
<template #character> <template #character>
<MdiStar v-if="ratingMeta.icon.full === 'mdi-star'" class="text-sm" /> <MdiStar v-if="ratingMeta.icon.full === 'mdi-star'" class="text-sm" />
<MdiHeart v-if="ratingMeta.icon.full === 'mdi-heart'" class="text-sm" /> <MdiHeart v-if="ratingMeta.icon.full === 'mdi-heart'" class="text-sm" />

11
packages/nc-gui-v2/components/cell/TextArea.vue

@ -1,21 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import type { VNodeRef } from '@vue/runtime-core' import type { VNodeRef } from '@vue/runtime-core'
import { inject, ref, useVModel } from '#imports' import { computed, inject, ref } from '#imports'
import { EditModeInj } from '~/context' import { EditModeInj } from '~/context'
interface Props { interface Props {
modelValue: string | null modelValue: string | null
} }
const props = withDefaults(defineProps<Props>(), { const { modelValue } = defineProps<Props>()
modelValue: '',
})
const emits = defineEmits(['update:modelValue']) const emits = defineEmits(['update:modelValue'])
const editEnabled = inject(EditModeInj, ref(false)) const editEnabled = inject(EditModeInj, ref(false))
const vModel = useVModel(props, 'modelValue', emits) const vModel = computed({
get: () => modelValue ?? '',
set: (value) => emits('update:modelValue', value),
})
const focus: VNodeRef = (el) => (el as HTMLTextAreaElement)?.focus() const focus: VNodeRef = (el) => (el as HTMLTextAreaElement)?.focus()
</script> </script>

38
packages/nc-gui-v2/components/smartsheet-column/FormulaOptions.vue

@ -4,7 +4,7 @@ import type { ListItem as AntListItem } from 'ant-design-vue'
import jsep from 'jsep' import jsep from 'jsep'
import type { ColumnType } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import { UITypes, jsepCurlyHook } from 'nocodb-sdk' import { UITypes, jsepCurlyHook } from 'nocodb-sdk'
import { useColumnCreateStoreOrThrow, useDebounceFn } from '#imports' import { onMounted, useColumnCreateStoreOrThrow, useDebounceFn } from '#imports'
import { MetaInj } from '~/context' import { MetaInj } from '~/context'
import { import {
NcAutocompleteTree, NcAutocompleteTree,
@ -16,8 +16,6 @@ import {
insertAtCursor, insertAtCursor,
validateDateWithUnknownFormat, validateDateWithUnknownFormat,
} from '@/utils' } from '@/utils'
import MdiFunctionIcon from '~icons/mdi/function'
import MdiOperatorIcon from '~icons/mdi/calculator'
enum JSEPNode { enum JSEPNode {
COMPOUND = 'Compound', COMPOUND = 'Compound',
@ -31,8 +29,7 @@ enum JSEPNode {
ARRAY_EXP = 'ArrayExpression', ARRAY_EXP = 'ArrayExpression',
} }
const { formState, validateInfos, setAdditionalValidations, sqlUi, onDataTypeChange, onAlter, column } = const { formState, validateInfos, setAdditionalValidations, sqlUi, column } = useColumnCreateStoreOrThrow()
useColumnCreateStoreOrThrow()
const meta = inject(MetaInj) const meta = inject(MetaInj)
@ -72,8 +69,6 @@ const wordToComplete = ref<string | undefined>('')
const selected = ref(0) const selected = ref(0)
const tooltip = ref(true)
const sortOrder: Record<string, number> = { const sortOrder: Record<string, number> = {
column: 0, column: 0,
function: 1, function: 1,
@ -95,7 +90,7 @@ const suggestionsList = computed(() => {
...columns.value ...columns.value
.filter( .filter(
(c: Record<string, any>) => (c: Record<string, any>) =>
!column || (column.id !== c.id && !(c.uidt === UITypes.LinkToAnotherRecord && c.system === 1)), !column || (column.value.id !== c.id && !(c.uidt === UITypes.LinkToAnotherRecord && c.system === 1)),
) )
.map((c: any) => ({ .map((c: any) => ({
text: c.title, text: c.title,
@ -230,7 +225,7 @@ function validateAgainstMeta(parsedTree: any, errors = new Set(), typeErrors = n
} else if (parsedTree.type === JSEPNode.IDENTIFIER) { } else if (parsedTree.type === JSEPNode.IDENTIFIER) {
if ( if (
columns.value columns.value
.filter((c: Record<string, any>) => !column || column.id !== c.id) .filter((c: Record<string, any>) => !column || column.value.id !== c.id)
.every((c: Record<string, any>) => c.title !== parsedTree.name) .every((c: Record<string, any>) => c.title !== parsedTree.name)
) { ) {
errors.add(`Column '${parsedTree.name}' is not available`) errors.add(`Column '${parsedTree.name}' is not available`)
@ -241,7 +236,7 @@ function validateAgainstMeta(parsedTree: any, errors = new Set(), typeErrors = n
// get all formula columns excluding itself // get all formula columns excluding itself
const formulaPaths = columns.value const formulaPaths = columns.value
.filter((c: Record<string, any>) => c.id !== column?.id && c.uidt === UITypes.Formula) .filter((c: Record<string, any>) => c.id !== column?.value.id && c.uidt === UITypes.Formula)
.reduce((res: Record<string, any>[], c: Record<string, any>) => { .reduce((res: Record<string, any>[], c: Record<string, any>) => {
// in `formula`, get all the target neighbours // in `formula`, get all the target neighbours
// i.e. all column id (e.g. cl_xxxxxxxxxxxxxx) with formula type // i.e. all column id (e.g. cl_xxxxxxxxxxxxxx) with formula type
@ -256,9 +251,10 @@ function validateAgainstMeta(parsedTree: any, errors = new Set(), typeErrors = n
}, []) }, [])
// include target formula column (i.e. the one to be saved if applicable) // include target formula column (i.e. the one to be saved if applicable)
const targetFormulaCol = columns.value.find((c: ColumnType) => c.title === parsedTree.name && c.uidt === UITypes.Formula) const targetFormulaCol = columns.value.find((c: ColumnType) => c.title === parsedTree.name && c.uidt === UITypes.Formula)
if (targetFormulaCol) {
if (targetFormulaCol && column?.value.id) {
formulaPaths.push({ formulaPaths.push({
[column.id]: [targetFormulaCol.id], [column.value.id]: [targetFormulaCol.id],
}) })
} }
const vertices = formulaPaths.length const vertices = formulaPaths.length
@ -267,6 +263,7 @@ function validateAgainstMeta(parsedTree: any, errors = new Set(), typeErrors = n
const adj = new Map() const adj = new Map()
const inDegrees = new Map() const inDegrees = new Map()
// init adjacency list & indegree // init adjacency list & indegree
for (const [_, v] of Object.entries(formulaPaths)) { for (const [_, v] of Object.entries(formulaPaths)) {
const src = Object.keys(v)[0] const src = Object.keys(v)[0]
const neighbours = v[src] const neighbours = v[src]
@ -346,12 +343,14 @@ function validateAgainstType(parsedTree: any, expectedType: string, func: any, t
} }
} }
} else if (parsedTree.type === JSEPNode.IDENTIFIER) { } else if (parsedTree.type === JSEPNode.IDENTIFIER) {
const col = columns.value.find((c) => c.title === parsedTree.name) as Record<string, any> const col = columns.value.find((c) => c.title === parsedTree.name)
if (col === undefined) { if (col === undefined) {
return return
} }
if (col.uidt === UITypes.Formula) { if (col.uidt === UITypes.Formula) {
const foundType = getRootDataType(jsep(col?.formula_raw)) const foundType = getRootDataType(jsep((col as any).formula_raw))
if (foundType === 'N/A') { if (foundType === 'N/A') {
typeErrors.add(`Not supported to reference column ${col.title}`) typeErrors.add(`Not supported to reference column ${col.title}`)
} else if (expectedType !== foundType) { } else if (expectedType !== foundType) {
@ -594,7 +593,7 @@ function getFormulaTypeName(type: string) {
} }
// set default value // set default value
formState.value.formula_raw = (column?.colOptions as Record<string, any>)?.formula_raw || '' formState.value.formula_raw = (column?.value?.colOptions as Record<string, any>)?.formula_raw || ''
// set additional validations // set additional validations
setAdditionalValidations({ setAdditionalValidations({
@ -659,19 +658,24 @@ onMounted(() => {
<div>({{ idx + 1 }}): {{ example }}</div> <div>({{ idx + 1 }}): {{ example }}</div>
</div> </div>
</template> </template>
<template #title> <template #title>
<div class="flex"> <div class="flex">
<div class="flex-1"> <div class="flex-1">
{{ item.text }} {{ item.text }}
</div> </div>
<div class=""> <div class="">
{{ getFormulaTypeName(item.type) }} {{ getFormulaTypeName(item.type) }}
</div> </div>
</div> </div>
</template> </template>
<template #avatar> <template #avatar>
<MdiFunctionIcon v-if="item.type === 'function'" class="text-lg" /> <MdiFunction v-if="item.type === 'function'" class="text-lg" />
<MdiOperatorIcon v-if="item.type === 'op'" class="text-lg" />
<MdiCalculator v-if="item.type === 'op'" class="text-lg" />
<component :is="item.icon" v-if="item.type === 'column'" class="text-lg" /> <component :is="item.icon" v-if="item.type === 'column'" class="text-lg" />
</template> </template>
</a-list-item-meta> </a-list-item-meta>

6
packages/nc-gui-v2/components/smartsheet/sidebar/toolbar/Reload.vue

@ -2,9 +2,11 @@
import { ReloadViewDataHookInj, RightSidebarInj } from '~/context' import { ReloadViewDataHookInj, RightSidebarInj } from '~/context'
import { inject, ref } from '#imports' import { inject, ref } from '#imports'
const reloadTri = inject(ReloadViewDataHookInj)! const reloadHook = inject(ReloadViewDataHookInj)!
const sidebarOpen = inject(RightSidebarInj, ref(true)) const sidebarOpen = inject(RightSidebarInj, ref(true))
const onClick = () => reloadHook.trigger()
</script> </script>
<template> <template>
@ -12,7 +14,7 @@ const sidebarOpen = inject(RightSidebarInj, ref(true))
<template #title> {{ $t('general.reload') }} </template> <template #title> {{ $t('general.reload') }} </template>
<div class="nc-sidebar-right-item hover:after:bg-green-500 group"> <div class="nc-sidebar-right-item hover:after:bg-green-500 group">
<MdiReload class="group-hover:(!text-white)" @click="reloadTri.trigger(null)" /> <MdiReload class="group-hover:(!text-white)" @click="onClick" />
</div> </div>
</a-tooltip> </a-tooltip>
</template> </template>

9
packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue

@ -10,14 +10,12 @@ const column = inject(ColumnInj)
const reloadTrigger = inject(ReloadViewDataHookInj)! const reloadTrigger = inject(ReloadViewDataHookInj)!
const cellValue = inject(CellValueInj) const cellValue = inject(CellValueInj, ref<any>(null))
const row = inject(RowInj) const row = inject(RowInj)
const active = false const active = false
const localState = null
const listItemsDlg = ref(false) const listItemsDlg = ref(false)
const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvideLTARStore( const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvideLTARStore(
@ -32,8 +30,8 @@ await loadRelatedTableMeta()
<template> <template>
<div class="flex w-full chips-wrapper align-center" :class="{ active }"> <div class="flex w-full chips-wrapper align-center" :class="{ active }">
<div class="chips d-flex align-center flex-grow"> <div class="chips d-flex align-center flex-grow">
<template v-if="cellValue || localState"> <template v-if="cellValue">
<ItemChip :item="cellValue" :value="cellValue[relatedTablePrimaryValueProp]" @unlink="unlink(cellValue || localState)" /> <ItemChip :item="cellValue" :value="cellValue[relatedTablePrimaryValueProp]" @unlink="unlink(cellValue)" />
</template> </template>
</div> </div>
<div class="flex-1 flex justify-end gap-1 min-h-[30px] align-center"> <div class="flex-1 flex justify-end gap-1 min-h-[30px] align-center">
@ -42,6 +40,7 @@ await loadRelatedTableMeta()
@click="listItemsDlg = true" @click="listItemsDlg = true"
/> />
</div> </div>
<ListItems v-model="listItemsDlg" /> <ListItems v-model="listItemsDlg" />
</div> </div>
</template> </template>

17
packages/nc-gui-v2/components/virtual-cell/HasMany.vue

@ -4,7 +4,7 @@ import type { Ref } from 'vue'
import ItemChip from './components/ItemChip.vue' import ItemChip from './components/ItemChip.vue'
import ListChildItems from './components/ListChildItems.vue' import ListChildItems from './components/ListChildItems.vue'
import ListItems from './components/ListItems.vue' import ListItems from './components/ListItems.vue'
import { inject, ref, useProvideLTARStore } from '#imports' import { computed, inject, ref, useProvideLTARStore } from '#imports'
import { CellValueInj, ColumnInj, ReloadViewDataHookInj, RowInj } from '~/context' import { CellValueInj, ColumnInj, ReloadViewDataHookInj, RowInj } from '~/context'
const column = inject(ColumnInj)! const column = inject(ColumnInj)!
@ -26,13 +26,26 @@ const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvid
) )
await loadRelatedTableMeta() await loadRelatedTableMeta()
const cells = computed(() =>
cellValue.value.reduce((acc: any[], curr: any) => {
if (!relatedTablePrimaryValueProp.value) return acc
const value = curr[relatedTablePrimaryValueProp.value]
if (!value) return acc
return [...acc, { value, item: curr }]
}, [] as any[]),
)
</script> </script>
<template> <template>
<div class="flex align-center items-center gap-1 w-full chips-wrapper"> <div class="flex align-center items-center gap-1 w-full chips-wrapper">
<div class="chips flex align-center img-container flex-grow hm-items flex-nowrap min-w-0 overflow-hidden"> <div class="chips flex align-center img-container flex-grow hm-items flex-nowrap min-w-0 overflow-hidden">
<template v-if="cellValue"> <template v-if="cellValue">
<ItemChip v-for="(ch, i) in cellValue" :key="i" :value="ch[relatedTablePrimaryValueProp]" @unlink="unlink(ch)" /> <ItemChip v-for="(cell, i) of cells" :key="i" :value="cell.value" @unlink="unlink(cell.item)" />
<span v-if="cellValue?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">more... </span> <span v-if="cellValue?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">more... </span>
</template> </template>
</div> </div>

21
packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue

@ -4,7 +4,7 @@ import type { Ref } from 'vue'
import ItemChip from './components/ItemChip.vue' import ItemChip from './components/ItemChip.vue'
import ListChildItems from './components/ListChildItems.vue' import ListChildItems from './components/ListChildItems.vue'
import ListItems from './components/ListItems.vue' import ListItems from './components/ListItems.vue'
import { inject, ref, useProvideLTARStore } from '#imports' import { computed, inject, ref, useProvideLTARStore } from '#imports'
import { CellValueInj, ColumnInj, ReloadViewDataHookInj, RowInj } from '~/context' import { CellValueInj, ColumnInj, ReloadViewDataHookInj, RowInj } from '~/context'
const column = inject(ColumnInj)! const column = inject(ColumnInj)!
@ -26,23 +26,38 @@ const { loadRelatedTableMeta, relatedTablePrimaryValueProp, unlink } = useProvid
) )
await loadRelatedTableMeta() await loadRelatedTableMeta()
const cells = computed(() =>
cellValue.value.reduce((acc: any[], curr: any) => {
if (!relatedTablePrimaryValueProp.value) return acc
const value = curr[relatedTablePrimaryValueProp.value]
if (!value) return acc
return [...acc, { value, item: curr }]
}, [] as any[]),
)
</script> </script>
<template> <template>
<div class="flex align-center gap-1 w-full h-full chips-wrapper"> <div class="flex align-center gap-1 w-full h-full chips-wrapper">
<!-- <template v-if="!isForm"> -->
<div class="chips flex align-center img-container flex-grow hm-items flex-nowrap min-w-0 overflow-hidden"> <div class="chips flex align-center img-container flex-grow hm-items flex-nowrap min-w-0 overflow-hidden">
<template v-if="cellValue"> <template v-if="cellValue">
<ItemChip v-for="(ch, i) in cellValue" :key="i" :value="ch[relatedTablePrimaryValueProp]" @unlink="unlink(ch)" /> <ItemChip v-for="(cell, i) of cells" :key="i" :value="cell.value" @unlink="unlink(cell.item)" />
<span v-if="cellValue?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">more... </span> <span v-if="cellValue?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">more... </span>
</template> </template>
</div> </div>
<div class="flex-1 flex justify-end gap-1 min-h-[30px] align-center"> <div class="flex-1 flex justify-end gap-1 min-h-[30px] align-center">
<MdiArrowExpand class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="childListDlg = true" /> <MdiArrowExpand class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="childListDlg = true" />
<MdiPlus class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="listItemsDlg = true" /> <MdiPlus class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500" @click="listItemsDlg = true" />
</div> </div>
<ListItems v-model="listItemsDlg" /> <ListItems v-model="listItemsDlg" />
<ListChildItems v-model="childListDlg" @attach-record=";(childListDlg = false), (listItemsDlg = true)" /> <ListChildItems v-model="childListDlg" @attach-record=";(childListDlg = false), (listItemsDlg = true)" />
</div> </div>
</template> </template>

3
packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue

@ -41,7 +41,8 @@ const unlinkRow = async (row: Record<string, any>) => {
<a-button type="primary" size="small" @click="emit('attachRecord')"> <a-button type="primary" size="small" @click="emit('attachRecord')">
<div class="flex align-center gap-1"> <div class="flex align-center gap-1">
<MdiLinkVariantRemove class="text-xs text-white" @click="unlinkRow(row)" /> <!-- todo: row is not defined? @click="unlinkRow(row)" -->
<MdiLinkVariantRemove class="text-xs text-white" />
Link to '{{ meta.title }}' Link to '{{ meta.title }}'
</div> </div>
</a-button> </a-button>

104
packages/nc-gui-v2/components/webhook/Editor.vue

@ -2,20 +2,8 @@
import { Form } from 'ant-design-vue' import { Form } from 'ant-design-vue'
import { useToast } from 'vue-toastification' import { useToast } from 'vue-toastification'
import { MetaInj } from '~/context' import { MetaInj } from '~/context'
import MdiContentSaveIcon from '~icons/mdi/content-save' import { extractSdkResponseErrorMsg, fieldRequiredValidator } from '~/utils'
import MdiLinkIcon from '~icons/mdi/link' import { inject, reactive, useApi, useNuxtApp } from '#imports'
import MdiEmailIcon from '~icons/mdi/email'
import MdiSlackIcon from '~icons/mdi/slack'
import MdiMicrosoftTeamsIcon from '~icons/mdi/microsoft-teams'
import MdiDiscordIcon from '~icons/mdi/discord'
import MdiChatIcon from '~icons/mdi/chat'
import MdiWhatsAppIcon from '~icons/mdi/whatsapp'
import MdiCellPhoneMessageIcon from '~icons/mdi/cellphone-message'
import MdiGestureDoubleTapIcon from '~icons/mdi/gesture-double-tap'
import MdiInformationIcon from '~icons/mdi/information'
import MdiArrowLeftBoldIcon from '~icons/mdi/arrow-left-bold'
import { fieldRequiredValidator } from '~/utils/validation'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
interface Option { interface Option {
label: string label: string
@ -24,7 +12,9 @@ interface Option {
const emit = defineEmits(['backToList', 'editOrAdd']) const emit = defineEmits(['backToList', 'editOrAdd'])
const { $state, $api, $e } = useNuxtApp() const { $e } = useNuxtApp()
const { api, isLoading: loading } = useApi()
const toast = useToast() const toast = useToast()
@ -32,12 +22,12 @@ const meta = inject(MetaInj)
const useForm = Form.useForm const useForm = Form.useForm
const hook = reactive({ const hook = reactive<Record<string, any>>({
id: '', id: '',
title: '', title: '',
event: '', event: '',
operation: '', operation: '',
eventOperation: undefined, eventOperation: '',
notification: { notification: {
type: 'URL', type: 'URL',
payload: { payload: {
@ -45,7 +35,7 @@ const hook = reactive({
body: '{{ json data }}', body: '{{ json data }}',
headers: [{}], headers: [{}],
parameters: [{}], parameters: [{}],
} as any, },
}, },
condition: false, condition: false,
}) })
@ -64,8 +54,6 @@ const discordChannels = ref<Record<string, any>[]>([])
const mattermostChannels = ref<Record<string, any>[]>([]) const mattermostChannels = ref<Record<string, any>[]>([])
const loading = ref(false)
const filters = ref([]) const filters = ref([])
const formInput = ref({ const formInput = ref({
@ -218,7 +206,7 @@ const validators = computed(() => {
}), }),
} }
}) })
const { resetFields, validate, validateInfos } = useForm(hook, validators) const { validate, validateInfos } = useForm(hook, validators)
function onNotTypeChange() { function onNotTypeChange() {
hook.notification.payload = {} as any hook.notification.payload = {} as any
@ -258,7 +246,7 @@ function setHook(newHook: any) {
} }
async function onEventChange() { async function onEventChange() {
const { notification: { payload = {}, type = {} } = {}, ...rest } = hook const { notification: { payload = {}, type = {} } = {} } = hook
Object.assign(hook, { Object.assign(hook, {
...hook, ...hook,
@ -305,7 +293,7 @@ async function onEventChange() {
async function loadPluginList() { async function loadPluginList() {
try { try {
const plugins = (await $api.plugin.list()).list as any const plugins = (await api.plugin.list()).list as any
apps.value = plugins.reduce((o: Record<string, any>[], p: Record<string, any>) => { apps.value = plugins.reduce((o: Record<string, any>[], p: Record<string, any>) => {
p.tags = p.tags ? p.tags.split(',') : [] p.tags = p.tags ? p.tags.split(',') : []
p.parsedInput = p.input && JSON.parse(p.input) p.parsedInput = p.input && JSON.parse(p.input)
@ -327,31 +315,30 @@ async function saveHooks() {
await validate() await validate()
} catch (_: any) { } catch (_: any) {
toast.error('Invalid Form') toast.error('Invalid Form')
loading.value = false loading.value = false
return return
} }
try { try {
let res let res
if (hook.id) { if (hook.id) {
res = await $api.dbTableWebhook.update(hook.id, { res = await api.dbTableWebhook.update(hook.id, {
...hook, ...hook,
notification: { notification: {
...hook.notification, ...hook.notification,
payload: hook.notification.payload, payload: hook.notification.payload,
}, },
} as any) })
} else { } else {
res = await $api.dbTableWebhook.create( res = await api.dbTableWebhook.create(meta!.value.id!, {
meta?.value.id as string, ...hook,
{ notification: {
...hook, ...hook.notification,
notification: { payload: hook.notification.payload,
...hook.notification, },
payload: hook.notification.payload, } as any)
},
} as any,
)
} }
if (!hook.id && res) { if (!hook.id && res) {
@ -371,6 +358,7 @@ async function saveHooks() {
} finally { } finally {
loading.value = false loading.value = false
} }
$e('a:webhook:add', { $e('a:webhook:add', {
operation: hook.operation, operation: hook.operation,
condition: hook.condition, condition: hook.condition,
@ -389,7 +377,9 @@ defineExpose({
watch( watch(
() => hook.eventOperation, () => hook.eventOperation,
(v) => { () => {
if (!hook.eventOperation) return
const [event, operation] = hook.eventOperation.split(' ') const [event, operation] = hook.eventOperation.split(' ')
hook.event = event hook.event = event
hook.operation = operation hook.operation = operation
@ -405,21 +395,21 @@ onMounted(() => {
<div class="mb-4"> <div class="mb-4">
<div class="float-left mt-2"> <div class="float-left mt-2">
<div class="flex items-center"> <div class="flex items-center">
<MdiArrowLeftBoldIcon class="mr-3 text-xl cursor-pointer" @click="emit('backToList')" /> <MdiArrowLeftBold class="mr-3 text-xl cursor-pointer" @click="emit('backToList')" />
<span class="inline text-xl font-bold">{{ meta.title }} : {{ hook.title || 'Webhooks' }} </span> <span class="inline text-xl font-bold">{{ meta.title }} : {{ hook.title || 'Webhooks' }} </span>
</div> </div>
</div> </div>
<div class="float-right mb-5"> <div class="float-right mb-5">
<a-button class="mr-3" size="large" @click="testWebhook"> <a-button class="mr-3" size="large" @click="testWebhook">
<div class="flex items-center"> <div class="flex items-center">
<MdiGestureDoubleTapIcon class="mr-2" /> <MdiGestureDoubleTap class="mr-2" />
<!-- TODO: i18n --> <!-- TODO: i18n -->
Test Webhook Test Webhook
</div> </div>
</a-button> </a-button>
<a-button type="primary" size="large" @click.prevent="saveHooks"> <a-button type="primary" size="large" @click.prevent="saveHooks">
<div class="flex items-center"> <div class="flex items-center">
<MdiContentSaveIcon class="mr-2" /> <MdiContentSave class="mr-2" />
<!-- Save --> <!-- Save -->
{{ $t('general.save') }} {{ $t('general.save') }}
</div> </div>
@ -456,14 +446,22 @@ onMounted(() => {
> >
<a-select-option v-for="(notificationOption, i) in notificationList" :key="i" :value="notificationOption.type"> <a-select-option v-for="(notificationOption, i) in notificationList" :key="i" :value="notificationOption.type">
<div class="flex items-center"> <div class="flex items-center">
<MdiLinkIcon v-if="notificationOption.type === 'URL'" class="mr-2" /> <MdiLink v-if="notificationOption.type === 'URL'" class="mr-2" />
<MdiEmailIcon v-if="notificationOption.type === 'Email'" class="mr-2" />
<MdiSlackIcon v-if="notificationOption.type === 'Slack'" class="mr-2" /> <MdiEmail v-if="notificationOption.type === 'Email'" class="mr-2" />
<MdiMicrosoftTeamsIcon v-if="notificationOption.type === 'Microsoft Teams'" class="mr-2" />
<MdiDiscordIcon v-if="notificationOption.type === 'Discord'" class="mr-2" /> <MdiSlack v-if="notificationOption.type === 'Slack'" class="mr-2" />
<MdiChatIcon v-if="notificationOption.type === 'Mattermost'" class="mr-2" />
<MdiWhatsAppIcon v-if="notificationOption.type === 'Whatsapp Twilio'" class="mr-2" /> <MdiMicrosoftTeams v-if="notificationOption.type === 'Microsoft Teams'" class="mr-2" />
<MdiCellPhoneMessageIcon v-if="notificationOption.type === 'Twilio'" class="mr-2" />
<MdiDiscord v-if="notificationOption.type === 'Discord'" class="mr-2" />
<MdiChat v-if="notificationOption.type === 'Mattermost'" class="mr-2" />
<MdiWhatsapp v-if="notificationOption.type === 'Whatsapp Twilio'" class="mr-2" />
<MdiCellphoneMessage v-if="notificationOption.type === 'Twilio'" class="mr-2" />
{{ notificationOption.type }} {{ notificationOption.type }}
</div> </div>
</a-select-option> </a-select-option>
@ -471,17 +469,20 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<a-row v-if="hook.notification.type === 'URL'" class="mb-5" type="flex" :gutter="[16, 0]"> <a-row v-if="hook.notification.type === 'URL'" class="mb-5" type="flex" :gutter="[16, 0]">
<a-col :span="6"> <a-col :span="6">
<a-select v-model:value="hook.notification.payload.method" size="large"> <a-select v-model:value="hook.notification.payload.method" size="large">
<a-select-option v-for="(method, i) in methodList" :key="i" :value="method.title">{{ method.title }}</a-select-option> <a-select-option v-for="(method, i) in methodList" :key="i" :value="method.title">{{ method.title }}</a-select-option>
</a-select> </a-select>
</a-col> </a-col>
<a-col :span="18"> <a-col :span="18">
<a-form-item v-bind="validateInfos['notification.payload.path']"> <a-form-item v-bind="validateInfos['notification.payload.path']">
<a-input v-model:value="hook.notification.payload.path" size="large" placeholder="http://example.com" /> <a-input v-model:value="hook.notification.payload.path" size="large" placeholder="http://example.com" />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <a-col :span="24">
<a-tabs v-model:activeKey="urlTabKey" centered> <a-tabs v-model:activeKey="urlTabKey" centered>
<a-tab-pane key="body" tab="Body"> <a-tab-pane key="body" tab="Body">
@ -503,6 +504,7 @@ onMounted(() => {
</a-tabs> </a-tabs>
</a-col> </a-col>
</a-row> </a-row>
<a-row v-if="hook.notification.type === 'Slack'" type="flex"> <a-row v-if="hook.notification.type === 'Slack'" type="flex">
<a-col :span="24"> <a-col :span="24">
<a-form-item v-bind="validateInfos['notification.channels']"> <a-form-item v-bind="validateInfos['notification.channels']">
@ -516,6 +518,7 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<a-row v-if="hook.notification.type === 'Microsoft Teams'" type="flex"> <a-row v-if="hook.notification.type === 'Microsoft Teams'" type="flex">
<a-col :span="24"> <a-col :span="24">
<a-form-item v-bind="validateInfos['notification.channels']"> <a-form-item v-bind="validateInfos['notification.channels']">
@ -529,6 +532,7 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<a-row v-if="hook.notification.type === 'Discord'" type="flex"> <a-row v-if="hook.notification.type === 'Discord'" type="flex">
<a-col :span="24"> <a-col :span="24">
<a-form-item v-bind="validateInfos['notification.channels']"> <a-form-item v-bind="validateInfos['notification.channels']">
@ -542,6 +546,7 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<a-row v-if="hook.notification.type === 'Mattermost'" type="flex"> <a-row v-if="hook.notification.type === 'Mattermost'" type="flex">
<a-col :span="24"> <a-col :span="24">
<a-form-item v-bind="validateInfos['notification.channels']"> <a-form-item v-bind="validateInfos['notification.channels']">
@ -555,6 +560,7 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<a-row v-if="formInput[hook.notification.type] && hook.notification.payload" type="flex"> <a-row v-if="formInput[hook.notification.type] && hook.notification.payload" type="flex">
<a-col v-for="(input, i) in formInput[hook.notification.type]" :key="i" :span="24"> <a-col v-for="(input, i) in formInput[hook.notification.type]" :key="i" :span="24">
<a-form-item v-if="input.type === 'LongText'" v-bind="validateInfos[`notification.payload.${input.key}`]"> <a-form-item v-if="input.type === 'LongText'" v-bind="validateInfos[`notification.payload.${input.key}`]">
@ -565,6 +571,7 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<a-row class="mb-5" type="flex"> <a-row class="mb-5" type="flex">
<a-col :span="24"> <a-col :span="24">
<a-card> <a-card>
@ -573,6 +580,7 @@ onMounted(() => {
</a-card> </a-card>
</a-col> </a-col>
</a-row> </a-row>
<a-row> <a-row>
<a-col :span="24"> <a-col :span="24">
<div class="text-gray-600"> <div class="text-gray-600">
@ -582,7 +590,7 @@ onMounted(() => {
<template #title> <template #title>
<span> <strong>data</strong> : Row data <br /> </span> <span> <strong>data</strong> : Row data <br /> </span>
</template> </template>
<MdiInformationIcon class="ml-2" /> <MdiInformation class="ml-2" />
</a-tooltip> </a-tooltip>
<div class="mt-3"> <div class="mt-3">

Loading…
Cancel
Save