Improve Integration Cards (#643)
* Add integration action components and update integration JSON files - Introduced new action components for Email, Slack, Discord, Webhook, and Google Sheets integrations, enhancing the user interface for managing these integrations. - Updated the `integrations.json` files to include `actions_file_name` for each integration, linking them to their respective action components. - Improved the `GoogleSheetsIntegrationActions.vue` component by replacing direct provider information display with a badge for better UI consistency. These changes aim to streamline integration management and improve the overall user experience. * Minor UI fixes --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
49c2d6bf04
commit
c1ee072b71
|
|
@ -4,6 +4,7 @@
|
||||||
"icon": "heroicons:envelope-20-solid",
|
"icon": "heroicons:envelope-20-solid",
|
||||||
"section_name": "Notifications",
|
"section_name": "Notifications",
|
||||||
"file_name": "EmailIntegration",
|
"file_name": "EmailIntegration",
|
||||||
|
"actions_file_name": "EmailIntegrationActions",
|
||||||
"is_pro": false,
|
"is_pro": false,
|
||||||
"crisp_help_page_slug": "can-i-receive-notifications-on-form-submissions-134svqv"
|
"crisp_help_page_slug": "can-i-receive-notifications-on-form-submissions-134svqv"
|
||||||
},
|
},
|
||||||
|
|
@ -12,6 +13,7 @@
|
||||||
"icon": "mdi:slack",
|
"icon": "mdi:slack",
|
||||||
"section_name": "Notifications",
|
"section_name": "Notifications",
|
||||||
"file_name": "SlackIntegration",
|
"file_name": "SlackIntegration",
|
||||||
|
"actions_file_name": "SlackIntegrationActions",
|
||||||
"is_pro": true
|
"is_pro": true
|
||||||
},
|
},
|
||||||
"discord": {
|
"discord": {
|
||||||
|
|
@ -19,6 +21,7 @@
|
||||||
"icon": "ic:baseline-discord",
|
"icon": "ic:baseline-discord",
|
||||||
"section_name": "Notifications",
|
"section_name": "Notifications",
|
||||||
"file_name": "DiscordIntegration",
|
"file_name": "DiscordIntegration",
|
||||||
|
"actions_file_name": "DiscordIntegrationActions",
|
||||||
"is_pro": true
|
"is_pro": true
|
||||||
},
|
},
|
||||||
"webhook": {
|
"webhook": {
|
||||||
|
|
@ -26,6 +29,7 @@
|
||||||
"icon": "material-symbols:webhook",
|
"icon": "material-symbols:webhook",
|
||||||
"section_name": "Automation",
|
"section_name": "Automation",
|
||||||
"file_name": "WebhookIntegration",
|
"file_name": "WebhookIntegration",
|
||||||
|
"actions_file_name": "WebhookIntegrationActions",
|
||||||
"is_pro": false
|
"is_pro": false
|
||||||
},
|
},
|
||||||
"zapier": {
|
"zapier": {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-1 items-center">
|
||||||
|
<div
|
||||||
|
v-if="integration"
|
||||||
|
class="hidden md:block space-y-1"
|
||||||
|
>
|
||||||
|
<UBadge
|
||||||
|
:label="mentionAsText(integration.data.message)"
|
||||||
|
color="gray"
|
||||||
|
size="xs"
|
||||||
|
class="max-w-[300px] truncate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { mentionAsText } from '~/lib/utils.js'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
integration: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const formIntegrationsStore = useFormIntegrationsStore()
|
||||||
|
let interval = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!props.integration.data || props.integration.data.length === 0) {
|
||||||
|
interval = setInterval(() => formIntegrationsStore.fetchFormIntegrations(props.form.id), 3000)
|
||||||
|
setTimeout(() => { clearInterval(interval) }, 30000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (interval) {
|
||||||
|
clearInterval(interval)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-1 items-center">
|
||||||
|
<div
|
||||||
|
v-if="integration"
|
||||||
|
class="hidden md:block space-y-1"
|
||||||
|
>
|
||||||
|
<UBadge
|
||||||
|
:label="mentionAsText(integration.data.subject)"
|
||||||
|
color="gray"
|
||||||
|
size="xs"
|
||||||
|
class="max-w-[300px] block truncate"
|
||||||
|
/>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<UBadge
|
||||||
|
:label="firstEmail"
|
||||||
|
color="white"
|
||||||
|
size="xs"
|
||||||
|
class="max-w-[300px] block truncate"
|
||||||
|
/>
|
||||||
|
<UBadge
|
||||||
|
v-if="additionalEmailsCount > 0"
|
||||||
|
:label="`+${additionalEmailsCount}`"
|
||||||
|
color="white"
|
||||||
|
size="xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { mentionAsText } from '~/lib/utils.js'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
integration: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const formIntegrationsStore = useFormIntegrationsStore()
|
||||||
|
let interval = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!props.integration.data || props.integration.data.length === 0) {
|
||||||
|
interval = setInterval(() => formIntegrationsStore.fetchFormIntegrations(props.form.id), 3000)
|
||||||
|
setTimeout(() => { clearInterval(interval) }, 30000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (interval) {
|
||||||
|
clearInterval(interval)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const firstEmail = computed(() => {
|
||||||
|
const emails = mentionAsText(props.integration.data.send_to).split('\n').filter(Boolean)
|
||||||
|
return emails[0] || ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const additionalEmailsCount = computed(() => {
|
||||||
|
const emails = mentionAsText(props.integration.data.send_to).split('\n').filter(Boolean)
|
||||||
|
return Math.max(0, emails.length - 1)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -4,14 +4,12 @@
|
||||||
v-if="integration.provider"
|
v-if="integration.provider"
|
||||||
class="hidden md:block space-y-1"
|
class="hidden md:block space-y-1"
|
||||||
>
|
>
|
||||||
<div
|
<UBadge
|
||||||
class="font-medium mr-2"
|
:label="mentionAsText(integration.provider.email)"
|
||||||
>
|
color="gray"
|
||||||
{{ integration.provider.name }}
|
size="xs"
|
||||||
</div>
|
class="max-w-[300px] truncate"
|
||||||
<div class="text-sm">
|
/>
|
||||||
{{ integration.provider.email }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
@ -39,6 +37,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { mentionAsText } from '~/lib/utils.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
integration: {
|
integration: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
class="text-gray-500 border shadow rounded-md p-5 mt-4 relative flex items-center"
|
class="text-gray-500 border shadow rounded-md p-5 mt-4 relative flex items-center"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex items-center"
|
class="flex items-center w-full md:max-w-[240px]"
|
||||||
:class="{'flex-grow': !actionsComponent}"
|
:class="{'flex-grow': !actionsComponent}"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-1 items-center">
|
||||||
|
<div
|
||||||
|
v-if="integration"
|
||||||
|
class="hidden md:block space-y-1"
|
||||||
|
>
|
||||||
|
<UBadge
|
||||||
|
:label="mentionAsText(integration.data.message)"
|
||||||
|
color="gray"
|
||||||
|
size="xs"
|
||||||
|
class="max-w-[300px] block truncate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { mentionAsText } from '~/lib/utils.js'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
integration: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const formIntegrationsStore = useFormIntegrationsStore()
|
||||||
|
let interval = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!props.integration.data || props.integration.data.length === 0) {
|
||||||
|
interval = setInterval(() => formIntegrationsStore.fetchFormIntegrations(props.form.id), 3000)
|
||||||
|
setTimeout(() => { clearInterval(interval) }, 30000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (interval) {
|
||||||
|
clearInterval(interval)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-1 items-center">
|
||||||
|
<div
|
||||||
|
v-if="integration"
|
||||||
|
class="hidden md:block space-y-1"
|
||||||
|
>
|
||||||
|
<UBadge
|
||||||
|
:label="integration.data.webhook_url"
|
||||||
|
color="gray"
|
||||||
|
size="xs"
|
||||||
|
class="max-w-[300px] block truncate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
integration: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const formIntegrationsStore = useFormIntegrationsStore()
|
||||||
|
let interval = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!props.integration.data || props.integration.data.length === 0) {
|
||||||
|
interval = setInterval(() => formIntegrationsStore.fetchFormIntegrations(props.form.id), 3000)
|
||||||
|
setTimeout(() => { clearInterval(interval) }, 30000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (interval) {
|
||||||
|
clearInterval(interval)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
"icon": "heroicons:envelope-20-solid",
|
"icon": "heroicons:envelope-20-solid",
|
||||||
"section_name": "Notifications",
|
"section_name": "Notifications",
|
||||||
"file_name": "EmailIntegration",
|
"file_name": "EmailIntegration",
|
||||||
|
"actions_file_name": "EmailIntegrationActions",
|
||||||
"is_pro": false,
|
"is_pro": false,
|
||||||
"crisp_help_page_slug": "can-i-receive-notifications-on-form-submissions-134svqv"
|
"crisp_help_page_slug": "can-i-receive-notifications-on-form-submissions-134svqv"
|
||||||
},
|
},
|
||||||
|
|
@ -12,6 +13,7 @@
|
||||||
"icon": "mdi:slack",
|
"icon": "mdi:slack",
|
||||||
"section_name": "Notifications",
|
"section_name": "Notifications",
|
||||||
"file_name": "SlackIntegration",
|
"file_name": "SlackIntegration",
|
||||||
|
"actions_file_name": "SlackIntegrationActions",
|
||||||
"is_pro": true
|
"is_pro": true
|
||||||
},
|
},
|
||||||
"discord": {
|
"discord": {
|
||||||
|
|
@ -19,6 +21,7 @@
|
||||||
"icon": "ic:baseline-discord",
|
"icon": "ic:baseline-discord",
|
||||||
"section_name": "Notifications",
|
"section_name": "Notifications",
|
||||||
"file_name": "DiscordIntegration",
|
"file_name": "DiscordIntegration",
|
||||||
|
"actions_file_name": "DiscordIntegrationActions",
|
||||||
"is_pro": true
|
"is_pro": true
|
||||||
},
|
},
|
||||||
"webhook": {
|
"webhook": {
|
||||||
|
|
@ -26,6 +29,7 @@
|
||||||
"icon": "material-symbols:webhook",
|
"icon": "material-symbols:webhook",
|
||||||
"section_name": "Automation",
|
"section_name": "Automation",
|
||||||
"file_name": "WebhookIntegration",
|
"file_name": "WebhookIntegration",
|
||||||
|
"actions_file_name": "WebhookIntegrationActions",
|
||||||
"is_pro": false
|
"is_pro": false
|
||||||
},
|
},
|
||||||
"zapier": {
|
"zapier": {
|
||||||
|
|
|
||||||
|
|
@ -109,3 +109,15 @@ export const customDomainUsed = function () {
|
||||||
|
|
||||||
return host !== appDomain && getDomain(host) !== appDomain
|
return host !== appDomain && getDomain(host) !== appDomain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const mentionAsText = (content) => {
|
||||||
|
if (!content) return ''
|
||||||
|
|
||||||
|
// Parse the content and style mentions
|
||||||
|
return content.replace(
|
||||||
|
/<span\s+mention-field-id="([^"]+)"\s+mention-field-name="([^"]+)"[^>]*>([^<]+)<\/span>/g,
|
||||||
|
(match, fieldId, fieldName, text) => {
|
||||||
|
return `${text}`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue