Fix sentry tracked issues

This commit is contained in:
Julien Nahum 2024-01-18 11:37:04 +01:00
parent ebdd06cbe6
commit c97be832d7
10 changed files with 136 additions and 131 deletions

View File

@ -130,7 +130,7 @@ export default {
}) ?? null }) ?? null
}, },
onInput (event) { onInput (event) {
this.inputVal = event.target.value.replace(/[^0-9]/g, '') this.inputVal = event?.target?.value.replace(/[^0-9]/g, '')
}, },
onChangeCountryCode () { onChangeCountryCode () {
if (!this.selectedCountryCode && this.countries.length > 0) { if (!this.selectedCountryCode && this.countries.length > 0) {

View File

@ -1,7 +1,8 @@
<template> <template>
<editor-options-panel name="Link Settings - SEO" :already-opened="false" :has-pro-tag="true"> <editor-options-panel name="Link Settings - SEO" :already-opened="false" :has-pro-tag="true">
<template #icon> <template #icon>
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" <path stroke-linecap="round" stroke-linejoin="round"
d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"
/> />
@ -10,7 +11,8 @@
<p class="mt-4 text-gray-500 text-sm"> <p class="mt-4 text-gray-500 text-sm">
Customize the link, images and text that appear when you share your form on other sites (Open Graph). Customize the link, images and text that appear when you share your form on other sites (Open Graph).
</p> </p>
<select-input v-if="customDomainAllowed" v-model="form.custom_domain" :disabled="customDomainOptions.length <= 0" :options="customDomainOptions" name="type" <select-input v-if="customDomainAllowed" v-model="form.custom_domain" :disabled="customDomainOptions.length <= 0"
:options="customDomainOptions" name="type"
class="mt-4" label="Form Domain" placeholder="yourdomain.com" class="mt-4" label="Form Domain" placeholder="yourdomain.com"
/> />
<text-input v-model="form.seo_meta.page_title" name="page_title" class="mt-4" <text-input v-model="form.seo_meta.page_title" name="page_title" class="mt-4"
@ -26,50 +28,49 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../stores/working_form' import {useWorkingFormStore} from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue' import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
export default { export default {
components: { EditorOptionsPanel }, components: {EditorOptionsPanel},
props: {}, props: {},
setup () { setup() {
const workingFormStore = useWorkingFormStore() const workingFormStore = useWorkingFormStore()
return { return {
workspacesStore: useWorkspacesStore(), workspacesStore: useWorkspacesStore(),
workingFormStore workingFormStore
} }
}, },
data () { data() {
return { return {}
}
}, },
computed: { computed: {
form: { form: {
get () { get() {
return this.workingFormStore.content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set(value) {
this.workingFormStore.set(value) this.workingFormStore.set(value)
} }
}, },
workspace () { workspace() {
return this.workspacesStore.getCurrent return this.workspacesStore.getCurrent
}, },
customDomainOptions () { customDomainOptions() {
return this.workspace.custom_domains.map((domain) => { return this.workspace.custom_domains ? this.workspace.custom_domains.map((domain) => {
return { return {
name: domain, name: domain,
value: domain value: domain
} }
}) }) : []
}, },
customDomainAllowed () { customDomainAllowed() {
return useRuntimeConfig().public.customDomainsEnabled return useRuntimeConfig().public.customDomainsEnabled
} }
}, },
watch: {}, watch: {},
mounted () { mounted() {
['page_title', 'page_description', 'page_thumbnail'].forEach((keyname) => { ['page_title', 'page_description', 'page_thumbnail'].forEach((keyname) => {
if (this.form.seo_meta[keyname] === undefined) { if (this.form.seo_meta[keyname] === undefined) {
this.form.seo_meta[keyname] = null this.form.seo_meta[keyname] = null

View File

@ -38,14 +38,14 @@ export default {
computed: { computed: {
parsedFiles() { parsedFiles() {
return this.value.map((file) => { return this.value ? this.value.map((file) => {
return { return {
file_name: file.file_name, file_name: file.file_name,
file_url: file.file_url, file_url: file.file_url,
displayed_file_name: this.displayedFileName(file.file_name), displayed_file_name: this.displayedFileName(file.file_name),
is_image: !this.failedImages.includes(file.file_url) && this.isImage(file.file_name) is_image: !this.failedImages.includes(file.file_url) && this.isImage(file.file_name)
} }
}) }): []
} }
}, },

View File

@ -1,10 +1,10 @@
<template> <template>
<div> <div>
<forgot-password-modal :show="showForgotModal" @close="showForgotModal=false" /> <forgot-password-modal :show="showForgotModal" @close="showForgotModal=false"/>
<form class="mt-4" @submit.prevent="login" @keydown="form.onKeydown($event)"> <form class="mt-4" @submit.prevent="login" @keydown="form.onKeydown($event)">
<!-- Email --> <!-- Email -->
<text-input name="email" :form="form" label="Email" :required="true" placeholder="Your email address" /> <text-input name="email" :form="form" label="Email" :required="true" placeholder="Your email address"/>
<!-- Password --> <!-- Password -->
<text-input native-type="password" placeholder="Your password" <text-input native-type="password" placeholder="Your password"
@ -18,7 +18,8 @@
</v-checkbox> </v-checkbox>
<div class="w-full md:w-1/2 text-right"> <div class="w-full md:w-1/2 text-right">
<a href="#" class="text-xs hover:underline text-gray-500 sm:text-sm hover:text-gray-700" @click.prevent="showForgotModal=true"> <a href="#" class="text-xs hover:underline text-gray-500 sm:text-sm hover:text-gray-700"
@click.prevent="showForgotModal=true">
Forgot your password? Forgot your password?
</a> </a>
</div> </div>
@ -58,7 +59,7 @@ export default {
} }
}, },
setup () { setup() {
return { return {
authStore: useAuthStore(), authStore: useAuthStore(),
formsStore: useFormsStore(), formsStore: useFormsStore(),
@ -76,27 +77,29 @@ export default {
}), }),
methods: { methods: {
async login () { login() {
// Submit the form. // Submit the form.
const data = await this.form.post('login') this.form.post('login').then(async (data) => {
// Save the token.
this.authStore.setToken(data.token)
// Save the token. const userData = await opnFetch('user')
this.authStore.setToken(data.token) this.authStore.setUser(userData)
const userData = await opnFetch('user') const workspaces = await fetchAllWorkspaces()
this.authStore.setUser(userData) this.workspaceStore.set(workspaces.data.value)
const workspaces = await fetchAllWorkspaces() // Load forms
this.workspaceStore.set(workspaces.data.value) this.formsStore.loadAll(this.workspaceStore.currentId)
// Load forms // Redirect home.
this.formsStore.loadAll(this.workspaceStore.currentId) this.redirect()
}).catch(() => {
// Redirect home. })
this.redirect()
}, },
redirect () { redirect() {
if (this.isQuick) { if (this.isQuick) {
this.$emit('afterQuickLogin') this.$emit('afterQuickLogin')
return return
@ -106,10 +109,10 @@ export default {
const router = useRouter() const router = useRouter()
if (intendedUrlCookie.value) { if (intendedUrlCookie.value) {
router.push({ path: intendedUrlCookie.value }) router.push({path: intendedUrlCookie.value})
useCookie('intended_url').value = null useCookie('intended_url').value = null
} else { } else {
router.push({ name: 'home' }) router.push({name: 'home'})
} }
} }
} }

View File

@ -103,7 +103,7 @@ export default {
if (this.loadingNewLink) return if (this.loadingNewLink) return
this.loadingNewLink = true this.loadingNewLink = true
opnFetch(this.formEndpoint.replace('{id}', this.form.id) + '/regenerate-link/' + option, {method:'PUT'}).then((data) => { opnFetch(this.formEndpoint.replace('{id}', this.form.id) + '/regenerate-link/' + option, {method:'PUT'}).then((data) => {
this.formsStore.addOrUpdate(data.form) this.formsStore.save(data.form)
this.$router.push({name: 'forms-slug-show-share', params: {slug: data.form.slug}}) this.$router.push({name: 'forms-slug-show-share', params: {slug: data.form.slug}})
useAlert().success(data.message) useAlert().success(data.message)
this.loadingNewLink = false this.loadingNewLink = false

View File

@ -1,21 +1,23 @@
<template> <template>
<NuxtLayout> <div>
<div class="flex mt-6"> <NuxtLayout>
<div class="w-full md:w-2/3 md:mx-auto md:max-w-md"> <div class="flex mt-6">
<NuxtImg alt="Nice plant as we have nothing else to show!" src="/img/icons/plant.png" class="w-56 mb-5"/> <div class="w-full md:w-2/3 md:mx-auto md:max-w-md">
<NuxtImg alt="Nice plant as we have nothing else to show!" src="/img/icons/plant.png" class="w-56 mb-5"/>
<h1 class="mb-4 font-semibold text-3xl text-gray-900"> <h1 class="mb-4 font-semibold text-3xl text-gray-900">
Page Not Found Page Not Found
</h1> </h1>
<div class="links"> <div class="links">
<NuxtLink :to="{ name: 'index' }" class="hover:underline text-gray-700"> <NuxtLink :to="{ name: 'index' }" class="hover:underline text-gray-700">
Go Home Go Home
</NuxtLink> </NuxtLink>
</div>
</div> </div>
</div> </div>
</div> </NuxtLayout>
</NuxtLayout> </div>
</template> </template>
<script setup> <script setup>

View File

@ -8,99 +8,90 @@
{{ error }} {{ error }}
</div> </div>
<div v-else class="text-center mt-4 py-6"> <div v-else class="text-center mt-4 py-6">
<Loader class="h-6 w-6 text-nt-blue mx-auto" /> <Loader class="h-6 w-6 text-nt-blue mx-auto"/>
</div> </div>
</div> </div>
</template> </template>
<script> <script setup>
import { computed } from 'vue' import {computed} from 'vue'
import Breadcrumb from '~/components/global/Breadcrumb.vue'
import FormEditor from "~/components/open/forms/components/FormEditor.vue"; import FormEditor from "~/components/open/forms/components/FormEditor.vue";
import {hash} from "~/lib/utils.js"; import {hash} from "~/lib/utils.js";
export default { const formsStore = useFormsStore()
name: 'EditForm', const workingFormStore = useWorkingFormStore()
components: { Breadcrumb, FormEditor }, const workspacesStore = useWorkspacesStore()
beforeRouteLeave (to, from, next) { if (!formsStore.allLoaded) {
if (this.isDirty()) { formsStore.startLoading()
return useAlert().confirm('Changes you made may not be saved. Are you sure want to leave?', () => { }
window.onbeforeunload = null const updatedForm = storeToRefs(workingFormStore).content
next() const form = computed(() => formsStore.getByKey(useRoute().params.slug))
}, () => {}) const formsLoading = computed(() => formsStore.loading)
}
next()
},
setup () { const error = ref(null)
const formsStore = useFormsStore() const formInitialHash = ref(null)
const workingFormStore = useWorkingFormStore()
const workspacesStore = useWorkspacesStore()
if (!formsStore.allLoaded) { function isDirty() {
formsStore.startLoading() return formInitialHash.value && updatedForm.value && formInitialHash.value !== hash(JSON.stringify(updatedForm.value.data() ?? null))
} }
const updatedForm = storeToRefs(workingFormStore).content
const form = computed(() => formsStore.getByKey(useRoute().params.slug))
// Create a form.id watcher that updates working form function initUpdatedForm() {
watch(form, (form) => { if (!form || !form.value) {
if (form) { return
updatedForm.value = useForm(form) }
}
updatedForm.value = useForm(form.value)
if (!updatedForm.value) {
return
}
formInitialHash.value = hash(JSON.stringify(updatedForm.value.data()))
if (updatedForm.value && (!updatedForm.value.notification_settings || Array.isArray(updatedForm.value.notification_settings))) {
updatedForm.value.notification_settings = {}
}
}
// Create a form.id watcher that updates working form
watch(form, (form) => {
if (form.value) {
initUpdatedForm()
}
})
onBeforeRouteLeave((to, from, next) => {
if (isDirty()) {
return useAlert().confirm('Changes you made may not be saved. Are you sure want to leave?', () => {
window.onbeforeunload = null
next()
}, () => {
}) })
}
next()
})
useOpnSeoMeta({ onBeforeMount(() => {
title: 'Edit ' + ((form && form.value) ? form.value.title : 'Your Form') if (process.client) {
})
definePageMeta({
middleware: "auth"
})
return {
formsStore,
workingFormStore,
workspacesStore,
updatedForm,
form,
formsLoading: computed(() => formsStore.loading),
}
},
data () {
return {
error: null,
formInitialHash: null
}
},
computed: {
},
async beforeMount() {
window.onbeforeunload = () => { window.onbeforeunload = () => {
if (this.isDirty()) { if (isDirty()) {
return false return false
} }
} }
if (!this.form && !this.formsStore.allLoaded) {
await this.formsStore.loadAll(this.workspacesStore.currentId)
}
this.updatedForm = useForm(this.form)
this.formInitialHash = hash(JSON.stringify(this.updatedForm.data()))
if (this.updatedForm && (!this.updatedForm.notification_settings || Array.isArray(this.updatedForm.notification_settings))) {
this.updatedForm.notification_settings = {}
}
},
methods: {
isDirty () {
return this.formInitialHash && this.formInitialHash !== hash(JSON.stringify(this.updatedForm.data()))
}
} }
}
if (!form.value && !formsStore.allLoaded) {
formsStore.loadAll(workspacesStore.currentId).then(()=>{
initUpdatedForm()
})
} else {
initUpdatedForm()
}
})
useOpnSeoMeta({
title: 'Edit ' + ((form && form.value) ? form.value.title : 'Your Form')
})
definePageMeta({
middleware: "auth"
})
</script> </script>

View File

@ -189,7 +189,7 @@ onMounted(() => {
watch(() => form?.value?.id, (id) => { watch(() => form?.value?.id, (id) => {
if (id) { if (id) {
workingFormStore.set(form) workingFormStore.set(form.value)
} }
}) })

View File

@ -160,7 +160,7 @@ const saveChanges = () => {
.map(domain => domain ? domain.trim() : null) .map(domain => domain ? domain.trim() : null)
.filter(domain => domain && domain.length > 0) .filter(domain => domain && domain.length > 0)
}).then((data) => { }).then((data) => {
workspacesStore.addOrUpdate(data) workspacesStore.save(data)
useAlert().success('Custom domains saved.') useAlert().success('Custom domains saved.')
}).catch((error) => { }).catch((error) => {
useAlert().error('Failed to update custom domains: ' + error.response.data.message) useAlert().error('Failed to update custom domains: ' + error.response.data.message)

View File

@ -54,6 +54,14 @@ export default defineNuxtPlugin({
replaysOnErrorSampleRate: config.public.SENTRY_ERROR_REPLAY_SAMPLE_RATE, replaysOnErrorSampleRate: config.public.SENTRY_ERROR_REPLAY_SAMPLE_RATE,
beforeSend(event) { beforeSend(event) {
if (event.exception.values.length) {
// Don't send validation exceptions to Sentry
if (event.exception.values[0].type === 'FetchError' &&
event.exception.values[0].value.includes('422')
) {
return null
}
}
return event; return event;
}, },
}) })