Integration pages from Notion (#753)

* Integration pages from Notion

* fix integration page

* Refactor environment variables and update routing in integration pages

- Removed unused environment variables related to MUX from `.env.docker` and `.env.example` to streamline configuration.
- Updated routing links in `OpenFormFooter.vue` to correct navigation paths for "Integrations", "Report Abuse", and "Privacy Policy", enhancing user experience.
- Added middleware to `definePageMeta` in `index.vue` and `[slug].vue` for integration pages to enforce self-hosted logic.

These changes aim to improve code clarity and ensure proper routing functionality across the application.

---------

Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
Chirag Chhatrala
2025-05-19 18:38:15 +05:30
committed by GitHub
parent f9c734c826
commit c17f4776bc
12 changed files with 728 additions and 88 deletions

View File

@@ -50,6 +50,18 @@
Technical Docs
</a>
<template v-if="!useFeatureFlag('self_hosted')">
<router-link
:to="{ name: 'integrations' }"
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
>
Integrations
</router-link>
<router-link
:to="{ name: 'report-abuse' }"
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
>
Report Abuse
</router-link>
<router-link
:to="{ name: 'privacy-policy' }"
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
@@ -63,13 +75,6 @@
>
Terms & Conditions
</router-link>
<router-link
:to="{ name: 'report-abuse' }"
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
>
Report Abuse
</router-link>
</template>
</div>
</div>

View File

@@ -0,0 +1,68 @@
<template>
<div
v-if="innerJson"
id="custom-block"
>
<div
v-if="innerJson.type=='faq'"
class="rounded-lg bg-white z-10 pt-10"
>
<h2 class="font-medium">
Frequently Asked Questions
</h2>
<dl class="pt-4 space-y-6">
<div
v-for="question in innerJson.content"
:key="question.label"
class="space-y-2"
>
<dt class="font-semibold text-gray-900 dark:text-gray-100">
{{ question.label }}
</dt>
<dd
class="leading-6 text-gray-600 dark:text-gray-400"
v-html="question.content"
/>
</div>
</dl>
</div>
<div
v-else-if="innerJson.type=='cta'"
class="rounded-lg relative bg-gradient-to-r from-blue-400 to-blue-600 shadow-ld p-8 z-10"
>
<div class="absolute inset-px rounded-[calc(var(--radius)-1px)]">
<div class="flex justify-center w-full h-full">
<SpotlightCard
class="w-full p-2 rounded-[--radius] [--radius:theme(borderRadius.lg)] opacity-70"
from="#60a5fa"
:size="200"
/>
</div>
</div>
<div class="relative z-20 flex flex-col items-center gap-4 pb-1">
<h2 class="text-xl md:text-2xl text-center font-medium text-white">
{{ innerJson.title ? innerJson.title : 'Ready to upgrade your OpnForm forms?' }}
</h2>
<UButton
to="/register"
color="white"
class="hover:no-underline"
icon="i-heroicons-arrow-right"
trailing
>
Try OpnForm for free
</UButton>
</div>
</div>
</div>
</template>
<script setup>
import { blockProps } from 'vue-notion'
import useNotionBlock from '~/components/pages/notion/useNotionBlock.js'
const props = defineProps(blockProps)
const block = useNotionBlock(props)
const innerJson = computed(() => block.innerJson.value)
</script>

View File

@@ -0,0 +1,107 @@
import { computed } from 'vue'
export default function useNotionBlock (props) {
const block = computed(() => {
const id = props.contentId || Object.keys(props.blockMap)[0]
return props.blockMap[id]
})
const value = computed(() => {
return block.value?.value
})
const format = computed(() => {
return value.value?.format
})
const icon = computed(() => {
return format.value?.page_icon || ''
})
const width = computed(() => {
return format.value?.block_width
})
const properties = computed(() => {
return value.value?.properties
})
const caption = computed(() => {
return properties.value?.caption
})
const description = computed(() => {
return properties.value?.description
})
const src = computed(() => {
return mapImageUrl(properties.value?.source[0][0], block.value)
})
const title = computed(() => {
return properties.value?.title
})
const alt = computed(() => {
return caption.value?.[0][0]
})
const type = computed(() => {
return value.value?.type
})
const visible = computed(() => {
return !props.hideList.includes(type.value)
})
const hasPageLinkOptions = computed(() => {
return props.pageLinkOptions?.component && props.pageLinkOptions?.href
})
const parent = computed(() => {
return props.blockMap[value.value?.parent_id]
})
const innerJson = computed(() => {
if (type.value !== 'code') return
if (properties.value.language.flat('Infinity').join('') !== 'JSON') {
return
}
try {
return JSON.parse(
title.value.flat(Infinity).join('').replace(/\n/g, '').replace(/\t/g, '').trim()
)
} catch (error) {
console.error('Failed to parse JSON',
error,
title.value.flat(Infinity).join('').replace(/\n/g, '').replace(/\t/g, '').trim()
)
return
}
})
function mapImageUrl (source) {
// Implement your mapImageUrl logic here
return source
}
return {
icon,
width,
properties,
caption,
description,
src,
title,
alt,
block,
value,
format,
type,
visible,
hasPageLinkOptions,
parent,
innerJson
}
}