Skip to content

Commit bb0227b

Browse files
authored
Merge branch 'main' into c0656-moved-form-to-another-workpace
2 parents 356ad47 + 3aa80b2 commit bb0227b

File tree

23 files changed

+169
-44
lines changed

23 files changed

+169
-44
lines changed

.github/workflows/laravel.yml

-3
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,6 @@ jobs:
8787
# Maps tcp port 3306 on service container to the host
8888
- 3306:3306
8989

90-
concurrency:
91-
group: 'run-tests'
92-
9390
strategy:
9491
fail-fast: true
9592
matrix:

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<a href="https://github.com/JhumanJ/OpnForm/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-AGPLv3-purple" alt="License">
1616
<a href="https://github.com/JhumanJ/OpnForm/issues/new"><img src="https://img.shields.io/badge/Report a bug-Github-%231F80C0" alt="Report a bug"></a>
1717
<a href="https://github.com/JhumanJ/OpnForm/discussions/new?category=q-a"><img src="https://img.shields.io/badge/Ask a question-Github-%231F80C0" alt="Ask a question"></a>
18-
<a href="https://opnform.featurebase.app/"><img src="https://img.shields.io/badge/Feature request-Featurebase-%231F80C0" alt="Ask a question"></a>
18+
<a href="https://feedback.opnform.com"><img src="https://img.shields.io/badge/Feature request-Featurebase-%231F80C0" alt="Ask a question"></a>
1919
<a href="https://discord.gg/YTSjU2a9TS"><img src="https://dcbadge.vercel.app/api/server/YTSjU2a9TS?style=flat" alt="Ask a question"></a>
2020
<a href="https://console.algora.io/org/OpnForm/bounties?status=open"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2FOpnForm%2Fbounties%3Fstatus%3Dopen" alt="Open Bounties"></a>
2121
<a href="https://console.algora.io/org/OpnForm/bounties?status=completed"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2FOpnForm%2Fbounties%3Fstatus%3Dcompleted" alt="Rewarded Bounties"></a>

client/app.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,21 @@
2929
<ToolsStopImpersonation/>
3030

3131
<Notifications />
32+
<feature-base/>
3233
</div>
3334
</template>
3435

3536
<script>
3637
import {computed} from 'vue'
3738
import {useAppStore} from '~/stores/app'
39+
import FeatureBase from "~/components/vendor/FeatureBase.vue";
3840
3941
export default {
4042
el: '#app',
4143
4244
name: 'OpnForm',
4345
44-
components: {},
46+
components: {FeatureBase},
4547
4648
setup() {
4749
const config = useRuntimeConfig()

client/components/forms/FileInput.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ export default {
175175
}
176176
},
177177
openFileUpload () {
178-
if (this.disabled) return
178+
if (this.disabled || !this.$refs['actual-input']) return
179179
this.$refs['actual-input'].click()
180180
},
181181
manualFileUpload (e) {

client/components/forms/components/InputHelp.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<div class="flex mb-1 input-help">
33
<small :class="theme.default.help" class="grow flex">
4-
<slot name="help"><span class="field-help" v-html="help" /></slot>
4+
<slot name="help"><span v-if="help" class="field-help" v-html="help" /></slot>
55
</small>
66
<slot name="after-help">
77
<small class="flex-grow" />

client/components/forms/components/VSwitch.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ const props = defineProps({
1313
modelValue: { type: Boolean, default: false },
1414
disabled: { type: Boolean, default: false }
1515
})
16-
const emits = defineEmits(['update:modelValue'])
16+
const emit = defineEmits(['update:modelValue'])
1717
1818
const onClick = () => {
1919
if (props.disabled) return
20-
emits('update:modelValue', !props.modelValue)
20+
emit('update:modelValue', !props.modelValue)
2121
}
2222
</script>

client/components/global/Modal.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const props = defineProps({
7171
}
7272
})
7373
74-
const emits = defineEmits(['close'])
74+
const emit = defineEmits(['close'])
7575
7676
useHead({
7777
bodyAttrs: {
@@ -152,7 +152,7 @@ const onLeave = (el, done) => {
152152
153153
const close = () => {
154154
if (props.closeable) {
155-
emits('close')
155+
emit('close')
156156
}
157157
}
158158

client/components/global/Navbar.vue

+24-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,19 @@
1616
>
1717
Templates
1818
</NuxtLink>
19-
<NuxtLink v-if="$route.name !== 'ai-form-builder'" :to="{name:'ai-form-builder'}"
19+
<template v-if="featureBaseEnabled">
20+
<button v-if="user" @click.prevent="openChangelog"
21+
class="text-sm text-gray-600 dark:text-white hidden sm:inline hover:text-gray-800 cursor-pointer mt-1 mr-8"
22+
>
23+
What's new? <span id="fb-update-badge"></span>
24+
</button>
25+
<a :href="opnformConfig.links.changelog_url" target="_blank" v-else
26+
class="text-sm text-gray-600 dark:text-white hidden lg:inline hover:text-gray-800 cursor-pointer mt-1 mr-8"
27+
>
28+
What's new?
29+
</a>
30+
</template>
31+
<NuxtLink v-if="$route.name !== 'ai-form-builder' && user === null" :to="{name:'ai-form-builder'}"
2032
class="text-sm text-gray-600 dark:text-white hidden lg:inline hover:text-gray-800 cursor-pointer mt-1 mr-8"
2133
>
2234
AI Form Builder
@@ -103,8 +115,10 @@
103115
<NuxtLink :to="{ name: 'settings-admin' }" v-if="user.moderator"
104116
class="block block px-4 py-2 text-md text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600 flex items-center"
105117
>
106-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 mr-2">
107-
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75m-3-7.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285Z" />
118+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
119+
stroke="currentColor" class="w-4 h-4 mr-2">
120+
<path stroke-linecap="round" stroke-linejoin="round"
121+
d="M9 12.75 11.25 15 15 9.75m-3-7.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285Z"/>
108122
</svg>
109123
Admin
110124
</NuxtLink>
@@ -190,6 +204,9 @@ export default {
190204
paidPlansEnabled() {
191205
return this.config.public.paidPlansEnabled
192206
},
207+
featureBaseEnabled() {
208+
return this.config.public.featureBaseOrganization !== null
209+
},
193210
showAuth() {
194211
return this.$route.name && this.$route.name !== 'forms-slug'
195212
},
@@ -217,6 +234,10 @@ export default {
217234
},
218235
219236
methods: {
237+
openChangelog() {
238+
if (process.server) return
239+
window.Featurebase('manually_open_changelog_popup')
240+
},
220241
async logout() {
221242
// Log out the user.
222243
this.authStore.logout()

client/components/global/transitions/Collapsible.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const props = defineProps({
1717
modelValue: {type: Boolean},
1818
maxHeight: {type: Number, default: 200},
1919
})
20-
const emits = defineEmits(['click-away'])
20+
const emit = defineEmits(['click-away'])
2121
2222
const motion = ref(null)
2323
const collapsible = ref(null)
@@ -47,6 +47,6 @@ const onLeave = (el, done) => {
4747
}
4848
4949
const onClickAway = (event) => {
50-
emits('click-away', event)
50+
emit('click-away', event)
5151
}
5252
</script>

client/components/open/forms/components/form-components/FormNotifications.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export default {
5454
5555
computed: {
5656
zapierUrl () {
57-
opnformConfig.links.zapier_integration
57+
return opnformConfig.links.zapier_integration
5858
}
5959
},
6060
}

client/components/open/forms/components/form-logic-components/FormBlockLogicEditor.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
</h5>
3737
<select-input :key="resetKey" v-model="logic.actions" name="actions"
3838
:multiple="true" class="mt-1" placeholder="Actions..."
39-
help="Action(s) triggerred when above conditions are true"
39+
help="Action(s) triggered when above conditions are true"
4040
:options="actionOptions"
4141
@update:model-value="onActionInput"
4242
/>
+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<script setup>
2+
import {onMounted} from "vue";
3+
4+
const scriptLoaded = ref(false);
5+
const user = computed(() => useAuthStore().user);
6+
const featureBaseOrganization = useRuntimeConfig().public.featureBaseOrganization;
7+
8+
const loadScript = () => {
9+
if (scriptLoaded.value || !user.value || !featureBaseOrganization) return;
10+
const script = document.createElement("script");
11+
script.src = "https://do.featurebase.app/js/sdk.js";
12+
script.id = "featurebase-sdk";
13+
document.head.appendChild(script);
14+
scriptLoaded.value = true;
15+
};
16+
17+
const setupForUser = () => {
18+
if (process.server || !user.value || !featureBaseOrganization) return
19+
window.Featurebase(
20+
"identify",
21+
{
22+
organization: featureBaseOrganization,
23+
email: user.value.email,
24+
name: user.value.name,
25+
id: user.value.id.toString(),
26+
profilePicture: user.value.photo_url
27+
}
28+
);
29+
30+
window.Featurebase("initialize_changelog_widget", {
31+
organization: featureBaseOrganization,
32+
placement: "right",
33+
theme: "light",
34+
alwaysShow: true,
35+
fullscreenPopup: true,
36+
usersName: user.value?.name
37+
})
38+
39+
window.Featurebase("initialize_feedback_widget", {
40+
organization: featureBaseOrganization,
41+
theme: "light",
42+
placement: "right",
43+
email: user.value?.email,
44+
usersName: user.value?.name
45+
});
46+
}
47+
48+
onMounted(() => {
49+
if (process.server) return
50+
51+
// Setup base
52+
if (!window.hasOwnProperty('Featurebase') || typeof window.Featurebase !== "function") {
53+
window.Featurebase = function () {
54+
(window.Featurebase.q = window.Featurebase.q || []).push(arguments);
55+
};
56+
}
57+
58+
if (!user.value) return
59+
loadScript()
60+
setupForUser()
61+
})
62+
63+
watch(user, (val) => {
64+
if (process.server || !val) return
65+
66+
loadScript()
67+
setupForUser()
68+
});
69+
70+
</script>
71+
<template></template>

client/opnform.config.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ export default {
88
"links": {
99
"help_url": "https://github.com/JhumanJ/OpnForm/discussions",
1010
"helpdesk_sitemap_url": "https://notionforms.crisp.help/sitemap.xml",
11-
"changelog_url": "https://opnform.canny.io/changelog",
1211
"github_url": "https://github.com/JhumanJ/OpnForm",
1312
"github_forum_url": "https://github.com/JhumanJ/OpnForm/discussions",
1413
'discord': 'https://discord.gg/YTSjU2a9TS',
1514
"twitter": "https://twitter.com/OpnForm",
1615
"zapier_integration": "https://zapier.com/developer/public-invite/146950/58db583730cc46b821614468d94c35de/",
1716
"book_onboarding": "https://zcal.co/i/YQVGEULQ",
18-
"feature_requests": "https://opnform.featurebase.app/",
19-
"roadmap": "https://opnform.featurebase.app/roadmap",
17+
"feature_requests": "https://feedback.opnform.com/",
18+
"changelog_url": "https://feedback.opnform.com/changelog",
19+
"roadmap": "https://feedback.opnform.com/roadmap",
2020
}
2121
}

client/pages/forms/[slug]/edit.vue

+5-3
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ const error = ref(null)
3333
const formInitialHash = ref(null)
3434
3535
function isDirty() {
36-
return formInitialHash.value &&
37-
updatedForm.value &&
38-
formInitialHash.value !== hash(JSON.stringify(updatedForm?.value?.data() ?? null))
36+
try {
37+
return formInitialHash.value && updatedForm.value && formInitialHash.value !== hash(JSON.stringify(updatedForm?.value?.data() ?? null))
38+
} catch (e) {
39+
return false
40+
}
3941
}
4042
4143
function initUpdatedForm() {

client/pages/forms/[slug]/index.vue

+20-8
Original file line numberDiff line numberDiff line change
@@ -116,41 +116,53 @@ await loadForm(true)
116116
117117
onMounted(() => {
118118
crisp.hideChat()
119+
document.body.classList.add('public-page')
119120
if (form.value) {
120121
handleDarkMode(form.value?.dark_mode)
121122
handleTransparentMode(form.value?.transparent_background)
122123
123124
if (process.client) {
124125
if (form.value.custom_code) {
125126
const scriptEl = document.createRange().createContextualFragment(form.value.custom_code)
126-
document.head.append(scriptEl)
127+
try {
128+
document.head.append(scriptEl)
129+
} catch (e) {
130+
console.error('Error appending custom code', e)
131+
}
127132
}
128133
if (!isIframe) focusOnFirstFormElement()
129134
}
130135
}
131136
})
132137
133138
onBeforeRouteLeave((to, from) => {
139+
document.body.classList.remove('public-page')
134140
crisp.showChat()
135141
disableDarkMode()
136142
})
137143
144+
const pageMeta = computed(() => {
145+
if (form.value && form.value.is_pro && form.value.seo_meta) {
146+
return form.value.seo_meta
147+
}
148+
return {}
149+
})
138150
useOpnSeoMeta({
139151
title: () => {
140-
if (form && form.value?.is_pro && form.value.seo_meta.page_title) {
141-
return form.value.seo_meta.page_title
152+
if (pageMeta.value.page_title) {
153+
return pageMeta.value.page_title
142154
}
143155
return form.value ? form.value.title : 'Create beautiful forms'
144156
},
145157
description: () => {
146-
if (form && form.value?.is_pro && form.value.seo_meta.page_description) {
147-
return form.value.seo_meta.page_description
158+
if (pageMeta.value.description) {
159+
return pageMeta.value.description
148160
}
149161
return (form && form.value?.description) ? form.value?.description.substring(0, 160) : null
150162
},
151163
ogImage: () => {
152-
if (form && form.value?.is_pro && form.value.seo_meta.page_thumbnail) {
153-
return form.value.seo_meta.page_thumbnail
164+
if (pageMeta.value.page_thumbnail) {
165+
return pageMeta.value.page_thumbnail
154166
}
155167
return (form && form.value?.cover_picture) ? form.value?.cover_picture : null
156168
},
@@ -160,7 +172,7 @@ useOpnSeoMeta({
160172
})
161173
useHead({
162174
titleTemplate: (titleChunk) => {
163-
if (form && form.value?.is_pro && form.value?.seo_meta.page_title) {
175+
if (pageMeta.value.page_title) {
164176
// Disable template if custom SEO title
165177
return titleChunk
166178
}

client/pages/home.vue

+8-4
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,21 @@
6969
<NuxtLink :to="{name:'forms-slug-show-submissions', params: {slug:form.slug}}"
7070
class="absolute inset-0"/>
7171
<span class="font-semibold text-gray-900 dark:text-white">{{ form.title }}</span>
72-
<ul class="flex text-gray-500">
73-
<li class="pr-1">
72+
<ul class="flex text-gray-500 text-sm gap-4">
73+
<li class="pr-1 mr-3">
7474
{{ form.views_count }} view{{ form.views_count > 0 ? 's' : '' }}
7575
</li>
76-
<li class="list-disc ml-6 pr-1">
76+
<li class="list-disc mr-3">
7777
{{ form.submissions_count }}
7878
submission{{ form.submissions_count > 0 ? 's' : '' }}
7979
</li>
80-
<li class="list-disc ml-6">
80+
<li class="list-disc mr-3">
8181
Edited {{ form.last_edited_human }}
8282
</li>
83+
<li class='list-disc hidden lg:list-item' v-if="form.creator">
84+
By
85+
{{ form?.creator?.name }}
86+
</li>
8387
</ul>
8488
<div v-if="['draft','closed'].includes(form.visibility) || (form.tags && form.tags.length > 0)"
8589
class="mt-1 flex items-center flex-wrap gap-3">

client/pages/settings/password.vue

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const update = () => {
4141
form.patch('/settings/password').then((response) => {
4242
form.reset()
4343
useAlert().success('Password updated.')
44+
}).catch((error) => {
45+
console.error(error)
4446
})
4547
}
4648
</script>

client/plugins/sentry.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default defineNuxtPlugin({
4646
if (event.exception.values.length) {
4747
// Don't send validation exceptions to Sentry
4848
if (event.exception.values[0].type === 'FetchError' &&
49-
event.exception.values[0].value.includes('422')
49+
(event.exception.values[0].value.includes('422') || event.exception.values[0].value.includes('401'))
5050
) {
5151
return null
5252
}

0 commit comments

Comments
 (0)