opnform-host-nginx/client/pages/forms/[slug]/index.vue

282 lines
7.6 KiB
Vue
Raw Normal View History

<template>
<div
id="public-form"
class="flex flex-col"
>
<div v-if="form && !isIframe && (form.logo_picture || form.cover_picture)">
<div v-if="form.cover_picture">
<div
id="cover-picture"
class="max-h-56 w-full overflow-hidden flex items-center justify-center"
>
<img
alt="Form Cover Picture"
:src="form.cover_picture"
class="w-full"
>
</div>
</div>
<div
v-if="form.logo_picture"
class="w-full p-5 relative mx-auto"
:class="{'pt-20':!form.cover_picture, 'md:w-3/5 lg:w-1/2 md:max-w-2xl': form.width === 'centered', 'max-w-7xl': (form.width === 'full' && !isIframe) }"
:style="{ 'direction': form?.layout_rtl ? 'rtl' : 'ltr' }"
>
<img
alt="Logo Picture"
:src="form.logo_picture"
:class="{'top-5':!form.cover_picture, '-top-10':form.cover_picture}"
class="w-20 h-20 object-contain absolute transition-all"
>
</div>
</div>
<div
class="w-full mx-auto px-4"
:class="{'mt-6':!isIframe, 'md:w-3/5 lg:w-1/2 md:max-w-2xl': form && (form.width === 'centered'), 'max-w-7xl': (form && form.width === 'full' && !isIframe)}"
>
<div v-if="!formLoading && !form">
Refactor Form Not Found Handling and Improve Error Response (#730) * Refactor Form Not Found Handling and Improve Error Response - Replace the existing "Whoops" message with a NotFoundForm component for better user experience when a form is not found. - Enhance error handling in the loadForm function to set a 404 response status when a form cannot be loaded, improving clarity for users and developers. These changes aim to streamline the user experience and provide clearer feedback when forms are unavailable. * Refactor Navbar and NotFoundForm Components for Improved User Experience - Simplify the Navbar component by removing unnecessary conditional rendering for the authentication display, enhancing clarity in the layout. - Update the NotFoundForm component to utilize a dynamic actions array for rendering navigation options, improving maintainability and flexibility. - Adjust text styles for better readability and consistency across the NotFoundForm component. These changes aim to streamline the user interface and enhance the overall user experience when navigating forms and handling not found scenarios. * Update NotFoundForm Component for Improved Action Clarity - Adjust the text and icon in the actions array of the NotFoundForm component to enhance clarity and user experience. The "Start Fresh" action has been renamed to "Create Form" and the icon has been updated from a check-circle to a rocket-launch icon, better reflecting the action's purpose. - Minor formatting adjustments were made to the template for improved readability. These changes aim to provide users with clearer navigation options and a more intuitive interface when encountering a not found scenario. --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2025-03-20 16:55:53 +01:00
<NotFoundForm />
</div>
<div v-else-if="formLoading">
<p class="text-center mt-6 p-4">
<loader class="h-6 w-6 text-nt-blue mx-auto" />
</p>
</div>
<template v-else>
<div v-if="recordLoading">
<p class="text-center mt-6 p-4">
<loader class="h-6 w-6 text-nt-blue mx-auto" />
</p>
</div>
<OpenCompleteForm
v-show="!recordLoading"
ref="openCompleteForm"
:form="form"
class="mb-10"
:dark-mode="darkMode"
Decouple title from title block (#696) * Decouple title from title block * fix lint * remove dry run for FormTitleMigration * Skip form title migration for forms with hidden titles * Refactor AI Form Generation with Dedicated Prompt Services - Extract AI form generation logic from GenerateTemplate command into dedicated prompt service classes - Update GenerateAiForm job to use new prompt generation services - Improve GptCompleter with more robust error handling and token tracking - Add error field to AiFormCompletion model for better error logging - Simplify command signature from 'ai:make-form-template' to 'form:generate' * Consolidate Template Metadata Generation with Unified Prompt Service - Create GenerateTemplateMetadataPrompt to centralize template metadata generation - Update GenerateTemplate command to use new consolidated metadata generation approach - Enhance GptCompleter to support strict JSON schema validation - Increase form prompt max length to support more complex form descriptions - Refactor form generation to simplify metadata extraction and processing * Implement Form Mode Strategy for Flexible Form Rendering - Introduce FormModeStrategy to centralize form rendering logic - Add support for different form modes: LIVE, PREVIEW, TEST, EDIT, PREFILL - Refactor components to use mode-based rendering strategy - Remove legacy boolean props like adminPreview and creating - Enhance form component flexibility and reusability * Refine Form Mode Strategy Display Behavior - Update FormModeStrategy to hide hidden fields in PREVIEW mode - Add FormMode getter in UrlFormPrefill component for mode-specific rendering - Clarify mode-specific validation and display logic in form strategies * Enhance Form Template Generation with Advanced Field Options - Update GenerateTemplate command to use front_url for template URL output - Expand GenerateFormPrompt with comprehensive field configuration options - Add support for advanced field types: date with time, toggle switches, radio/checkbox selections - Introduce field width configuration and HTML formatting for text elements - Re-enable select, multi-select, and matrix field type definitions with enhanced configurations * Remove Deprecated Template Metadata Generation Services - Delete multiple AI prompt services related to template metadata generation - Simplify GenerateTemplate command to use default values instead of complex metadata generation - Remove GenerateTemplateMetadataPrompt and related classes like GenerateTemplateDescriptionPrompt, GenerateTemplateImageKeywordsPrompt, etc. - Update form template generation to use basic fallback metadata generation approach * Restore GenerateTemplateMetadataPrompt for Comprehensive Template Generation - Reintroduce GenerateTemplateMetadataPrompt to replace default metadata generation - Update GenerateTemplate command to use consolidated metadata generation approach - Extract detailed metadata components including title, description, industries, and image search query - Improve template generation with more dynamic and AI-generated metadata * Refactor Template Preview Section Layout - Remove unnecessary nested div in template preview section - Simplify HTML structure for the template preview component - Maintain existing styling and functionality while improving code readability * Refactor Constructor and Code Formatting in AI Form Generation and Prompt Classes - Updated the constructor in GenerateAiForm.php to use a block structure for improved readability and consistency. - Added a blank line in the Prompt.php file to enhance code formatting and maintain consistency with PHP coding standards. - Modified the migration file to use a more concise class declaration syntax, improving clarity. These changes aim to enhance code readability and maintainability across the affected files. --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2025-03-21 16:59:18 +01:00
:mode="FormMode.LIVE"
@password-entered="passwordEntered"
/>
</template>
</div>
</div>
</template>
<script setup>
2024-03-20 19:24:53 +01:00
import OpenCompleteForm from "~/components/open/forms/OpenCompleteForm.vue"
import sha256 from 'js-sha256'
import { onBeforeRouteLeave } from 'vue-router'
import {
disableDarkMode,
handleDarkMode,
handleTransparentMode,
focusOnFirstFormElement,
useDarkMode
} from '~/lib/forms/public-page'
Decouple title from title block (#696) * Decouple title from title block * fix lint * remove dry run for FormTitleMigration * Skip form title migration for forms with hidden titles * Refactor AI Form Generation with Dedicated Prompt Services - Extract AI form generation logic from GenerateTemplate command into dedicated prompt service classes - Update GenerateAiForm job to use new prompt generation services - Improve GptCompleter with more robust error handling and token tracking - Add error field to AiFormCompletion model for better error logging - Simplify command signature from 'ai:make-form-template' to 'form:generate' * Consolidate Template Metadata Generation with Unified Prompt Service - Create GenerateTemplateMetadataPrompt to centralize template metadata generation - Update GenerateTemplate command to use new consolidated metadata generation approach - Enhance GptCompleter to support strict JSON schema validation - Increase form prompt max length to support more complex form descriptions - Refactor form generation to simplify metadata extraction and processing * Implement Form Mode Strategy for Flexible Form Rendering - Introduce FormModeStrategy to centralize form rendering logic - Add support for different form modes: LIVE, PREVIEW, TEST, EDIT, PREFILL - Refactor components to use mode-based rendering strategy - Remove legacy boolean props like adminPreview and creating - Enhance form component flexibility and reusability * Refine Form Mode Strategy Display Behavior - Update FormModeStrategy to hide hidden fields in PREVIEW mode - Add FormMode getter in UrlFormPrefill component for mode-specific rendering - Clarify mode-specific validation and display logic in form strategies * Enhance Form Template Generation with Advanced Field Options - Update GenerateTemplate command to use front_url for template URL output - Expand GenerateFormPrompt with comprehensive field configuration options - Add support for advanced field types: date with time, toggle switches, radio/checkbox selections - Introduce field width configuration and HTML formatting for text elements - Re-enable select, multi-select, and matrix field type definitions with enhanced configurations * Remove Deprecated Template Metadata Generation Services - Delete multiple AI prompt services related to template metadata generation - Simplify GenerateTemplate command to use default values instead of complex metadata generation - Remove GenerateTemplateMetadataPrompt and related classes like GenerateTemplateDescriptionPrompt, GenerateTemplateImageKeywordsPrompt, etc. - Update form template generation to use basic fallback metadata generation approach * Restore GenerateTemplateMetadataPrompt for Comprehensive Template Generation - Reintroduce GenerateTemplateMetadataPrompt to replace default metadata generation - Update GenerateTemplate command to use consolidated metadata generation approach - Extract detailed metadata components including title, description, industries, and image search query - Improve template generation with more dynamic and AI-generated metadata * Refactor Template Preview Section Layout - Remove unnecessary nested div in template preview section - Simplify HTML structure for the template preview component - Maintain existing styling and functionality while improving code readability * Refactor Constructor and Code Formatting in AI Form Generation and Prompt Classes - Updated the constructor in GenerateAiForm.php to use a block structure for improved readability and consistency. - Added a blank line in the Prompt.php file to enhance code formatting and maintain consistency with PHP coding standards. - Modified the migration file to use a more concise class declaration syntax, improving clarity. These changes aim to enhance code readability and maintainability across the affected files. --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2025-03-21 16:59:18 +01:00
import { FormMode } from "~/lib/forms/FormModeStrategy.js"
const crisp = useCrisp()
const formsStore = useFormsStore()
const recordsStore = useRecordsStore()
const darkMode = useDarkMode()
const isIframe = useIsIframe()
const formLoading = computed(() => formsStore.loading)
const recordLoading = computed(() => recordsStore.loading)
const slug = useRoute().params.slug
const form = computed(() => formsStore.getByKey(slug))
const $t = useI18n()
2024-01-16 13:27:54 +01:00
const openCompleteForm = ref(null)
const passwordEntered = function (password) {
2024-01-16 18:27:58 +01:00
const cookie = useCookie('password-' + slug, {
2024-01-16 13:27:54 +01:00
maxAge: 60 * 60 * 7,
sameSite: 'none',
secure: true
2024-01-16 18:27:58 +01:00
})
cookie.value = sha256(password)
nextTick(() => {
loadForm().then(() => {
if (form.value?.is_password_protected) {
openCompleteForm.value.addPasswordError($t('forms.invalid_password'))
2024-01-16 18:27:58 +01:00
}
})
})
}
2024-01-16 13:27:54 +01:00
const loadForm = async (setup=false) => {
if (formsStore.loading || (form.value && !form.value.is_password_protected)) return Promise.resolve()
Refactor Form Not Found Handling and Improve Error Response (#730) * Refactor Form Not Found Handling and Improve Error Response - Replace the existing "Whoops" message with a NotFoundForm component for better user experience when a form is not found. - Enhance error handling in the loadForm function to set a 404 response status when a form cannot be loaded, improving clarity for users and developers. These changes aim to streamline the user experience and provide clearer feedback when forms are unavailable. * Refactor Navbar and NotFoundForm Components for Improved User Experience - Simplify the Navbar component by removing unnecessary conditional rendering for the authentication display, enhancing clarity in the layout. - Update the NotFoundForm component to utilize a dynamic actions array for rendering navigation options, improving maintainability and flexibility. - Adjust text styles for better readability and consistency across the NotFoundForm component. These changes aim to streamline the user interface and enhance the overall user experience when navigating forms and handling not found scenarios. * Update NotFoundForm Component for Improved Action Clarity - Adjust the text and icon in the actions array of the NotFoundForm component to enhance clarity and user experience. The "Start Fresh" action has been renamed to "Create Form" and the icon has been updated from a check-circle to a rocket-launch icon, better reflecting the action's purpose. - Minor formatting adjustments were made to the template for improved readability. These changes aim to provide users with clearer navigation options and a more intuitive interface when encountering a not found scenario. --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2025-03-20 16:55:53 +01:00
const event = useRequestEvent()
2024-01-16 13:27:54 +01:00
if (setup) {
const {data, error} = await formsStore.publicLoad(slug)
if (error.value) {
console.error(`Error loading form [${slug}]:`,error.value)
2024-01-16 13:27:54 +01:00
formsStore.stopLoading()
Refactor Form Not Found Handling and Improve Error Response (#730) * Refactor Form Not Found Handling and Improve Error Response - Replace the existing "Whoops" message with a NotFoundForm component for better user experience when a form is not found. - Enhance error handling in the loadForm function to set a 404 response status when a form cannot be loaded, improving clarity for users and developers. These changes aim to streamline the user experience and provide clearer feedback when forms are unavailable. * Refactor Navbar and NotFoundForm Components for Improved User Experience - Simplify the Navbar component by removing unnecessary conditional rendering for the authentication display, enhancing clarity in the layout. - Update the NotFoundForm component to utilize a dynamic actions array for rendering navigation options, improving maintainability and flexibility. - Adjust text styles for better readability and consistency across the NotFoundForm component. These changes aim to streamline the user interface and enhance the overall user experience when navigating forms and handling not found scenarios. * Update NotFoundForm Component for Improved Action Clarity - Adjust the text and icon in the actions array of the NotFoundForm component to enhance clarity and user experience. The "Start Fresh" action has been renamed to "Create Form" and the icon has been updated from a check-circle to a rocket-launch icon, better reflecting the action's purpose. - Minor formatting adjustments were made to the template for improved readability. These changes aim to provide users with clearer navigation options and a more intuitive interface when encountering a not found scenario. --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2025-03-20 16:55:53 +01:00
setResponseStatus(event, 404, 'Page Not Found')
2024-01-16 13:27:54 +01:00
return
}
formsStore.save(data.value)
} else {
2024-01-16 15:36:14 +01:00
try {
const data = await formsStore.publicFetch(slug)
formsStore.save(data)
} catch (e) {
formsStore.stopLoading()
Refactor Form Not Found Handling and Improve Error Response (#730) * Refactor Form Not Found Handling and Improve Error Response - Replace the existing "Whoops" message with a NotFoundForm component for better user experience when a form is not found. - Enhance error handling in the loadForm function to set a 404 response status when a form cannot be loaded, improving clarity for users and developers. These changes aim to streamline the user experience and provide clearer feedback when forms are unavailable. * Refactor Navbar and NotFoundForm Components for Improved User Experience - Simplify the Navbar component by removing unnecessary conditional rendering for the authentication display, enhancing clarity in the layout. - Update the NotFoundForm component to utilize a dynamic actions array for rendering navigation options, improving maintainability and flexibility. - Adjust text styles for better readability and consistency across the NotFoundForm component. These changes aim to streamline the user interface and enhance the overall user experience when navigating forms and handling not found scenarios. * Update NotFoundForm Component for Improved Action Clarity - Adjust the text and icon in the actions array of the NotFoundForm component to enhance clarity and user experience. The "Start Fresh" action has been renamed to "Create Form" and the icon has been updated from a check-circle to a rocket-launch icon, better reflecting the action's purpose. - Minor formatting adjustments were made to the template for improved readability. These changes aim to provide users with clearer navigation options and a more intuitive interface when encountering a not found scenario. --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2025-03-20 16:55:53 +01:00
setResponseStatus(event, 404, 'Page Not Found')
2024-01-16 15:36:14 +01:00
return
}
}
formsStore.stopLoading()
// Adapt page to form: colors, custom code etc
handleDarkMode(form.value?.dark_mode)
handleTransparentMode(form.value?.transparent_background)
// Remove 'hidden' class from html tag if present
nextTick(() => {
if (import.meta.client) {
window.document.documentElement.classList.remove('hidden')
}
})
}
2024-01-16 18:46:05 +01:00
await loadForm(true)
// Start loader if record needs to be loaded
if (useRoute().query?.submission_id) {
recordsStore.startLoading()
}
onMounted(() => {
2024-01-16 18:46:05 +01:00
crisp.hideChat()
2024-03-12 16:13:00 +01:00
document.body.classList.add('public-page')
2024-01-16 15:32:52 +01:00
if (form.value) {
handleDarkMode(form.value?.dark_mode)
handleTransparentMode(form.value?.transparent_background)
// Remove 'hidden' class from html tag if present
nextTick(() => {
if (import.meta.client) {
window.document.documentElement.classList.remove('hidden')
}
})
if (import.meta.client) {
if (form.value.custom_code) {
const scriptEl = document.createRange().createContextualFragment(form.value.custom_code)
try {
document.head.append(scriptEl)
} catch (e) {
console.error('Error appending custom code', e)
}
}
if (!isIframe && form.value?.auto_focus) focusOnFirstFormElement()
}
2024-01-16 15:32:52 +01:00
}
})
onBeforeRouteLeave(() => {
2024-03-12 16:13:00 +01:00
document.body.classList.remove('public-page')
2024-01-16 18:46:05 +01:00
crisp.showChat()
disableDarkMode()
})
const pageMeta = computed(() => {
if (form.value && form.value.is_pro && form.value.seo_meta) {
return form.value.seo_meta
}
return {}
})
const getFontUrl = computed(() => {
if(!form.value || !form.value.font_family) return null
const family = form.value.font_family.replace(/ /g, '+')
return `https://fonts.googleapis.com/css?family=${family}:wght@400,500,700,800,900&display=swap`
})
const headLinks = computed(() => {
const links = []
if (form.value && form.value.font_family) {
links.push({
rel: 'stylesheet',
href: getFontUrl.value
})
}
if (pageMeta.value.page_favicon) {
links.push({
rel: 'icon', type: 'image/x-icon',
href: pageMeta.value.page_favicon
})
links.push({
rel: 'apple-touch-icon',
type: 'image/png',
href: pageMeta.value.page_favicon
})
links.push({
rel: 'shortcut icon',
href: pageMeta.value.page_favicon
})
}
return links
})
useOpnSeoMeta({
title: () => {
if (pageMeta.value.page_title) {
return pageMeta.value.page_title
}
return form.value ? form.value.title : 'Create beautiful forms'
},
2024-01-16 11:46:03 +01:00
description: () => {
if (pageMeta.value.page_description) {
return pageMeta.value.page_description
}
return null
},
2024-01-16 11:46:03 +01:00
ogImage: () => {
if (pageMeta.value.page_thumbnail) {
return pageMeta.value.page_thumbnail
}
return (form.value && form.value?.cover_picture) ? form.value?.cover_picture : null
},
2024-01-12 15:59:01 +01:00
robots: () => {
return (form.value && form.value?.can_be_indexed) ? null : 'noindex, nofollow'
}
})
const getHtmlClass = computed(() => {
return {
dark: form.value?.dark_mode === 'dark',
hidden: form.value?.dark_mode === 'auto' && import.meta.server,
}
})
useHead({
htmlAttrs: {
dir: () => form.value?.layout_rtl ? 'rtl' : 'ltr',
class: getHtmlClass.value,
lang: () => form.value?.language || 'en'
},
titleTemplate: (titleChunk) => {
if (pageMeta.value.page_title) {
// Disable template if custom SEO title
return titleChunk
}
return titleChunk ? `${titleChunk} - OpnForm` : 'OpnForm'
},
link: headLinks.value,
meta: pageMeta.value.page_favicon ? [
{
name: 'apple-mobile-web-app-capable',
content: 'yes'
},
{
name: 'apple-mobile-web-app-status-bar-style',
content: 'black-translucent'
},
] : {},
script: [{ src: '/widgets/iframeResizer.contentWindow.min.js' }]
})
</script>