diff --git a/api/.env.docker b/api/.env.docker index 9c4e66d5..db728ef6 100644 --- a/api/.env.docker +++ b/api/.env.docker @@ -34,18 +34,7 @@ AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= -PUSHER_APP_ID= -PUSHER_APP_KEY= -PUSHER_APP_SECRET= -PUSHER_APP_CLUSTER=mt1 - -MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" -MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" - JWT_TTL=1440 JWT_SECRET= -MUX_WORKSPACE_ID= -MUX_API_TOKEN= - OPEN_AI_API_KEY= \ No newline at end of file diff --git a/api/.env.example b/api/.env.example index 41365481..61448dee 100644 --- a/api/.env.example +++ b/api/.env.example @@ -76,9 +76,6 @@ H_CAPTCHA_SECRET_KEY= RE_CAPTCHA_SITE_KEY= RE_CAPTCHA_SECRET_KEY= -MUX_WORKSPACE_ID= -MUX_API_TOKEN= - ADMIN_EMAILS= TEMPLATE_EDITOR_EMAILS= diff --git a/client/components/global/NotionPage.vue b/client/components/global/NotionPage.vue index 5263fc9f..b2ebf8af 100644 --- a/client/components/global/NotionPage.vue +++ b/client/components/global/NotionPage.vue @@ -1,7 +1,18 @@ - - diff --git a/client/components/pages/OpenFormFooter.vue b/client/components/pages/OpenFormFooter.vue index 6f47a834..fa61bf8f 100644 --- a/client/components/pages/OpenFormFooter.vue +++ b/client/components/pages/OpenFormFooter.vue @@ -50,6 +50,18 @@ Technical Docs + + Integrations + + + Report Abuse + Terms & Conditions - - - Report Abuse - diff --git a/client/components/pages/notion/CustomBlock.vue b/client/components/pages/notion/CustomBlock.vue new file mode 100644 index 00000000..c421616b --- /dev/null +++ b/client/components/pages/notion/CustomBlock.vue @@ -0,0 +1,68 @@ + + + + + Frequently Asked Questions + + + + + {{ question.label }} + + + + + + + + + + + + + + {{ innerJson.title ? innerJson.title : 'Ready to upgrade your OpnForm forms?' }} + + + Try OpnForm for free + + + + + + + diff --git a/client/components/pages/notion/useNotionBlock.js b/client/components/pages/notion/useNotionBlock.js new file mode 100644 index 00000000..763dbe04 --- /dev/null +++ b/client/components/pages/notion/useNotionBlock.js @@ -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 + } +} diff --git a/client/pages/integrations/[slug].vue b/client/pages/integrations/[slug].vue new file mode 100644 index 00000000..c0b58550 --- /dev/null +++ b/client/pages/integrations/[slug].vue @@ -0,0 +1,95 @@ + + + + + + + Other Integrations + + + + {{ page.Title }} + + + + + Discover our other Integrations + + + + + + + Whoops - Page not found + + + + + + + + diff --git a/client/pages/integrations/index.vue b/client/pages/integrations/index.vue new file mode 100644 index 00000000..b5649e4a --- /dev/null +++ b/client/pages/integrations/index.vue @@ -0,0 +1,239 @@ + + + + + + + + + + Available Integrations + + + Explore our powerful Integrations + + + + + + + Most Popular + + + + + + + Setup Guide + + + + + + {{ integration.title }} + + + {{ integration.description }} + + + + + ✔ {{ step }} + + + + + + + + + + + Integration General Setup Guides + + + This can be another text + + + + + + {{ guide.title }} + + + + {{ index + 1 }} + + + + + + + + + + + Need help? + + + Visit our Help Center for detailed documentation! + + + Help Center + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/pages/privacy-policy.vue b/client/pages/privacy-policy.vue index 6a2e54d6..26d90046 100644 --- a/client/pages/privacy-policy.vue +++ b/client/pages/privacy-policy.vue @@ -6,7 +6,7 @@ Privacy Policy @@ -16,26 +16,19 @@ diff --git a/client/pages/terms-conditions.vue b/client/pages/terms-conditions.vue index acdbe201..e76a4683 100644 --- a/client/pages/terms-conditions.vue +++ b/client/pages/terms-conditions.vue @@ -6,7 +6,7 @@ Terms & Conditions @@ -16,26 +16,19 @@ diff --git a/client/stores/notion_cms.js b/client/stores/notion_cms.js new file mode 100644 index 00000000..6d0b9538 --- /dev/null +++ b/client/stores/notion_cms.js @@ -0,0 +1,127 @@ +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' +import opnformConfig from "~/opnform.config.js" + +function notionApiFetch (entityId, type = 'table') { + const apiUrl = opnformConfig.notion.worker + return useFetch(`${apiUrl}/${type}/${entityId}`) +} + +function fetchNotionDatabasePages (databaseId) { + return notionApiFetch(databaseId) +} + +function fetchNotionPageContent (pageId) { + return notionApiFetch(pageId, 'page') +} + +export const useNotionCmsStore = defineStore('notion_cms', () => { + + // State + const databases = ref({}) + const pages = ref({}) + const pageContents = ref({}) + const slugToIdMap = ref({}) + + const loading = ref(false) + + // Actions + const loadDatabase = (databaseId) => { + return new Promise((resolve, reject) => { + if (databases.value[databaseId]) return resolve() + + loading.value = true + return fetchNotionDatabasePages(databaseId).then((response) => { + databases.value[databaseId] = response.data.value.map(page => formatId(page.id)) + response.data.value.forEach(page => { + pages.value[formatId(page.id)] = { + ...page, + id: formatId(page.id) + } + const slug = page.Slug ?? page.slug ?? null + if (slug) { + setSlugToIdMap(slug, page.id) + } + }) + loading.value = false + resolve() + }).catch((error) => { + loading.value = false + console.error(error) + reject(error) + }) + }) + } + const loadPage = async (pageId) => { + return new Promise((resolve, reject) => { + if (pageContents.value[pageId]) return resolve('in already here') + loading.value = true + return fetchNotionPageContent(pageId).then((response) => { + pageContents.value[formatId(pageId)] = response.data.value + loading.value = false + return resolve('in finishg') + }).catch((error) => { + console.error(error) + loading.value = false + return reject(error) + }) + }) + } + + const loadPageBySlug = (slug) => { + if (!slugToIdMap.value[slug.toLowerCase()]) return + loadPage(slugToIdMap.value[slug.toLowerCase()]) + } + + const formatId = (id) => id.replaceAll('-', '') + + const getPage = (pageId) => { + return { + ...pages.value[pageId], + blocks: getPageBody(pageId) + } + } + + const getPageBody = (pageId) => { + return pageContents.value[pageId] + } + + const setSlugToIdMap = (slug, pageId) => { + if (!slug) return + slugToIdMap.value[slug.toLowerCase()] = formatId(pageId) + } + + const getPageBySlug = (slug) => { + if (!slug) return + const pageId = slugToIdMap.value[slug.toLowerCase()] + return getPage(pageId) + } + +// Getters + const databasePages = (databaseId) => computed(() => databases.value[databaseId]?.map(id => pages.value[id]) || []) + const pageContent = (pageId) => computed(() => pageContents.value[pageId]) + const pageBySlug = (slug) => computed(() => getPageBySlug(slug)) + + return { + // state + loading, + databases, + pages, + pageContents, + slugToIdMap, + + // actions + loadDatabase, + loadPage, + loadPageBySlug, + getPage, + getPageBody, + setSlugToIdMap, + getPageBySlug, + + // getters + databasePages, + pageContent, + pageBySlug + } +}) diff --git a/client/stores/notion_pages.js b/client/stores/notion_pages.js deleted file mode 100644 index 608f78c9..00000000 --- a/client/stores/notion_pages.js +++ /dev/null @@ -1,26 +0,0 @@ -import { defineStore } from "pinia" -import { useContentStore } from "~/composables/stores/useContentStore.js" -import opnformConfig from "~/opnform.config.js" -export const useNotionPagesStore = defineStore("notion_pages", () => { - const contentStore = useContentStore() - - const load = (pageId) => { - contentStore.startLoading() - - const apiUrl = opnformConfig.notion.worker - return useFetch(`${apiUrl}/page/${pageId}`) - .then(({ data }) => { - const val = data.value - val["id"] = pageId - contentStore.save(val) - }) - .finally(() => { - contentStore.stopLoading() - }) - } - - return { - ...contentStore, - load, - } -})
+ + Other Integrations + +
+ + Discover our other Integrations + +
+ Explore our powerful Integrations +
+ {{ integration.description }} +
+ This can be another text +
+ Visit our Help Center for detailed documentation! +