apply first submission modal changes (#584)
* apply first submission modal changes * Apply changes * fix submiussions url * fix lint * Fix nuxt versions issues * Add fixed version of nitropack * Attempt to fix build --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
469031c9ec
commit
ef404e1d1e
|
|
@ -87,6 +87,7 @@ class PublicFormController extends Controller
|
||||||
public function answer(AnswerFormRequest $request)
|
public function answer(AnswerFormRequest $request)
|
||||||
{
|
{
|
||||||
$form = $request->form;
|
$form = $request->form;
|
||||||
|
$isFirstSubmission = ($form->submissions_count === 0);
|
||||||
$submissionId = false;
|
$submissionId = false;
|
||||||
|
|
||||||
$submissionData = $request->validated();
|
$submissionData = $request->validated();
|
||||||
|
|
@ -104,6 +105,7 @@ class PublicFormController extends Controller
|
||||||
return $this->success(array_merge([
|
return $this->success(array_merge([
|
||||||
'message' => 'Form submission saved.',
|
'message' => 'Form submission saved.',
|
||||||
'submission_id' => $submissionId,
|
'submission_id' => $submissionId,
|
||||||
|
'is_first_submission' => $isFirstSubmission
|
||||||
], $request->form->is_pro && $request->form->redirect_url ? [
|
], $request->form->is_pro && $request->form->redirect_url ? [
|
||||||
'redirect' => true,
|
'redirect' => true,
|
||||||
'redirect_url' => $request->form->redirect_url,
|
'redirect_url' => $request->form->redirect_url,
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ class FormResource extends JsonResource
|
||||||
'views_count' => $this->views_count,
|
'views_count' => $this->views_count,
|
||||||
'submissions_count' => $this->submissions_count,
|
'submissions_count' => $this->submissions_count,
|
||||||
'redirect_url' => $this->redirect_url,
|
'redirect_url' => $this->redirect_url,
|
||||||
|
'submissions_url' => $this->submissions_url,
|
||||||
'database_fields_update' => $this->database_fields_update,
|
'database_fields_update' => $this->database_fields_update,
|
||||||
'cleanings' => $this->getCleanigns(),
|
'cleanings' => $this->getCleanigns(),
|
||||||
'can_be_indexed' => $this->can_be_indexed,
|
'can_be_indexed' => $this->can_be_indexed,
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,15 @@ class Form extends Model implements CachableAttributes
|
||||||
return front_url('/forms/' . $this->slug);
|
return front_url('/forms/' . $this->slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSubmissionsUrlAttribute()
|
||||||
|
{
|
||||||
|
if ($this->custom_domain) {
|
||||||
|
return 'https://' . $this->custom_domain . '/forms/' . $this->slug . '/show/submissions';
|
||||||
|
}
|
||||||
|
|
||||||
|
return front_url('/forms/' . $this->slug . '/show/submissions');
|
||||||
|
}
|
||||||
|
|
||||||
public function getEditUrlAttribute()
|
public function getEditUrlAttribute()
|
||||||
{
|
{
|
||||||
return front_url('/forms/' . $this->slug . '/show');
|
return front_url('/forms/' . $this->slug . '/show');
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,11 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</v-transition>
|
</v-transition>
|
||||||
|
<FirstSubmissionModal
|
||||||
|
:show="showFirstSubmissionModal"
|
||||||
|
:form="form"
|
||||||
|
@close="showFirstSubmissionModal=false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -191,9 +196,10 @@ import VTransition from '~/components/global/transitions/VTransition.vue'
|
||||||
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js"
|
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js"
|
||||||
import clonedeep from "clone-deep"
|
import clonedeep from "clone-deep"
|
||||||
import ThemeBuilder from "~/lib/forms/themes/ThemeBuilder.js"
|
import ThemeBuilder from "~/lib/forms/themes/ThemeBuilder.js"
|
||||||
|
import FirstSubmissionModal from '~/components/open/forms/components/FirstSubmissionModal.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { VTransition, OpenFormButton, OpenForm, FormCleanings, FormTimer },
|
components: { VTransition, OpenFormButton, OpenForm, FormCleanings, FormTimer, FirstSubmissionModal },
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
form: { type: Object, required: true },
|
form: { type: Object, required: true },
|
||||||
|
|
@ -207,7 +213,10 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
const authStore = useAuthStore()
|
||||||
return {
|
return {
|
||||||
|
authStore,
|
||||||
|
authenticated: computed(() => authStore.check),
|
||||||
isIframe: useIsIframe(),
|
isIframe: useIsIframe(),
|
||||||
pendingSubmission: pendingSubmission(props.form),
|
pendingSubmission: pendingSubmission(props.form),
|
||||||
confetti: useConfetti()
|
confetti: useConfetti()
|
||||||
|
|
@ -222,7 +231,8 @@ export default {
|
||||||
password: null
|
password: null
|
||||||
}),
|
}),
|
||||||
hidePasswordDisabledMsg: false,
|
hidePasswordDisabledMsg: false,
|
||||||
submissionId: false
|
submissionId: false,
|
||||||
|
showFirstSubmissionModal: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -246,6 +256,9 @@ export default {
|
||||||
if(!this.form || !this.form.font_family) return null
|
if(!this.form || !this.form.font_family) return null
|
||||||
const family = this.form?.font_family.replace(/ /g, '+')
|
const family = this.form?.font_family.replace(/ /g, '+')
|
||||||
return `https://fonts.googleapis.com/css?family=${family}:wght@400,500,700,800,900&display=swap`
|
return `https://fonts.googleapis.com/css?family=${family}:wght@400,500,700,800,900&display=swap`
|
||||||
|
},
|
||||||
|
isFormOwner() {
|
||||||
|
return this.authenticated && this.form && this.form.creator_id === this.authStore.user.id
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -291,7 +304,9 @@ export default {
|
||||||
if (data.submission_id) {
|
if (data.submission_id) {
|
||||||
this.submissionId = data.submission_id
|
this.submissionId = data.submission_id
|
||||||
}
|
}
|
||||||
|
if (this.isFormOwner && !this.isIframe && data?.is_first_submission) {
|
||||||
|
this.showFirstSubmissionModal = true
|
||||||
|
}
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.submitted = true
|
this.submitted = true
|
||||||
this.$emit('submitted', true)
|
this.$emit('submitted', true)
|
||||||
|
|
|
||||||
|
|
@ -12,22 +12,29 @@
|
||||||
</template>
|
</template>
|
||||||
<div class="">
|
<div class="">
|
||||||
<div class="text-sm text-gray-500">
|
<div class="text-sm text-gray-500">
|
||||||
Congratulations on creating your form and receiving your first submission! Your form is now live and ready for action. You can now <span class="font-semibold">share your form</span> with others, or <span class="font-semibold">open your Notion database</span> to view the submitted data.
|
Congratulations on creating your form and receiving your first submission! Your form is now live and ready for action. You can now <span class="font-semibold">share your form</span> with others, or <span class="font-semibold">open your Form submission page</span> to view the submitted data.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2 items-center max-w-full">
|
<div class="flex gap-2 items-center max-w-full">
|
||||||
|
<p class="text-sm w-48 text-gray-500">
|
||||||
|
Share form URL:
|
||||||
|
</p>
|
||||||
<ShareFormUrl
|
<ShareFormUrl
|
||||||
class="flex-grow my-4"
|
class="flex-grow my-4"
|
||||||
:form="form"
|
:form="form"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex py-2 items-center max-w-full">
|
||||||
|
<p class="text-sm w-48 text-gray-500">
|
||||||
|
Check your submissions:
|
||||||
|
</p>
|
||||||
<UButton
|
<UButton
|
||||||
v-track.form_first_submission_modal_open_db_click
|
|
||||||
color="white"
|
color="white"
|
||||||
icon="i-logos-notion-icon"
|
icon="i-heroicons-document"
|
||||||
:to="form.notion_database_url"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
@click="trackOpenDbClick"
|
||||||
>
|
>
|
||||||
See Notion database
|
See Submissions
|
||||||
</UButton>
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -39,11 +46,11 @@
|
||||||
v-for="(item, i) in helpLinks"
|
v-for="(item, i) in helpLinks"
|
||||||
:key="i"
|
:key="i"
|
||||||
role="button"
|
role="button"
|
||||||
class="bg-white shadow border border-gray-200 rounded-lg p-4 pb-2 items-center justify-center flex flex-col relative hover:bg-gray-50 group transition-colors"
|
class="bg-white shadow border border-gray-200 rounded-lg p-4 pb-2 items-center justify-center flex flex-col relative hover:bg-gray-50 dark:hover:bg-gray-800 group transition-colors"
|
||||||
@click="item.action"
|
@click="item.action"
|
||||||
>
|
>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="h-8 w-8 text-gray-600 group-hover:text-gray-800 transition-colors flex items-center">
|
<div class="h-8 w-8 text-gray-600 group-hover:text-gray-800 dark:group-hover:text-white transition-colors flex items-center">
|
||||||
<Icon
|
<Icon
|
||||||
:name="item.icon"
|
:name="item.icon"
|
||||||
class=""
|
class=""
|
||||||
|
|
@ -51,7 +58,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-gray-500 font-medium text-xs text-center my-2">
|
<p class="text-gray-500 font-medium text-xs text-center my-2">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -62,36 +69,23 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import ShareFormUrl from '~/components/notion/forms/components/ShareFormUrl.vue'
|
import ShareFormUrl from '~/components/open/forms/components/ShareFormUrl.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
show: { type: Boolean, required: true },
|
show: { type: Boolean, required: true },
|
||||||
form: { type: Object, required: true }
|
form: { type: Object, required: true }
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(['close'])
|
||||||
const confetti = useConfetti()
|
const confetti = useConfetti()
|
||||||
const crisp = useCrisp()
|
const crisp = useCrisp()
|
||||||
|
const amplitude = useAmplitude()
|
||||||
watch(() => props.show, () => {
|
watch(() => props.show, () => {
|
||||||
if (props.show) {
|
if (props.show) {
|
||||||
confetti.play()
|
confetti.play()
|
||||||
useAmplitude().logEvent('form_first_submission_modal_viewed')
|
useAmplitude().logEvent('form_first_submission_modal_viewed')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const helpLinks = computed(() => {
|
const helpLinks = computed(() => {
|
||||||
return [
|
return [
|
||||||
{
|
|
||||||
'label': 'Embed form on your website',
|
|
||||||
'icon': 'heroicons:code-bracket',
|
|
||||||
'action': () => crisp.openHelpdeskArticle('how-to-embed-your-form-on-your-website-yqa6i')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'label': 'Embed form in Notion',
|
|
||||||
'icon': 'ri:notion-fill',
|
|
||||||
'action': () => crisp.openHelpdeskArticle('can-i-embed-my-form-in-a-notion-page-or-site-11iroj9')
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'label': 'Help Center',
|
'label': 'Help Center',
|
||||||
'icon': 'heroicons:book-open',
|
'icon': 'heroicons:book-open',
|
||||||
|
|
@ -105,4 +99,10 @@ const helpLinks = computed(() => {
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
const trackOpenDbClick = () => {
|
||||||
|
const submissionsUrl = props.form.submissions_url
|
||||||
|
window.open(submissionsUrl, '_blank')
|
||||||
|
amplitude.logEvent('form_first_submission_modal_open_db_click')
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="border border-nt-blue-light bg-blue-50 dark:bg-notion-dark-light rounded-md p-2 overflow-hidden"
|
||||||
|
>
|
||||||
|
<div class="flex items-center w-full gap-2">
|
||||||
|
<p class="select-all text-nt-blue flex-grow truncate overflow-hidden">
|
||||||
|
<a
|
||||||
|
v-if="link"
|
||||||
|
:href="share_url"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ share_url }}
|
||||||
|
</a>
|
||||||
|
<span v-else>
|
||||||
|
{{ share_url }}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<UButton
|
||||||
|
class="shrink-0"
|
||||||
|
size="sm"
|
||||||
|
icon="i-heroicons-clipboard-document"
|
||||||
|
label="Copy"
|
||||||
|
@click="copyToClipboard"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, defineProps } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
extraQueryParam: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const { copy } = useClipboard()
|
||||||
|
|
||||||
|
const share_url = computed(() => {
|
||||||
|
if (props.extraQueryParam) {
|
||||||
|
return `${props.form.share_url}?${props.extraQueryParam}`
|
||||||
|
}
|
||||||
|
return props.form.share_url
|
||||||
|
})
|
||||||
|
|
||||||
|
function copyToClipboard() {
|
||||||
|
if (import.meta.server)
|
||||||
|
return
|
||||||
|
copy(share_url.value)
|
||||||
|
if (props.form.visibility == 'draft') {
|
||||||
|
useAlert().warning(
|
||||||
|
'Copied! But other people won\'t be able to see the form since it\'s currently in draft mode',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
useAlert().success('Copied!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ export default defineNuxtConfig({
|
||||||
...process.env.NUXT_PUBLIC_GTM_CODE ? ['@zadigetvoltaire/nuxt-gtm'] : [],
|
...process.env.NUXT_PUBLIC_GTM_CODE ? ['@zadigetvoltaire/nuxt-gtm'] : [],
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
transpile: process.env.NODE_ENV === "development" ? [] : ["vue-notion", "query-builder-vue-3", "vue-signature-pad"],
|
transpile: ["vue-notion", "query-builder-vue-3", "vue-signature-pad"],
|
||||||
},
|
},
|
||||||
experimental: {
|
experimental: {
|
||||||
inlineRouteRules: true
|
inlineRouteRules: true
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -16,62 +16,62 @@
|
||||||
"@nuxt/devtools": "~1.0.0",
|
"@nuxt/devtools": "~1.0.0",
|
||||||
"@nuxt/eslint-config": "^0.2.0",
|
"@nuxt/eslint-config": "^0.2.0",
|
||||||
"@zadigetvoltaire/nuxt-gtm": "^0.0.13",
|
"@zadigetvoltaire/nuxt-gtm": "^0.0.13",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.20",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
"nuxt": "^3.9.1",
|
"nuxt": "^3.13.2",
|
||||||
"nuxt-icon": "^0.6.10",
|
"nuxt-icon": "^0.6.10",
|
||||||
"nuxt-simple-sitemap": "^4.2.3",
|
"nuxt-simple-sitemap": "^4.4.1",
|
||||||
"nuxt-utm": "^0.1.10",
|
"nuxt-utm": "^0.1.10",
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.47",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.3.3",
|
||||||
"sass": "^1.69.6",
|
"sass": "^1.80.2",
|
||||||
"vue": "^3.3.10",
|
"vue": "^3.3.10",
|
||||||
"vue-router": "^4.2.5"
|
"vue-router": "^4.4.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-html": "^6.4.7",
|
"@codemirror/lang-html": "^6.4.9",
|
||||||
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
|
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
|
||||||
"@iconify-json/material-symbols": "^1.1.82",
|
"@iconify-json/material-symbols": "^1.2.4",
|
||||||
"@nuxt/ui": "^2.15.0",
|
"@nuxt/ui": "^2.18.7",
|
||||||
"@pinia/nuxt": "^0.5.1",
|
"@pinia/nuxt": "^0.5.5",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@sentry/vite-plugin": "^2.10.2",
|
"@sentry/vite-plugin": "^2.22.6",
|
||||||
"@sentry/vue": "^7.93.0",
|
"@sentry/vue": "^7.119.2",
|
||||||
"@vueuse/components": "^10.5.0",
|
"vue": "^3.2.13",
|
||||||
|
"@vueuse/components": "^10.11.1",
|
||||||
"@vueuse/core": "^10.5.0",
|
"@vueuse/core": "^10.5.0",
|
||||||
"@vueuse/motion": "^2.0.0",
|
"@vueuse/motion": "^2.2.6",
|
||||||
"@vueuse/nuxt": "^10.7.0",
|
"@vueuse/nuxt": "^10.11.1",
|
||||||
"amplitude-js": "^8.21.9",
|
"amplitude-js": "^8.21.9",
|
||||||
"chart.js": "^4.4.0",
|
"chart.js": "^4.4.5",
|
||||||
"clone-deep": "^4.0.1",
|
"clone-deep": "^4.0.1",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"crisp-sdk-web": "^1.0.21",
|
"crisp-sdk-web": "^1.0.25",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
"debounce": "^1.2.1",
|
"debounce": "^1.2.1",
|
||||||
"esbuild": "^0.23.0",
|
"esbuild": "^0.23.1",
|
||||||
"fuse.js": "^6.4.6",
|
"fuse.js": "^6.6.2",
|
||||||
"js-sha256": "^0.10.0",
|
"js-sha256": "^0.10.1",
|
||||||
"libphonenumber-js": "^1.10.44",
|
"libphonenumber-js": "^1.11.12",
|
||||||
"object-to-formdata": "^4.5.1",
|
"object-to-formdata": "^4.5.1",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.2.4",
|
||||||
"prismjs": "^1.24.1",
|
"prismjs": "^1.29.0",
|
||||||
"qrcode": "^1.5.1",
|
"qrcode": "^1.5.4",
|
||||||
"query-builder-vue-3": "^1.0.1",
|
"query-builder-vue-3": "^1.0.1",
|
||||||
"tailwind-merge": "^2.3.0",
|
"tailwind-merge": "^2.5.4",
|
||||||
"tinymotion": "^0.2.0",
|
"tinymotion": "^0.2.0",
|
||||||
"v-calendar": "^3.1.2",
|
"v-calendar": "^3.1.2",
|
||||||
"vue": "^3.2.13",
|
"vue-chartjs": "^5.3.1",
|
||||||
"vue-chartjs": "^5.2.0",
|
|
||||||
"vue-codemirror": "^6.1.1",
|
"vue-codemirror": "^6.1.1",
|
||||||
"vue-confetti": "^2.3.0",
|
"vue-confetti": "^2.3.0",
|
||||||
"vue-country-flag-next": "^2.3.2",
|
"vue-country-flag-next": "^2.3.2",
|
||||||
"vue-json-pretty": "^2.4.0",
|
"vue-json-pretty": "^2.4.0",
|
||||||
"vue-notion": "^3.0.0-beta.1",
|
"vue-notion": "^3.0.0",
|
||||||
"vue-signature-pad": "^3.0.2",
|
"vue-signature-pad": "^3.0.2",
|
||||||
"vue3-editor": "^0.1.1",
|
"vue3-editor": "^0.1.1",
|
||||||
"vuedraggable": "next",
|
"vuedraggable": "^4.1.0",
|
||||||
"webcam-easy": "^1.1.1"
|
"webcam-easy": "^1.1.1"
|
||||||
},
|
},
|
||||||
"eslintIgnore": [
|
"eslintIgnore": [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue