Nuxt Migration notifications (#265)
* Nuxt Migration notifications * @input to @update:model-value * change field type fixes * @update:model-value * Enable form-block-logic-editor * vue-confetti migration * PR request changes * useAlert in setup
This commit is contained in:
parent
b4365b5e30
commit
6fd2985ff5
|
@ -27,7 +27,8 @@
|
|||
<NuxtPage/>
|
||||
</NuxtLayout>
|
||||
<ToolsStopImpersonation/>
|
||||
<!-- <notifications />-->
|
||||
|
||||
<Notifications />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div class="fixed top-0 bottom-24 right-0 flex px-4 items-start justify-end z-50 pointer-events-none">
|
||||
<notification v-slot="{ notifications, close }">
|
||||
<div class="relative pointer-events-auto" v-for="notification in notifications" :key="notification.id">
|
||||
<div class="fixed top-0 bottom-24 right-0 flex px-4 items-start justify-end z-50 relative pointer-events-auto">
|
||||
<NuxtNotifications>
|
||||
<template #body="props">
|
||||
<div
|
||||
v-if="notification.type==='success'"
|
||||
v-if="props.item.type==='success'"
|
||||
class="flex max-w-sm w-full mx-auto bg-white shadow-md rounded-lg overflow-hidden mt-4"
|
||||
>
|
||||
<div class="flex justify-center items-center w-12 bg-green-500">
|
||||
|
@ -14,13 +14,13 @@
|
|||
|
||||
<div class="-mx-3 py-2 px-4">
|
||||
<div class="mx-3">
|
||||
<span class="text-green-500 font-semibold pr-6">{{notification.title}}</span>
|
||||
<p class="text-gray-600 text-sm">{{notification.text}}</p>
|
||||
<span class="text-green-500 font-semibold pr-6">{{props.item.title}}</span>
|
||||
<p class="text-gray-600 text-sm">{{props.item.text}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="notification.type==='info'"
|
||||
v-if="props.item.type==='info'"
|
||||
class="flex max-w-sm w-full mx-auto bg-white shadow-md rounded-lg overflow-hidden mt-4"
|
||||
>
|
||||
<div class="flex justify-center items-center w-12 bg-blue-500">
|
||||
|
@ -31,13 +31,13 @@
|
|||
|
||||
<div class="-mx-3 py-2 px-4">
|
||||
<div class="mx-3">
|
||||
<span class="text-blue-500 font-semibold pr-6">{{notification.title}}</span>
|
||||
<p class="text-gray-600 text-sm">T{{notification.text}}</p>
|
||||
<span class="text-blue-500 font-semibold pr-6">{{props.item.title}}</span>
|
||||
<p class="text-gray-600 text-sm">T{{props.item.text}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="notification.type==='error'"
|
||||
v-if="props.item.type==='error'"
|
||||
class="flex max-w-sm w-full mx-auto bg-white shadow-md rounded-lg overflow-hidden mt-4"
|
||||
>
|
||||
<div class="flex justify-center items-center w-12 bg-red-500">
|
||||
|
@ -54,14 +54,14 @@
|
|||
|
||||
<div class="-mx-3 py-2 px-4">
|
||||
<div class="mx-3">
|
||||
<span class="text-red-500 font-semibold pr-6">{{notification.title}}</span>
|
||||
<p class="text-gray-600 text-sm">{{notification.text}}</p>
|
||||
<span class="text-red-500 font-semibold pr-6">{{props.item.title}}</span>
|
||||
<p class="text-gray-600 text-sm">{{props.item.text}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex max-w-sm w-full mx-auto bg-white shadow-md rounded-lg overflow-hidden mt-4"
|
||||
v-if="notification.type==='warning'"
|
||||
v-if="props.item.type==='warning'"
|
||||
>
|
||||
<div class="flex justify-center items-center w-12 bg-yellow-500">
|
||||
<svg
|
||||
|
@ -77,14 +77,14 @@
|
|||
|
||||
<div class="-mx-3 py-2 px-4">
|
||||
<div class="mx-3">
|
||||
<span class="text-yellow-500 font-semibold pr-6">{{notification.title}}</span>
|
||||
<p class="text-gray-600 text-sm">{{notification.text}}</p>
|
||||
<span class="text-yellow-500 font-semibold pr-6">{{props.item.title}}</span>
|
||||
<p class="text-gray-600 text-sm">{{props.item.text}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex max-w-sm w-full mx-auto bg-white shadow-md rounded-lg overflow-hidden mt-4"
|
||||
v-if="notification.type==='confirm'"
|
||||
v-if="props.item.type==='confirm'"
|
||||
>
|
||||
<div class="flex justify-center items-center w-12 bg-blue-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6 text-white">
|
||||
|
@ -94,16 +94,16 @@
|
|||
|
||||
<div class="-mx-3 py-2 px-4">
|
||||
<div class="mx-3">
|
||||
<span class="text-blue-500 font-semibold pr-6">{{notification.title}}</span>
|
||||
<p class="text-gray-600 text-sm">{{notification.text}}</p>
|
||||
<span class="text-blue-500 font-semibold pr-6">{{props.item.title}}</span>
|
||||
<p class="text-gray-600 text-sm">{{props.item.text}}</p>
|
||||
<div class="w-full flex gap-2 mt-1">
|
||||
<v-button color="blue" size="small" @click.prevent="notification.success();close(notification.id)">Yes</v-button>
|
||||
<v-button color="gray" shade="light" size="small" @click.prevent="notification.failure();close(notification.id)">No</v-button>
|
||||
<v-button color="blue" size="small" @click.prevent="props.item.data.success();props.close()">Yes</v-button>
|
||||
<v-button color="gray" shade="light" size="small" @click.prevent="props.item.data.failure();props.close()">No</v-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="close(notification.id)" class="absolute top-0 right-0 px-2 py-2 cursor-pointer">
|
||||
<button @click="props.close()" class="absolute top-0 right-0 px-2 py-2 cursor-pointer">
|
||||
<svg
|
||||
class="fill-current h-6 w-6 text-gray-300 hover:text-gray-500"
|
||||
role="button"
|
||||
|
@ -116,8 +116,8 @@
|
|||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</notification>
|
||||
</template>
|
||||
</NuxtNotifications>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -34,6 +34,11 @@ export default {
|
|||
default: () => {}
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
return {
|
||||
useAlert: useAlert()
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
|
@ -44,18 +49,18 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
onDeleteClick () {
|
||||
this.alertConfirm('Do you really want to delete this record?', this.deleteRecord)
|
||||
this.useAlert.confirm('Do you really want to delete this record?', this.deleteRecord)
|
||||
},
|
||||
async deleteRecord () {
|
||||
axios.delete('/api/open/forms/' + this.form.id + '/records/' + this.rowid + '/delete').then(async (response) => {
|
||||
if (response.data.type === 'success') {
|
||||
this.$emit('deleted')
|
||||
this.alertSuccess(response.data.message)
|
||||
this.useAlert.success(response.data.message)
|
||||
} else {
|
||||
this.alertError('Something went wrong!')
|
||||
this.useAlert.error('Something went wrong!')
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.alertError(error.response.data.message)
|
||||
this.useAlert.error(error.response.data.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,7 +129,8 @@ export default {
|
|||
setup(props) {
|
||||
return {
|
||||
isIframe: useIsIframe(),
|
||||
pendingSubmission: pendingSubmission(props.form)
|
||||
pendingSubmission: pendingSubmission(props.form),
|
||||
confetti: useConfetti()
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -214,12 +215,11 @@ export default {
|
|||
|
||||
// If enabled display confetti
|
||||
if (this.form.confetti_on_submission) {
|
||||
this.playConfetti()
|
||||
this.confetti.play()
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.response && error.response.data && error.response.data.message) {
|
||||
console.error(error)
|
||||
// this.alertError(error.response.data.message)
|
||||
useAlert().error(error.response.data.message)
|
||||
}
|
||||
this.loading = false
|
||||
onFailure()
|
||||
|
|
|
@ -56,9 +56,9 @@ export default {
|
|||
document.execCommand('copy')
|
||||
document.body.removeChild(el)
|
||||
if(this.isDraft){
|
||||
this.alertWarning('Copied! But other people won\'t be able to see the form since it\'s currently in draft mode')
|
||||
useAlert().warning('Copied! But other people won\'t be able to see the form since it\'s currently in draft mode')
|
||||
} else {
|
||||
this.alertSuccess('Copied!')
|
||||
useAlert().success('Copied!')
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -217,9 +217,9 @@ export default {
|
|||
methods: {
|
||||
displayFormModificationAlert (responseData) {
|
||||
if (responseData.form && responseData.form.cleanings && Object.keys(responseData.form.cleanings).length > 0) {
|
||||
// this.alertWarning(responseData.message)
|
||||
useAlert().warning(responseData.message)
|
||||
} else {
|
||||
// this.alertSuccess(responseData.message)
|
||||
useAlert().success(responseData.message)
|
||||
}
|
||||
},
|
||||
openCrisp () {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
/>
|
||||
<div class="-mt-3 mb-3 text-gray-400 dark:text-gray-500">
|
||||
<small>
|
||||
Need another theme? <a href="#" @click.prevent="openChat">Send us some suggestions!</a>
|
||||
Need another theme? <a href="#" @click.prevent="crisp.openAndShowChat">Send us some suggestions!</a>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
@ -80,56 +80,25 @@
|
|||
</editor-options-panel>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { useWorkingFormStore } from '../../../../../stores/working_form'
|
||||
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
|
||||
import ProTag from '~/components/global/ProTag.vue'
|
||||
|
||||
export default {
|
||||
components: { EditorOptionsPanel, ProTag },
|
||||
props: {
|
||||
},
|
||||
setup () {
|
||||
const workingFormStore = useWorkingFormStore()
|
||||
return {
|
||||
workingFormStore
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isMounted: false
|
||||
}
|
||||
},
|
||||
const form = storeToRefs(workingFormStore).content
|
||||
const isMounted = ref(false)
|
||||
const crisp = useCrisp()
|
||||
const confetti = useConfetti()
|
||||
|
||||
computed: {
|
||||
form: {
|
||||
get () {
|
||||
return this.workingFormStore.content
|
||||
},
|
||||
/* We add a setter */
|
||||
set (value) {
|
||||
this.workingFormStore.set(value)
|
||||
}
|
||||
}
|
||||
},
|
||||
onMounted(() => {
|
||||
isMounted.value = true
|
||||
})
|
||||
|
||||
watch: {},
|
||||
|
||||
mounted () {
|
||||
this.isMounted = true
|
||||
},
|
||||
|
||||
methods: {
|
||||
onChangeConfettiOnSubmission (val) {
|
||||
this.form.confetti_on_submission = val
|
||||
if (this.isMounted && val) {
|
||||
this.playConfetti()
|
||||
}
|
||||
},
|
||||
openChat () {
|
||||
window.$crisp.push(['do', 'chat:show'])
|
||||
window.$crisp.push(['do', 'chat:open'])
|
||||
}
|
||||
const onChangeConfettiOnSubmission = (val) => {
|
||||
form.confetti_on_submission = val
|
||||
if (isMounted.value && val) {
|
||||
confetti.play()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -6,20 +6,20 @@
|
|||
</div>
|
||||
<SelectInput v-model="content.operator" class="w-full" :options="operators"
|
||||
:name="'operator_'+property.id" placeholder="Comparison operator"
|
||||
@update:modelValue="operatorChanged()"
|
||||
@update:model-value="operatorChanged()"
|
||||
/>
|
||||
|
||||
<template v-if="hasInput">
|
||||
<component v-bind="inputComponentData" :is="inputComponentData.component" v-model="content.value" class="w-full"
|
||||
:name="'value_'+property.id" placeholder="Filter Value"
|
||||
@update:modelValue="emitInput()"
|
||||
@update:model-value="emitInput()"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OpenFilters from '../../../../../../data/open_filters.json'
|
||||
import OpenFilters from '../../../../../data/open_filters.json'
|
||||
|
||||
export default {
|
||||
components: { },
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<query-builder v-model="query" :rules="rules" :config="config" @update:modelValue="onChange">
|
||||
<query-builder v-model="query" :rules="rules" :config="config" @update:model-value="onChange">
|
||||
<template #groupOperator="props">
|
||||
<div class="query-builder-group-slot__group-selection flex items-center px-5 border-b py-1 mb-1 flex">
|
||||
<p class="mr-2 font-semibold">
|
||||
|
@ -13,7 +13,7 @@
|
|||
option-key="identifier"
|
||||
name="operator-input"
|
||||
margin-bottom=""
|
||||
@update:modelValue="props.updateCurrentOperator($event)"
|
||||
@update:model-value="props.updateCurrentOperator($event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -24,7 +24,7 @@
|
|||
<component
|
||||
:is="ruleCtrl.ruleComponent"
|
||||
:model-value="ruleCtrl.ruleData"
|
||||
@update:modelValue="ruleCtrl.updateRuleData"
|
||||
@update:model-value="ruleCtrl.updateRuleData"
|
||||
/>
|
||||
</template>
|
||||
</query-builder>
|
||||
|
|
|
@ -69,12 +69,11 @@
|
|||
<script>
|
||||
import ConditionEditor from './ConditionEditor.vue'
|
||||
import Modal from '../../../../global/Modal.vue'
|
||||
import SelectInput from '../../../../forms/SelectInput.vue'
|
||||
import clonedeep from 'clone-deep'
|
||||
|
||||
export default {
|
||||
name: 'FormBlockLogicEditor',
|
||||
components: { SelectInput, Modal, ConditionEditor },
|
||||
components: { Modal, ConditionEditor },
|
||||
props: {
|
||||
field: {
|
||||
type: Object,
|
||||
|
|
|
@ -93,7 +93,8 @@ export default {
|
|||
user : computed(() => authStore.user),
|
||||
templates : computed(() => templatesStore.content),
|
||||
industries : computed(() => templatesStore.industries),
|
||||
types : computed(() => templatesStore.types)
|
||||
types : computed(() => templatesStore.types),
|
||||
useAlert: useAlert()
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -156,7 +157,7 @@ export default {
|
|||
this.templateForm.form = this.form
|
||||
await this.templateForm.post('/api/templates').then((response) => {
|
||||
if (response.data.message) {
|
||||
this.alertSuccess(response.data.message)
|
||||
this.useAlert.success(response.data.message)
|
||||
}
|
||||
this.templatesStore.save(response.data.data)
|
||||
this.$emit('close')
|
||||
|
@ -166,7 +167,7 @@ export default {
|
|||
this.templateForm.form = this.form
|
||||
await this.templateForm.put('/api/templates/' + this.template.id).then((response) => {
|
||||
if (response.data.message) {
|
||||
this.alertSuccess(response.data.message)
|
||||
this.useAlert.success(response.data.message)
|
||||
}
|
||||
this.templatesStore.save(response.data.data)
|
||||
this.$emit('close')
|
||||
|
@ -176,7 +177,7 @@ export default {
|
|||
if (!this.template) return
|
||||
axios.delete('/api/templates/' + this.template.id).then((response) => {
|
||||
if (response.data.message) {
|
||||
this.alertSuccess(response.data.message)
|
||||
this.useAlert.success(response.data.message)
|
||||
}
|
||||
this.$router.push({ name: 'templates' })
|
||||
this.templatesStore.remove(this.template)
|
||||
|
|
|
@ -109,7 +109,7 @@ export default {
|
|||
return this.field && this.field.type.startsWith('nf')
|
||||
},
|
||||
typeCanBeChanged () {
|
||||
return ['text', 'email', 'phone', 'number', 'select', 'multi_select'].includes(this.field.type)
|
||||
return ['text', 'email', 'phone_number', 'number', 'select', 'multi_select'].includes(this.field.type)
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -86,14 +86,16 @@
|
|||
</div>
|
||||
|
||||
<!-- Logic Block -->
|
||||
<!-- <form-block-logic-editor class="py-2 px-4 border-b" :form="form" :field="field" />-->
|
||||
<form-block-logic-editor class="py-2 px-4 border-b" :form="form" :field="field" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormBlockLogicEditor from '../../components/form-logic-components/FormBlockLogicEditor.vue'
|
||||
|
||||
export default {
|
||||
name: 'BlockOptions',
|
||||
components: { },
|
||||
components: {FormBlockLogicEditor},
|
||||
props: {
|
||||
field: {
|
||||
type: Object,
|
||||
|
|
|
@ -41,11 +41,11 @@ export default {
|
|||
computed: {
|
||||
changeTypeOptions () {
|
||||
let newTypes = []
|
||||
if (['text', 'email', 'phone', 'number'].includes(this.field.type)) {
|
||||
if (['text', 'email', 'phone_number', 'number'].includes(this.field.type)) {
|
||||
newTypes = [
|
||||
{ name: 'Text Input', value: 'text' },
|
||||
{ name: 'Email Input', value: 'email' },
|
||||
{ name: 'Phone Input', value: 'phone' },
|
||||
{ name: 'Phone Input', value: 'phone_number' },
|
||||
{ name: 'Number Input', value: 'number' }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
/>
|
||||
|
||||
<v-checkbox v-model="field.is_scale" class="mt-4"
|
||||
:name="field.id+'_is_scale'" @input="initScale"
|
||||
:name="field.id+'_is_scale'" @update:model-value="initScale"
|
||||
>
|
||||
Scale
|
||||
</v-checkbox>
|
||||
|
@ -337,7 +337,7 @@
|
|||
{name:'Above input',value:'above_input'},
|
||||
]"
|
||||
:form="field" label="Field Help Position"
|
||||
@input="onFieldHelpPositionChange"
|
||||
@update:model-value="onFieldHelpPositionChange"
|
||||
/>
|
||||
|
||||
<template v-if="['text','number','url','email'].includes(field.type)">
|
||||
|
@ -382,7 +382,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Logic Block -->
|
||||
<!-- <form-block-logic-editor class="py-2 px-4 border-b" :form="form" :field="field" />-->
|
||||
<form-block-logic-editor class="py-2 px-4 border-b" :form="form" :field="field" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -390,10 +390,11 @@
|
|||
import timezones from '~/data/timezones.json'
|
||||
import countryCodes from '~/data/country_codes.json'
|
||||
import CountryFlag from 'vue-country-flag-next'
|
||||
import FormBlockLogicEditor from '../../components/form-logic-components/FormBlockLogicEditor.vue'
|
||||
|
||||
export default {
|
||||
name: 'FieldOptions',
|
||||
components: { CountryFlag },
|
||||
components: { CountryFlag, FormBlockLogicEditor },
|
||||
props: {
|
||||
field: {
|
||||
type: Object,
|
||||
|
@ -533,23 +534,23 @@ export default {
|
|||
},
|
||||
initRating () {
|
||||
if (this.field.is_rating) {
|
||||
this.$set(this.field, 'is_scale', false)
|
||||
this.field.is_scale = false
|
||||
if (!this.field.rating_max_value) {
|
||||
this.$set(this.field, 'rating_max_value', 5)
|
||||
this.field.rating_max_value = 5
|
||||
}
|
||||
}
|
||||
},
|
||||
initScale () {
|
||||
if (this.field.is_scale) {
|
||||
this.$set(this.field, 'is_rating', false)
|
||||
this.field.is_rating = false
|
||||
if (!this.field.scale_min_value) {
|
||||
this.$set(this.field, 'scale_min_value', 1)
|
||||
this.field.scale_min_value = 1
|
||||
}
|
||||
if (!this.field.scale_max_value) {
|
||||
this.$set(this.field, 'scale_max_value', 5)
|
||||
this.field.scale_max_value = 5
|
||||
}
|
||||
if (!this.field.scale_step_value) {
|
||||
this.$set(this.field, 'scale_step_value', 1)
|
||||
this.field.scale_step_value = 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -14,8 +14,7 @@ export default {
|
|||
components: { OpenTag },
|
||||
props: {
|
||||
value: {
|
||||
type: Object | null,
|
||||
required: true
|
||||
type: Object
|
||||
}
|
||||
|
||||
},
|
||||
|
|
|
@ -129,10 +129,10 @@ export default {
|
|||
|
||||
// AppSumo License
|
||||
if (data.appsumo_license === false) {
|
||||
this.alertError('Invalid AppSumo license. This probably happened because this license was already' +
|
||||
useAlert().error('Invalid AppSumo license. This probably happened because this license was already' +
|
||||
' attached to another OpnForm account. Please contact support.')
|
||||
} else if (data.appsumo_license === true) {
|
||||
this.alertSuccess('Your AppSumo license was successfully activated! You now have access to all the' +
|
||||
useAlert().success('Your AppSumo license was successfully activated! You now have access to all the' +
|
||||
' features of the AppSumo deal.')
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,11 @@ export default {
|
|||
props: {
|
||||
show: { type: Boolean, required: true }
|
||||
},
|
||||
|
||||
setup () {
|
||||
return {
|
||||
useAlert: useAlert()
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
state: 'default',
|
||||
aiForm: useForm({
|
||||
|
@ -118,10 +122,10 @@ export default {
|
|||
|
||||
this.loading = true
|
||||
this.aiForm.post('/api/forms/ai/generate').then(response => {
|
||||
this.alertSuccess(response.data.message)
|
||||
this.useAlert.success(response.data.message)
|
||||
this.fetchGeneratedForm(response.data.ai_form_completion_id)
|
||||
}).catch(error => {
|
||||
this.alertError(error.response.data.message)
|
||||
this.useAlert.error(error.response.data.message)
|
||||
this.loading = false
|
||||
this.state = 'default'
|
||||
})
|
||||
|
@ -131,18 +135,18 @@ export default {
|
|||
setTimeout(() => {
|
||||
axios.get('/api/forms/ai/' + generationId).then(response => {
|
||||
if (response.data.ai_form_completion.status === 'completed') {
|
||||
this.alertSuccess(response.data.message)
|
||||
this.useAlert.success(response.data.message)
|
||||
this.$emit('form-generated', JSON.parse(response.data.ai_form_completion.result))
|
||||
this.$emit('close')
|
||||
} else if (response.data.ai_form_completion.status === 'failed') {
|
||||
this.alertError('Something went wrong, please try again.')
|
||||
this.useAlert.error('Something went wrong, please try again.')
|
||||
this.state = 'default'
|
||||
this.loading = false
|
||||
} else {
|
||||
this.fetchGeneratedForm(generationId)
|
||||
}
|
||||
}).catch(error => {
|
||||
this.alertError(error.response.data.message)
|
||||
this.useAlert.error(error.response.data.message)
|
||||
this.state = 'default'
|
||||
this.loading = false
|
||||
})
|
||||
|
|
|
@ -157,7 +157,8 @@ export default {
|
|||
const formsStore = useFormsStore()
|
||||
return {
|
||||
formsStore,
|
||||
user: computed(() => authStore.user)
|
||||
user: computed(() => authStore.user),
|
||||
useAlert: useAlert()
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -180,7 +181,7 @@ export default {
|
|||
el.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(el)
|
||||
this.alertSuccess('Copied!')
|
||||
this.useAlert.success('Copied!')
|
||||
},
|
||||
duplicateForm () {
|
||||
if (this.loadingDuplicate) return
|
||||
|
@ -188,7 +189,7 @@ export default {
|
|||
opnFetch(this.formEndpoint.replace('{id}', this.form.id) + '/duplicate',{method: 'POST'}).then((data) => {
|
||||
this.formsStore.save(data.new_form)
|
||||
this.$router.push({ name: 'forms-show', params: { slug: data.new_form.slug } })
|
||||
this.alertSuccess('Form was successfully duplicated.')
|
||||
this.useAlert.success('Form was successfully duplicated.')
|
||||
this.loadingDuplicate = false
|
||||
})
|
||||
},
|
||||
|
@ -198,7 +199,7 @@ export default {
|
|||
opnFetch(this.formEndpoint.replace('{id}', this.form.id),{method:'DELETE'}).then(() => {
|
||||
this.formsStore.remove(this.form)
|
||||
this.$router.push({ name: 'home' })
|
||||
this.alertSuccess('Form was deleted.')
|
||||
this.useAlert.success('Form was deleted.')
|
||||
this.loadingDelete = false
|
||||
})
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ export default {
|
|||
axios.put(this.formEndpoint.replace('{id}', this.form.id) + '/regenerate-link/' + option).then((response) => {
|
||||
this.formsStore.addOrUpdate(response.data.form)
|
||||
this.$router.push({name: 'forms-slug-show-share', params: {slug: response.data.form.slug}})
|
||||
this.alertSuccess(response.data.message)
|
||||
useAlert().success(response.data.message)
|
||||
this.loadingNewLink = false
|
||||
}).finally(() => {
|
||||
this.showGenerateFormLinkModal = false
|
||||
|
|
|
@ -84,7 +84,7 @@ export default {
|
|||
axios.get('/api/subscription/new/' + this.plan + '/' + (!this.yearly ? 'monthly' : 'yearly') + '/checkout/with-trial').then((response) => {
|
||||
window.location = response.data.checkout_url
|
||||
}).catch((error) => {
|
||||
this.alertError(error.response.data.message)
|
||||
useAlert().error(error.response.data.message)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
this.close()
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
const { notify } = useNotification()
|
||||
|
||||
export const useAlert = () => {
|
||||
|
||||
function success(message, autoClose = 10000) {
|
||||
notify({
|
||||
title: 'Success',
|
||||
text: message,
|
||||
type: 'success',
|
||||
duration: autoClose
|
||||
})
|
||||
}
|
||||
|
||||
function error(message, autoClose = 10000) {
|
||||
notify({
|
||||
title: 'Error',
|
||||
text: message,
|
||||
type: 'error',
|
||||
duration: autoClose
|
||||
})
|
||||
}
|
||||
|
||||
function warning(message, autoClose = 10000) {
|
||||
notify({
|
||||
title: 'Warning',
|
||||
text: message,
|
||||
type: 'warning',
|
||||
duration: autoClose
|
||||
})
|
||||
}
|
||||
|
||||
function confirm(message, success, failure = ()=>{}, autoClose = 10000) {
|
||||
notify({
|
||||
title: 'Confirm',
|
||||
text: message,
|
||||
type: 'confirm',
|
||||
duration: autoClose,
|
||||
data: {
|
||||
success,
|
||||
failure
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
success,
|
||||
error,
|
||||
warning,
|
||||
confirm
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { ref, onUnmounted } from 'vue'
|
||||
|
||||
export const useConfetti = () => {
|
||||
let timeoutId = ref(null)
|
||||
const nuxtApp = useNuxtApp()
|
||||
const $confetti = nuxtApp.vueApp.config.globalProperties.$confetti
|
||||
|
||||
function play(duration=3000) {
|
||||
$confetti.start({ defaultSize: 6 })
|
||||
timeoutId = setTimeout(() => {
|
||||
$confetti.stop()
|
||||
}, duration)
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timeoutId) clearTimeout(timeoutId)
|
||||
})
|
||||
|
||||
return {
|
||||
play
|
||||
}
|
||||
}
|
|
@ -2,9 +2,9 @@ export default {
|
|||
methods: {
|
||||
displayFormModificationAlert (responseData) {
|
||||
if (responseData.form && responseData.form.cleanings && Object.keys(responseData.form.cleanings).length > 0) {
|
||||
this.alertWarning(responseData.message)
|
||||
useAlert().warning(responseData.message)
|
||||
} else {
|
||||
this.alertSuccess(responseData.message)
|
||||
useAlert().success(responseData.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ export default defineNuxtConfig({
|
|||
modules: [
|
||||
'@pinia/nuxt',
|
||||
'@vueuse/nuxt',
|
||||
'@vueuse/motion/nuxt'
|
||||
'@vueuse/motion/nuxt',
|
||||
'nuxt3-notifications'
|
||||
],
|
||||
postcss: {
|
||||
plugins: {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
|||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"dev": "export NODE_TLS_REJECT_UNAUTHORIZED=0; nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
|
@ -50,7 +50,7 @@
|
|||
"vue-notion": "^3.0.0-beta.1",
|
||||
"vue-signature-pad": "^3.0.2",
|
||||
"vue3-editor": "^0.1.1",
|
||||
"vue3-vt-notifications": "^1.0.0",
|
||||
"nuxt3-notifications": "^1.1.9",
|
||||
"vuedraggable": "next"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ export default {
|
|||
|
||||
beforeRouteLeave (to, from, next) {
|
||||
if (this.isDirty()) {
|
||||
return this.alertConfirm('Changes you made may not be saved. Are you sure want to leave?', () => {
|
||||
return useAlert().confirm('Changes you made may not be saved. Are you sure want to leave?', () => {
|
||||
window.onbeforeunload = null
|
||||
next()
|
||||
}, () => {})
|
||||
|
|
|
@ -30,7 +30,7 @@ import CreateFormBaseModal from '../../../components/pages/forms/create/CreateFo
|
|||
|
||||
// beforeRouteLeave (to, from, next) {
|
||||
// if (this.isDirty()) {
|
||||
// return this.alertConfirm('Changes you made may not be saved. Are you sure want to leave?', () => {
|
||||
// return useAlert().confirm('Changes you made may not be saved. Are you sure want to leave?', () => {
|
||||
// window.onbeforeunload = null
|
||||
// next()
|
||||
// }, () => {})
|
||||
|
|
|
@ -41,14 +41,14 @@ export default {
|
|||
this.loading = true
|
||||
axios.delete('/api/user').then(async (response) => {
|
||||
this.loading = false
|
||||
this.alertSuccess(response.data.message)
|
||||
useAlert().success(response.data.message)
|
||||
// Log out the user.
|
||||
await this.authStore.logout()
|
||||
|
||||
// Redirect to login.
|
||||
this.$router.push({ name: 'login' })
|
||||
}).catch((error) => {
|
||||
this.alertError(error.response.data.message)
|
||||
useAlert().error(error.response.data.message)
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ export default {
|
|||
this.workspacesStore.set([])
|
||||
this.$router.push({ name: 'home' })
|
||||
}).catch((error) => {
|
||||
this.alertError(error.response.data.message)
|
||||
useAlert().error(error.response.data.message)
|
||||
this.loading = false
|
||||
})
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ export default {
|
|||
const url = response.data.portal_url
|
||||
window.location = url
|
||||
}).catch((error) => {
|
||||
this.alertError(error.response.data.message)
|
||||
useAlert().error(error.response.data.message)
|
||||
}).finally(() => {
|
||||
this.billingLoading = false
|
||||
})
|
||||
|
|
|
@ -169,9 +169,9 @@ export default {
|
|||
.filter(domain => domain && domain.length > 0)
|
||||
}).then((response) => {
|
||||
this.workspacesStore.addOrUpdate(response.data)
|
||||
this.alertSuccess('Custom domains saved.')
|
||||
useAlert().success('Custom domains saved.')
|
||||
}).catch((error) => {
|
||||
this.alertError('Failed to update custom domains: ' + error.response.data.message)
|
||||
useAlert().error('Failed to update custom domains: ' + error.response.data.message)
|
||||
}).finally(() => {
|
||||
this.customDomainsLoading = false
|
||||
})
|
||||
|
@ -182,12 +182,12 @@ export default {
|
|||
},
|
||||
deleteWorkspace (workspace) {
|
||||
if (this.workspaces.length <= 1) {
|
||||
this.alertError('You cannot delete your only workspace.')
|
||||
useAlert().error('You cannot delete your only workspace.')
|
||||
return
|
||||
}
|
||||
this.alertConfirm('Do you really want to delete this workspace? All forms created in this workspace will be removed.', () => {
|
||||
useAlert().confirm('Do you really want to delete this workspace? All forms created in this workspace will be removed.', () => {
|
||||
this.workspacesStore.delete(workspace.id).then(() => {
|
||||
this.alertSuccess('Workspace successfully removed.')
|
||||
useAlert().success('Workspace successfully removed.')
|
||||
})
|
||||
})
|
||||
},
|
||||
|
|
|
@ -24,7 +24,7 @@ export default {
|
|||
|
||||
mounted () {
|
||||
this.$router.push({ name: 'pricing' })
|
||||
this.alertError('Unfortunately we could not confirm your subscription. Please try again and contact us if the issue persists.')
|
||||
useAlert().error('Unfortunately we could not confirm your subscription. Please try again and contact us if the issue persists.')
|
||||
},
|
||||
|
||||
computed: {}
|
||||
|
|
|
@ -63,10 +63,10 @@ export default {
|
|||
this.$router.push({ name: 'home' })
|
||||
|
||||
if (this.user.has_enterprise_subscription) {
|
||||
this.alertSuccess('Awesome! Your subscription to OpnForm is now confirmed! You now have access to all Enterprise ' +
|
||||
useAlert().success('Awesome! Your subscription to OpnForm is now confirmed! You now have access to all Enterprise ' +
|
||||
'features. No need to invite your teammates, just ask them to create a OpnForm account and to connect the same Notion workspace. Feel free to contact us if you have any question 🙌')
|
||||
} else {
|
||||
this.alertSuccess('Awesome! Your subscription to OpnForm is now confirmed! You now have access to all Pro ' +
|
||||
useAlert().success('Awesome! Your subscription to OpnForm is now confirmed! You now have access to all Pro ' +
|
||||
'features. Feel free to contact us if you have any question 🙌')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -261,7 +261,7 @@ const copyTemplateUrl = () => {
|
|||
el.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(el)
|
||||
this.alertSuccess('Copied!')
|
||||
useAlert().success('Copied!')
|
||||
}
|
||||
|
||||
// metaTitle() {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import VueConfetti from 'vue-confetti'
|
||||
|
||||
export default defineNuxtPlugin(nuxtApp => {
|
||||
nuxtApp.vueApp.use(VueConfetti)
|
||||
})
|
|
@ -20,6 +20,11 @@ export const useWorkspacesStore = defineStore('workspaces', () => {
|
|||
storedWorkspaceId.value = id
|
||||
}
|
||||
|
||||
const set = (items) => {
|
||||
contentStore.content.value = new Map
|
||||
save(items)
|
||||
}
|
||||
|
||||
const save = (items) => {
|
||||
contentStore.save(items)
|
||||
if ((getCurrent.value == null) && contentStore.length.value) {
|
||||
|
@ -39,6 +44,7 @@ export const useWorkspacesStore = defineStore('workspaces', () => {
|
|||
currentId,
|
||||
getCurrent,
|
||||
setCurrentId,
|
||||
set,
|
||||
save,
|
||||
remove
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue