Vue3: migrating from vuex to Pinia (#249)

* Vue3: migrating from vuex to Pinia

* toggle input fixes

* update configureCompat

---------

Co-authored-by: Forms Dev <chirag+new@notionforms.io>
This commit is contained in:
Chirag Chhatrala 2023-12-01 23:27:14 +05:30 committed by GitHub
parent af30067eda
commit 47653ebe64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
105 changed files with 2092 additions and 1577 deletions

896
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@
"js-cookie": "^2.2.1", "js-cookie": "^2.2.1",
"js-sha256": "^0.9.0", "js-sha256": "^0.9.0",
"libphonenumber-js": "^1.10.44", "libphonenumber-js": "^1.10.44",
"pinia": "^2.1.7",
"prismjs": "^1.24.1", "prismjs": "^1.24.1",
"qrcode": "^1.5.1", "qrcode": "^1.5.1",
"query-builder-vue": "^1.2.0", "query-builder-vue": "^1.2.0",
@ -38,8 +39,7 @@
"vue-signature-pad": "^3.0.2", "vue-signature-pad": "^3.0.2",
"vue3-editor": "^0.1.1", "vue3-editor": "^0.1.1",
"vue3-vt-notifications": "^1.0.0", "vue3-vt-notifications": "^1.0.0",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0"
"vuex": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.12", "@babel/core": "^7.20.12",

9
resources/js/app.js vendored
View File

@ -1,5 +1,5 @@
import { createApp, configureCompat, ref } from 'vue' import { createApp, configureCompat, ref } from 'vue'
import store from '~/store' import { createPinia } from 'pinia'
import router from '~/router' import router from '~/router'
import App from '~/components/App.vue' import App from '~/components/App.vue'
import Base from './base.js' import Base from './base.js'
@ -11,9 +11,10 @@ import '~/components'
import '../sass/app.scss' import '../sass/app.scss'
const pinia = createPinia()
const app = createApp(App) const app = createApp(App)
.use(router) .use(router)
.use(store) .use(pinia)
.mixin(Base) .mixin(Base)
registerPlugin(app) registerPlugin(app)
@ -25,7 +26,9 @@ configureCompat({
// GLOBAL_MOUNT: false, // GLOBAL_MOUNT: false,
COMPONENT_V_MODEL: false, COMPONENT_V_MODEL: false,
INSTANCE_SET: false, INSTANCE_SET: false,
INSTANCE_DELETE: false INSTANCE_DELETE: false,
ATTR_FALSE_VALUE: false,
WATCH_ARRAY: false
}) })
router.app = app router.app = app

View File

@ -37,6 +37,8 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import { useAppStore } from '../stores/app'
import Loading from './Loading.vue' import Loading from './Loading.vue'
import Hotjar from './service/Hotjar.vue' import Hotjar from './service/Hotjar.vue'
import Amplitude from './service/Amplitude.vue' import Amplitude from './service/Amplitude.vue'
@ -44,7 +46,6 @@ import Crisp from './service/Crisp.vue'
import StopImpersonation from './pages/StopImpersonation.vue' import StopImpersonation from './pages/StopImpersonation.vue'
import Notifications from './common/Notifications.vue' import Notifications from './common/Notifications.vue'
import SeoMeta from '../mixins/seo-meta.js' import SeoMeta from '../mixins/seo-meta.js'
import { mapState } from 'vuex'
// Load layout components dynamically. // Load layout components dynamically.
const requireContext = import.meta.glob('../layouts/**.vue', { eager: true }) const requireContext = import.meta.glob('../layouts/**.vue', { eager: true })
@ -74,6 +75,13 @@ export default {
mixins: [SeoMeta], mixins: [SeoMeta],
setup () {
const appStore = useAppStore()
return {
layout : computed(() => appStore.layout)
}
},
data: () => ({ data: () => ({
metaTitle: 'OpnForm', metaTitle: 'OpnForm',
metaDescription: 'Create beautiful forms for free. Unlimited fields, unlimited submissions. It\'s free and it takes less than 1 minute to create your first form.', metaDescription: 'Create beautiful forms for free. Unlimited fields, unlimited submissions. It\'s free and it takes less than 1 minute to create your first form.',
@ -95,9 +103,6 @@ export default {
isOnboardingPage () { isOnboardingPage () {
return this.$route.name === 'onboarding' return this.$route.name === 'onboarding'
}, },
...mapState({
layout: state => state.app.layout
}),
layoutComponent () { layoutComponent () {
return layouts[this.layout] return layouts[this.layout]
} }

View File

@ -10,7 +10,8 @@
<script> <script>
// https://github.com/nuxt/nuxt.js/blob/master/lib/app/components/nuxt-loading.vue // https://github.com/nuxt/nuxt.js/blob/master/lib/app/components/nuxt-loading.vue
import { mapState } from 'vuex' import { computed } from 'vue'
import { useAppStore } from '../stores/app';
export default { export default {
data: () => ({ data: () => ({
@ -19,12 +20,13 @@ export default {
failedColor: 'red' failedColor: 'red'
}), }),
computed: { setup () {
...mapState({ const appStore = useAppStore()
percent: state => state.app.loader.percent, return {
canSuccess: state => state.app.loader.canSuccess, percent : computed(() => appStore.loader.percent),
show: state => state.app.loader.show canSuccess : computed(() => appStore.loader.canSuccess),
}) show : computed(() => appStore.loader.show)
}
} }
} }
</script> </script>

View File

@ -14,9 +14,18 @@
</template> </template>
<script> <script>
import { useAuthStore } from '../stores/auth'
export default { export default {
name: 'LoginWithGithub', name: 'LoginWithGithub',
setup () {
const authStore = useAuthStore()
return {
authStore
}
},
computed: { computed: {
githubAuth: () => window.config.githubAuth, githubAuth: () => window.config.githubAuth,
url: () => '/api/oauth/github' url: () => '/api/oauth/github'
@ -34,9 +43,7 @@ export default {
async login () { async login () {
const newWindow = openWindow('', 'Login') const newWindow = openWindow('', 'Login')
const url = await this.$store.dispatch('auth/fetchOauthUrl', { const url = await this.authStore.fetchOauthUrl('github')
provider: 'github'
})
newWindow.location.href = url newWindow.location.href = url
}, },
@ -49,9 +56,7 @@ export default {
return return
} }
this.$store.dispatch('auth/saveToken', { this.authStore.saveToken(e.data.token)
token: e.data.token
})
this.$router.push({ name: 'home' }) this.$router.push({ name: 'home' })
} }

View File

@ -132,7 +132,10 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../stores/auth';
import { useFormsStore } from '../stores/forms';
import { useWorkspacesStore } from '../stores/workspaces';
import Dropdown from './common/Dropdown.vue' import Dropdown from './common/Dropdown.vue'
import WorkspaceDropdown from './WorkspaceDropdown.vue' import WorkspaceDropdown from './WorkspaceDropdown.vue'
@ -142,6 +145,18 @@ export default {
Dropdown Dropdown
}, },
setup () {
const authStore = useAuthStore()
const formsStore = useFormsStore()
const workspacesStore = useWorkspacesStore()
return {
authStore,
formsStore,
workspacesStore,
user : computed(() => authStore.user)
}
},
data: () => ({ data: () => ({
appName: window.config.appName appName: window.config.appName
}), }),
@ -151,12 +166,12 @@ export default {
helpUrl: () => window.config.links.help_url, helpUrl: () => window.config.links.help_url,
form () { form () {
if (this.$route.name && this.$route.name.startsWith('forms.show_public')) { if (this.$route.name && this.$route.name.startsWith('forms.show_public')) {
return this.$store.getters['open/forms/getBySlug'](this.$route.params.slug) return this.formsStore.getBySlug(this.$route.params.slug)
} }
return null return null
}, },
workspace () { workspace () {
return this.$store.getters['open/workspaces/getCurrent']() return this.workspacesStore.getCurrent()
}, },
paidPlansEnabled () { paidPlansEnabled () {
return window.config.paid_plans_enabled return window.config.paid_plans_enabled
@ -182,9 +197,6 @@ export default {
isIframe () { isIframe () {
return window.location !== window.parent.location || window.frameElement return window.location !== window.parent.location || window.frameElement
}, },
...mapGetters({
user: 'auth/user'
}),
userOnboarded () { userOnboarded () {
return this.user && this.user.workspaces_count > 0 return this.user && this.user.workspaces_count > 0
}, },
@ -196,11 +208,11 @@ export default {
methods: { methods: {
async logout () { async logout () {
// Log out the user. // Log out the user.
await this.$store.dispatch('auth/logout') await this.authStore.logout()
// Reset store // Reset store
this.$store.dispatch('open/workspaces/resetState') this.workspacesStore.resetState()
this.$store.dispatch('open/forms/resetState') this.formsStore.resetState()
// Redirect to login. // Redirect to login.
this.$router.push({ name: 'login' }) this.$router.push({ name: 'login' })

View File

@ -41,8 +41,11 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import { useAuthStore } from '../stores/auth'
import { useFormsStore } from '../stores/forms'
import { useWorkspacesStore } from '../stores/workspaces'
import Dropdown from './common/Dropdown.vue' import Dropdown from './common/Dropdown.vue'
import { mapGetters, mapState } from 'vuex'
export default { export default {
@ -51,20 +54,26 @@ export default {
Dropdown Dropdown
}, },
setup () {
const authStore = useAuthStore()
const formsStore = useFormsStore()
const workspacesStore = useWorkspacesStore()
return {
formsStore,
workspacesStore,
user : computed(() => authStore.user),
workspaces : computed(() => workspacesStore.content),
loading : computed(() => workspacesStore.loading)
}
},
data: () => ({ data: () => ({
appName: window.config.appName appName: window.config.appName
}), }),
computed: { computed: {
...mapState({
workspaces: state => state['open/workspaces'].content,
loading: state => state['open/workspaces'].loading
}),
...mapGetters({
user: 'auth/user'
}),
workspace () { workspace () {
return this.$store.getters['open/workspaces/getCurrent']() return this.workspacesStore.getCurrent()
} }
}, },
@ -76,12 +85,12 @@ export default {
methods: { methods: {
switchWorkspace (workspace) { switchWorkspace (workspace) {
this.$store.commit('open/workspaces/setCurrentId', workspace.id) this.workspacesStore.setCurrentId(workspace.id)
this.$refs.dropdown.close() this.$refs.dropdown.close()
if (this.$route.name !== 'home') { if (this.$route.name !== 'home') {
this.$router.push({ name: 'home' }) this.$router.push({ name: 'home' })
} }
this.$store.dispatch('open/forms/load', workspace.id) this.formsStore.load(workspace.id)
}, },
isUrl (str) { isUrl (str) {
try { try {

View File

@ -54,7 +54,8 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth';
export default { export default {
name: 'Breadcrumb', name: 'Breadcrumb',
@ -66,20 +67,22 @@ export default {
path: { type: Array } path: { type: Array }
}, },
setup () {
const authStore = useAuthStore()
return {
authenticated : computed(() => authStore.check)
}
},
data () { data () {
return { return {
displayHome: true displayHome: true
} }
}, },
computed: { computed: {},
...mapGetters({
authenticated: 'auth/check'
})
},
mounted () { mounted () {},
},
methods: {} methods: {}
} }

View File

@ -37,8 +37,10 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import Modal from '../Modal.vue' import Modal from '../Modal.vue'
import {mapGetters} from 'vuex' import { useAuthStore } from '../../stores/auth';
import { useWorkspacesStore } from '../../stores/workspaces';
import PricingTable from "../pages/pricing/PricingTable.vue"; import PricingTable from "../pages/pricing/PricingTable.vue";
export default { export default {
@ -46,6 +48,15 @@ export default {
components: {PricingTable, Modal}, components: {PricingTable, Modal},
props: {}, props: {},
setup () {
const authStore = useAuthStore()
const workspacesStore = useWorkspacesStore()
return {
user : computed(() => authStore.user),
currentWorkSpace : computed(() => workspacesStore.getCurrent())
}
},
data() { data() {
return { return {
showPremiumModal: false, showPremiumModal: false,
@ -54,10 +65,6 @@ export default {
}, },
computed: { computed: {
...mapGetters({
user: 'auth/user',
currentWorkSpace: 'open/workspaces/getCurrent',
}),
shouldDisplayProTag() { shouldDisplayProTag() {
if (!window.config.paid_plans_enabled) return false if (!window.config.paid_plans_enabled) return false
if (!this.user || !this.currentWorkSpace) return true if (!this.user || !this.currentWorkSpace) return true

View File

@ -11,7 +11,7 @@
:data="countries" :data="countries"
:disabled="disabled || countries.length===1" :searchable="true" :search-keys="['name']" :option-key="'code'" :color="color" :disabled="disabled || countries.length===1" :searchable="true" :search-keys="['name']" :option-key="'code'" :color="color"
:has-error="hasValidation && form.errors.has(name)" :has-error="hasValidation && form.errors.has(name)"
:placeholder="'Select a country'" :uppercase-labels="true" :theme="theme" @input="onChangeCountryCode" :placeholder="'Select a country'" :uppercase-labels="true" :theme="theme" @update:model-value="onChangeCountryCode"
> >
<template #option="props"> <template #option="props">
<div class="flex items-center space-x-2 hover:text-white"> <div class="flex items-center space-x-2 hover:text-white">
@ -29,7 +29,7 @@
</v-select> </v-select>
<input v-model="inputVal" type="text" class="inline-flex-grow !border-l-0 !rounded-l-none" :disabled="disabled" <input v-model="inputVal" type="text" class="inline-flex-grow !border-l-0 !rounded-l-none" :disabled="disabled"
:class="[theme.default.input, { '!ring-red-500 !ring-2': hasValidation && form.errors.has(name), '!cursor-not-allowed !bg-gray-200': disabled }]" :class="[theme.default.input, { '!ring-red-500 !ring-2': hasValidation && form.errors.has(name), '!cursor-not-allowed !bg-gray-200': disabled }]"
:placeholder="placeholder" :style="inputStyle" @input="onInput" :placeholder="placeholder" :style="inputStyle" @update:model-value="onInput"
> >
</div> </div>

View File

@ -22,7 +22,7 @@
</input-help> </input-help>
</slot> </slot>
<slot name="error"> <slot name="error">
<has-error v-if="hasValidation" :form="form" :field="name" /> <has-error v-if="hasValidation && form" :form="form" :field="name" />
</slot> </slot>
</div> </div>
</template> </template>

View File

@ -16,8 +16,6 @@
</template> </template>
<script> <script>
import { mapState } from 'vuex'
import store from '~/store'
import axios from 'axios' import axios from 'axios'
export default { export default {

View File

@ -62,6 +62,9 @@
<script> <script>
import axios from 'axios' import axios from 'axios'
import Form from 'vform' import Form from 'vform'
import { computed } from 'vue'
import { useRecordsStore } from '../../../stores/records'
import { useWorkingFormStore } from '../../../stores/working_form'
import OpenFormButton from './OpenFormButton.vue' import OpenFormButton from './OpenFormButton.vue'
import clonedeep from 'clone-deep' import clonedeep from 'clone-deep'
import FormLogicPropertyResolver from '../../../forms/FormLogicPropertyResolver.js' import FormLogicPropertyResolver from '../../../forms/FormLogicPropertyResolver.js'
@ -100,6 +103,16 @@ export default {
}, },
adminPreview: { type: Boolean, default: false } // If used in FormEditorPreview adminPreview: { type: Boolean, default: false } // If used in FormEditorPreview
}, },
setup () {
const recordsStore = useRecordsStore()
const workingFormStore = useWorkingFormStore()
return {
recordsStore,
workingFormStore
}
},
data () { data () {
return { return {
dataForm: null, dataForm: null,
@ -154,8 +167,8 @@ export default {
newFields.push(...group) newFields.push(...group)
} }
}) })
// set the properties on working_form vuex // set the properties on working_form store
this.$store.commit('open/working_form/setProperties', newFields) this.workingFormStore.setProperties(newFields)
} }
}, },
/** /**
@ -293,12 +306,12 @@ export default {
if (!this.form || !this.form.editable_submissions || !this.form.submission_id) { if (!this.form || !this.form.editable_submissions || !this.form.submission_id) {
return null return null
} }
await this.$store.dispatch('open/records/loadRecord', await this.recordsStore.loadRecord(
axios.get('/api/forms/' + this.form.slug + '/submissions/' + this.form.submission_id).then((response) => { axios.get('/api/forms/' + this.form.slug + '/submissions/' + this.form.submission_id).then((response) => {
return { submission_id: this.form.submission_id, ...response.data.data } return { submission_id: this.form.submission_id, ...response.data.data }
}) })
) )
return this.$store.getters['open/records/getById'](this.form.submission_id) return this.recordsStore.getById(this.form.submission_id)
}, },
async initForm () { async initForm () {
if (this.isPublicFormPage && this.form.editable_submissions) { if (this.isPublicFormPage && this.form.editable_submissions) {

View File

@ -78,9 +78,10 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import { useWorkingFormStore } from '../../../stores/working_form';
import FormLogicPropertyResolver from '../../../forms/FormLogicPropertyResolver.js' import FormLogicPropertyResolver from '../../../forms/FormLogicPropertyResolver.js'
import FormPendingSubmissionKey from '../../../mixins/forms/form-pending-submission-key.js' import FormPendingSubmissionKey from '../../../mixins/forms/form-pending-submission-key.js'
import {mapState} from "vuex";
export default { export default {
name: 'OpenFormField', name: 'OpenFormField',
@ -113,15 +114,21 @@ export default {
}, },
adminPreview: {type: Boolean, default: false} // If used in FormEditorPreview adminPreview: {type: Boolean, default: false} // If used in FormEditorPreview
}, },
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore,
selectedFieldIndex : computed(() => workingFormStore.selectedFieldIndex),
showEditFieldSidebar : computed(() => workingFormStore.showEditFieldSidebar)
}
},
data() { data() {
return {} return {}
}, },
computed: { computed: {
...mapState({
selectedFieldIndex: state => state['open/working_form'].selectedFieldIndex,
showEditFieldSidebar: state => state['open/working_form'].showEditFieldSidebar
}),
fieldComponents() { fieldComponents() {
return { return {
text: 'TextInput', text: 'TextInput',
@ -214,10 +221,10 @@ export default {
methods: { methods: {
editFieldOptions() { editFieldOptions() {
this.$store.commit('open/working_form/openSettingsForField', this.field) this.workingFormStore.openSettingsForField(this.field)
}, },
openAddFieldSidebar() { openAddFieldSidebar() {
this.$store.commit('open/working_form/openAddFieldSidebar', this.field) this.workingFormStore.openAddFieldSidebar(this.field)
}, },
/** /**
* Get the right input component for the field/options combination * Get the right input component for the field/options combination

View File

@ -13,12 +13,12 @@
label="Hide Form Title" label="Hide Form Title"
:disabled="form.hide_title===true" :disabled="form.hide_title===true"
:help="hideTitleHelp" :help="hideTitleHelp"
@input="onChangeHideTitle" @update:model-value="onChangeHideTitle"
/> />
<toggle-switch-input :value="value.auto_submit" name="auto_submit" class="mt-4" <toggle-switch-input :value="value.auto_submit" name="auto_submit" class="mt-4"
label="Auto Submit Form" label="Auto Submit Form"
help="Form will auto submit immediate after open URL" help="Form will auto submit immediate after open URL"
@input="onChangeAutoSubmit" @update:model-value="onChangeAutoSubmit"
/> />
</collapse> </collapse>
</template> </template>

View File

@ -85,7 +85,11 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../../../stores/auth';
import { useFormsStore } from '../../../../stores/forms';
import { useWorkingFormStore } from '../../../../stores/working_form';
import { useWorkspacesStore } from '../../../../stores/workspaces';
import AddFormBlockSidebar from './form-components/AddFormBlockSidebar.vue' import AddFormBlockSidebar from './form-components/AddFormBlockSidebar.vue'
import FormFieldEditSidebar from '../fields/FormFieldEditSidebar.vue' import FormFieldEditSidebar from '../fields/FormFieldEditSidebar.vue'
import FormErrorModal from './form-components/FormErrorModal.vue' import FormErrorModal from './form-components/FormErrorModal.vue'
@ -143,6 +147,19 @@ export default {
} }
}, },
setup () {
const authStore = useAuthStore()
const formsStore = useFormsStore()
const workingFormStore = useWorkingFormStore()
const workspacesStore = useWorkspacesStore()
return {
formsStore,
workingFormStore,
workspacesStore,
user : computed(() => authStore.user)
}
},
data () { data () {
return { return {
showFormErrorModal: false, showFormErrorModal: false,
@ -153,23 +170,20 @@ export default {
}, },
computed: { computed: {
...mapGetters({
user: 'auth/user'
}),
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
createdForm () { createdForm () {
return this.$store.getters['open/forms/getById'](this.createdFormId) return this.formsStore.getById(this.createdFormId)
}, },
workspace () { workspace () {
return this.$store.getters['open/workspaces/getCurrent']() return this.workspacesStore.getCurrent()
}, },
steps () { steps () {
return [ return [
@ -245,7 +259,7 @@ export default {
this.validationErrorResponse = null this.validationErrorResponse = null
this.form.put('/api/open/forms/{id}/'.replace('{id}', this.form.id)).then((response) => { this.form.put('/api/open/forms/{id}/'.replace('{id}', this.form.id)).then((response) => {
const data = response.data const data = response.data
this.$store.commit('open/forms/addOrUpdate', data.form) this.formsStore.addOrUpdate(data.form)
this.$emit('on-save') this.$emit('on-save')
this.$router.push({ name: 'forms.show', params: { slug: this.form.slug } }) this.$router.push({ name: 'forms.show', params: { slug: this.form.slug } })
this.$logEvent('form_saved', { form_id: this.form.id, form_slug: this.form.slug }) this.$logEvent('form_saved', { form_id: this.form.id, form_slug: this.form.slug })
@ -266,7 +280,7 @@ export default {
this.updateFormLoading = true this.updateFormLoading = true
this.form.post('/api/open/forms').then((response) => { this.form.post('/api/open/forms').then((response) => {
this.$store.commit('open/forms/addOrUpdate', response.data.form) this.formsStore.addOrUpdate(response.data.form)
this.$emit('on-save') this.$emit('on-save')
this.createdFormId = response.data.form.id this.createdFormId = response.data.form.id

View File

@ -166,6 +166,8 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import { useWorkingFormStore } from '../../../../stores/working_form'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import ProTag from '../../../common/ProTag.vue' import ProTag from '../../../common/ProTag.vue'
import clonedeep from 'clone-deep' import clonedeep from 'clone-deep'
@ -182,6 +184,13 @@ export default {
EditableDiv EditableDiv
}, },
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
formFields: [], formFields: [],
@ -192,11 +201,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
} }
}, },
@ -313,7 +322,7 @@ export default {
return type return type
}, },
editOptions (index) { editOptions (index) {
this.$store.commit('open/working_form/openSettingsForField', index) this.workingFormStore.openSettingsForField(index)
}, },
removeBlock (blockIndex) { removeBlock (blockIndex) {
const newFields = clonedeep(this.formFields) const newFields = clonedeep(this.formFields)
@ -322,10 +331,10 @@ export default {
this.closeSidebar() this.closeSidebar()
}, },
closeSidebar () { closeSidebar () {
this.$store.commit('open/working_form/closeEditFieldSidebar') this.workingFormStore.closeEditFieldSidebar()
}, },
openAddFieldSidebar () { openAddFieldSidebar () {
this.$store.commit('open/working_form/openAddFieldSidebar', null) this.workingFormStore.openAddFieldSidebar(null)
} }
} }
} }

View File

@ -25,7 +25,7 @@
</h4> </h4>
<div v-for="field in properties" :key="field.id" class="p-2 border"> <div v-for="field in properties" :key="field.id" class="p-2 border">
{{ field.name }} {{ field.name }}
<v-switch v-model="displayColumns[field.id]" class="float-right" @input="onChangeDisplayColumns" /> <v-switch v-model="displayColumns[field.id]" class="float-right" @update:model-value="onChangeDisplayColumns" />
</div> </div>
</template> </template>
<template v-if="removed_properties.length > 0"> <template v-if="removed_properties.length > 0">
@ -34,7 +34,7 @@
</h4> </h4>
<div v-for="field in removed_properties" :key="field.id" class="p-2 border"> <div v-for="field in removed_properties" :key="field.id" class="p-2 border">
{{ field.name }} {{ field.name }}
<v-switch v-model="displayColumns[field.id]" class="float-right" @input="onChangeDisplayColumns" /> <v-switch v-model="displayColumns[field.id]" class="float-right" @update:model-value="onChangeDisplayColumns" />
</div> </div>
</template> </template>
</div> </div>
@ -83,6 +83,7 @@
import axios from 'axios' import axios from 'axios'
import Fuse from 'fuse.js' import Fuse from 'fuse.js'
import Form from 'vform' import Form from 'vform'
import { useWorkingFormStore } from '../../../../stores/working_form'
import ScrollShadow from '../../../common/ScrollShadow.vue' import ScrollShadow from '../../../common/ScrollShadow.vue'
import OpenTable from '../../tables/OpenTable.vue' import OpenTable from '../../tables/OpenTable.vue'
import clonedeep from 'clone-deep' import clonedeep from 'clone-deep'
@ -92,6 +93,15 @@ export default {
name: 'FormSubmissions', name: 'FormSubmissions',
components: { ScrollShadow, OpenTable, VSwitch }, components: { ScrollShadow, OpenTable, VSwitch },
props: {}, props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
formInitDone: false, formInitDone: false,
@ -111,10 +121,10 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
exportUrl () { exportUrl () {

View File

@ -30,7 +30,7 @@
<div class="mx-auto"> <div class="mx-auto">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2" v-html="block.icon" stroke="currentColor" stroke-width="2" v-html="block.icon"
/> ></svg>
</div> </div>
<p class="w-full text-xs text-gray-500 uppercase text-center font-semibold mt-1"> <p class="w-full text-xs text-gray-500 uppercase text-center font-semibold mt-1">
{{ block.title }} {{ block.title }}
@ -50,7 +50,7 @@
<div class="mx-auto"> <div class="mx-auto">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2" v-html="block.icon" stroke="currentColor" stroke-width="2" v-html="block.icon"
/> ></svg>
</div> </div>
<p class="w-full text-xs text-gray-500 uppercase text-center font-semibold mt-1"> <p class="w-full text-xs text-gray-500 uppercase text-center font-semibold mt-1">
{{ block.title }} {{ block.title }}
@ -63,14 +63,26 @@
</template> </template>
<script> <script>
import { mapState } from 'vuex'
import Form from 'vform' import Form from 'vform'
import clonedeep from 'clone-deep' import clonedeep from 'clone-deep'
import { computed } from 'vue'
import { useWorkingFormStore } from '../../../../../stores/working_form'
export default { export default {
name: 'AddFormBlockSidebar', name: 'AddFormBlockSidebar',
components: {}, components: {},
props: {}, props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore,
selectedFieldIndex : computed(() => workingFormStore.selectedFieldIndex),
showAddFieldSidebar : computed(() => workingFormStore.showAddFieldSidebar)
}
},
data () { data () {
return { return {
blockForm: null, blockForm: null,
@ -162,17 +174,13 @@ export default {
}, },
computed: { computed: {
...mapState({
selectedFieldIndex: state => state['open/working_form'].selectedFieldIndex,
showAddFieldSidebar: state => state['open/working_form'].showAddFieldSidebar
}),
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
showSidebar () { showSidebar () {
@ -209,7 +217,7 @@ export default {
methods: { methods: {
closeSidebar () { closeSidebar () {
this.$store.commit('open/working_form/closeAddFieldSidebar') this.workingFormStore.closeAddFieldSidebar()
}, },
reset () { reset () {
this.blockForm = new Form({ this.blockForm = new Form({
@ -231,12 +239,12 @@ export default {
const newFields = clonedeep(this.form.properties) const newFields = clonedeep(this.form.properties)
newFields.push(newBlock) newFields.push(newBlock)
this.form.properties = newFields this.form.properties = newFields
this.$store.commit('open/working_form/openSettingsForField', this.form.properties.length - 1) this.workingFormStore.openSettingsForField(this.form.properties.length - 1)
} else { } else {
const newFields = clonedeep(this.form.properties) const newFields = clonedeep(this.form.properties)
newFields.splice(this.selectedFieldIndex + 1, 0, newBlock) newFields.splice(this.selectedFieldIndex + 1, 0, newBlock)
this.form.properties = newFields this.form.properties = newFields
this.$store.commit('open/working_form/openSettingsForField', this.selectedFieldIndex + 1) this.workingFormStore.openSettingsForField(this.selectedFieldIndex + 1)
} }
this.reset() this.reset()
}, },

View File

@ -129,6 +129,7 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue' import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import ProTag from '../../../../common/ProTag.vue' import ProTag from '../../../../common/ProTag.vue'
import VTransition from '../../../../common/transitions/VTransition.vue' import VTransition from '../../../../common/transitions/VTransition.vue'
@ -136,6 +137,12 @@ import VTransition from '../../../../common/transitions/VTransition.vue'
export default { export default {
components: {EditorOptionsPanel, ProTag, VTransition}, components: {EditorOptionsPanel, ProTag, VTransition},
props: {}, props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
submissionOptions: {} submissionOptions: {}
@ -145,11 +152,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
/** /**

View File

@ -38,11 +38,18 @@
</template> </template>
<script> <script>
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 () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
} }
@ -50,11 +57,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
} }
}, },

View File

@ -17,12 +17,18 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue' import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import CodeInput from '../../../../forms/CodeInput.vue' import CodeInput from '../../../../forms/CodeInput.vue'
export default { export default {
components: { EditorOptionsPanel, CodeInput }, components: { EditorOptionsPanel, CodeInput },
props: { props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
}, },
data () { data () {
return { return {
@ -32,11 +38,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
} }
}, },

View File

@ -23,11 +23,18 @@
</template> </template>
<script> <script>
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 () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
} }
@ -35,11 +42,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
} }
}, },

View File

@ -71,7 +71,7 @@
/> />
<toggle-switch-input name="confetti_on_submission" :form="form" class="mt-4" <toggle-switch-input name="confetti_on_submission" :form="form" class="mt-4"
label="Confetti on successful submisison" label="Confetti on successful submisison"
@input="onChangeConfettiOnSubmission" @update:model-value="onChangeConfettiOnSubmission"
/> />
<toggle-switch-input name="auto_save" :form="form" <toggle-switch-input name="auto_save" :form="form"
label="Auto save form response" label="Auto save form response"
@ -81,6 +81,7 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue' import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import ProTag from '../../../../common/ProTag.vue' import ProTag from '../../../../common/ProTag.vue'
@ -88,6 +89,12 @@ export default {
components: { EditorOptionsPanel, ProTag }, components: { EditorOptionsPanel, ProTag },
props: { props: {
}, },
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
isMounted: false isMounted: false
@ -97,11 +104,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
} }
}, },

View File

@ -59,12 +59,18 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import VSwitch from '../../../../forms/components/VSwitch.vue' import VSwitch from '../../../../forms/components/VSwitch.vue'
import OpenCompleteForm from '../../OpenCompleteForm.vue' import OpenCompleteForm from '../../OpenCompleteForm.vue'
export default { export default {
components: { OpenCompleteForm, VSwitch }, components: { OpenCompleteForm, VSwitch },
props: { props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
}, },
data () { data () {
return { return {
@ -75,11 +81,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
creating () { // returns true if we are creating a form creating () { // returns true if we are creating a form

View File

@ -66,14 +66,27 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import clonedeep from 'clone-deep'
import { useFormsStore } from '../../../../../stores/forms'
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue' import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import SelectInput from '../../../../forms/SelectInput.vue' import SelectInput from '../../../../forms/SelectInput.vue'
import { mapState } from 'vuex'
import clonedeep from 'clone-deep'
export default { export default {
components: { SelectInput, EditorOptionsPanel }, components: { SelectInput, EditorOptionsPanel },
props: {}, props: {},
setup () {
const formsStore = useFormsStore()
const workingFormStore = useWorkingFormStore()
return {
formsStore,
workingFormStore,
forms : computed(() => formsStore.content)
}
},
data () { data () {
return { return {
showCopyFormSettingsModal: false, showCopyFormSettingsModal: false,
@ -106,20 +119,17 @@ export default {
} }
}) })
}, },
...mapState({
forms: state => state['open/forms'].content
}),
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
allTagsOptions () { allTagsOptions () {
return this.$store.getters['open/forms/getAllTags'].map((tagname) => { return this.formsStore.getAllTags.map((tagname) => {
return { return {
name: tagname, name: tagname,
value: tagname value: tagname

View File

@ -31,6 +31,7 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue' import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import FormNotificationsOption from './components/FormNotificationsOption.vue' import FormNotificationsOption from './components/FormNotificationsOption.vue'
import FormNotificationsSlack from './components/FormNotificationsSlack.vue' import FormNotificationsSlack from './components/FormNotificationsSlack.vue'
@ -40,7 +41,12 @@ import FormNotificationsWebhook from './components/FormNotificationsWebhook.vue'
export default { export default {
components: { FormNotificationsSubmissionConfirmation, FormNotificationsSlack, FormNotificationsDiscord, FormNotificationsOption, EditorOptionsPanel, FormNotificationsWebhook }, components: { FormNotificationsSubmissionConfirmation, FormNotificationsSlack, FormNotificationsDiscord, FormNotificationsOption, EditorOptionsPanel, FormNotificationsWebhook },
props: { props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
}, },
data () { data () {
return { return {
@ -50,11 +56,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
zapierUrl: () => window.config.links.zapier_integration zapierUrl: () => window.config.links.zapier_integration

View File

@ -17,11 +17,17 @@
</template> </template>
<script> <script>
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 () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
}, },
data () { data () {
return { return {
@ -30,11 +36,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
} }
}, },

View File

@ -11,12 +11,18 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue' import EditorOptionsPanel from '../../../editors/EditorOptionsPanel.vue'
import FormFieldsEditor from '../FormFieldsEditor.vue' import FormFieldsEditor from '../FormFieldsEditor.vue'
export default { export default {
components: { EditorOptionsPanel, FormFieldsEditor }, components: { EditorOptionsPanel, FormFieldsEditor },
props: { props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
}, },
data () { data () {
return { return {
@ -26,11 +32,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
} }
} }

View File

@ -46,12 +46,19 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '../../../../../common/ProTag.vue' import ProTag from '../../../../../common/ProTag.vue'
import FormNotificationsMessageActions from './FormNotificationsMessageActions.vue' import FormNotificationsMessageActions from './FormNotificationsMessageActions.vue'
export default { export default {
components: { ProTag, FormNotificationsMessageActions }, components: { ProTag, FormNotificationsMessageActions },
props: {}, props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
showModal: false showModal: false
@ -61,11 +68,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
} }
}, },

View File

@ -50,11 +50,18 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '../../../../../common/ProTag.vue' import ProTag from '../../../../../common/ProTag.vue'
export default { export default {
components: { ProTag }, components: { ProTag },
props: {}, props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
showModal: false showModal: false
@ -64,11 +71,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
replayToEmailField () { replayToEmailField () {

View File

@ -47,12 +47,19 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '../../../../../common/ProTag.vue' import ProTag from '../../../../../common/ProTag.vue'
import FormNotificationsMessageActions from './FormNotificationsMessageActions.vue' import FormNotificationsMessageActions from './FormNotificationsMessageActions.vue'
export default { export default {
components: { ProTag, FormNotificationsMessageActions }, components: { ProTag, FormNotificationsMessageActions },
props: {}, props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
showModal: false showModal: false
@ -62,11 +69,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
} }
}, },

View File

@ -62,11 +62,18 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '../../../../../common/ProTag.vue' import ProTag from '../../../../../common/ProTag.vue'
export default { export default {
components: { ProTag }, components: { ProTag },
props: {}, props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
showModal: false showModal: false
@ -76,11 +83,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
emailSubmissionConfirmationField () { emailSubmissionConfirmationField () {

View File

@ -43,11 +43,18 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '../../../../../common/ProTag.vue' import ProTag from '../../../../../common/ProTag.vue'
export default { export default {
components: { ProTag }, components: { ProTag },
props: {}, props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
showModal: false showModal: false
@ -57,11 +64,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
} }
}, },

View File

@ -38,7 +38,7 @@
:multiple="true" class="mt-1" placeholder="Actions..." :multiple="true" class="mt-1" placeholder="Actions..."
help="Action(s) triggerred when above conditions are true" help="Action(s) triggerred when above conditions are true"
:options="actionOptions" :options="actionOptions"
@input="onActionInput" @update:model-value="onActionInput"
/> />
<modal :show="showCopyFormModal" @close="showCopyFormModal = false"> <modal :show="showCopyFormModal" @close="showCopyFormModal = false">

View File

@ -72,10 +72,11 @@
</template> </template>
<script> <script>
import Form from 'vform'
import store from '~/store'
import { mapState, mapGetters } from 'vuex'
import axios from 'axios' import axios from 'axios'
import Form from 'vform'
import { computed } from 'vue'
import { useAuthStore } from '../../../../../stores/auth'
import { useTemplatesStore } from '../../../../../stores/templates'
import QuestionsEditor from './QuestionsEditor.vue' import QuestionsEditor from './QuestionsEditor.vue'
export default { export default {
@ -87,6 +88,18 @@ export default {
template: { type: Object, required: false, default: () => {} } template: { type: Object, required: false, default: () => {} }
}, },
setup () {
const authStore = useAuthStore()
const templatesStore = useTemplatesStore()
return {
templatesStore,
user : computed(() => authStore.user),
templates : computed(() => templatesStore.content),
industries : computed(() => templatesStore.industries),
types : computed(() => templatesStore.types)
}
},
data: () => ({ data: () => ({
templateForm: null templateForm: null
}), }),
@ -104,18 +117,10 @@ export default {
related_templates: null, related_templates: null,
questions: [] questions: []
}) })
store.dispatch('open/templates/loadIfEmpty') this.templatesStore.loadIfEmpty()
}, },
computed: { computed: {
...mapState({
templates: state => state['open/templates'].content,
industries: state => state['open/templates'].industries,
types: state => state['open/templates'].types
}),
...mapGetters({
user: 'auth/user'
}),
typesOptions () { typesOptions () {
return Object.values(this.types).map((type) => { return Object.values(this.types).map((type) => {
return { return {
@ -156,7 +161,7 @@ export default {
if (response.data.message) { if (response.data.message) {
this.alertSuccess(response.data.message) this.alertSuccess(response.data.message)
} }
this.$store.commit('open/templates/addOrUpdate', response.data.data) this.templatesStore.addOrUpdate(response.data.data)
this.$emit('close') this.$emit('close')
}) })
}, },
@ -166,7 +171,7 @@ export default {
if (response.data.message) { if (response.data.message) {
this.alertSuccess(response.data.message) this.alertSuccess(response.data.message)
} }
this.$store.commit('open/templates/addOrUpdate', response.data.data) this.templatesStore.addOrUpdate(response.data.data)
this.$emit('close') this.$emit('close')
}) })
}, },
@ -177,7 +182,7 @@ export default {
this.alertSuccess(response.data.message) this.alertSuccess(response.data.message)
} }
this.$router.push({ name: 'templates' }) this.$router.push({ name: 'templates' })
this.$store.commit('open/templates/remove', this.template) this.templatesStore.remove(this.template)
this.$emit('close') this.$emit('close')
}) })
} }

View File

@ -70,8 +70,9 @@
</template> </template>
<script> <script>
import { mapState } from 'vuex' import { computed } from 'vue'
import clonedeep from 'clone-deep' import clonedeep from 'clone-deep'
import { useWorkingFormStore } from '../../../../stores/working_form'
import ChangeFieldType from './components/ChangeFieldType.vue' import ChangeFieldType from './components/ChangeFieldType.vue'
import FieldOptions from './components/FieldOptions.vue' import FieldOptions from './components/FieldOptions.vue'
import BlockOptions from './components/BlockOptions.vue' import BlockOptions from './components/BlockOptions.vue'
@ -80,6 +81,14 @@ export default {
name: 'FormFieldEditSidebar', name: 'FormFieldEditSidebar',
components: { ChangeFieldType, FieldOptions, BlockOptions }, components: { ChangeFieldType, FieldOptions, BlockOptions },
props: {}, props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore,
selectedFieldIndex : computed(() => workingFormStore.selectedFieldIndex),
showEditFieldSidebar : computed(() => workingFormStore.showEditFieldSidebar)
}
},
data () { data () {
return { return {
@ -87,17 +96,13 @@ export default {
}, },
computed: { computed: {
...mapState({
selectedFieldIndex: state => state['open/working_form'].selectedFieldIndex,
showEditFieldSidebar: state => state['open/working_form'].showEditFieldSidebar
}),
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
field () { field () {
@ -145,7 +150,7 @@ export default {
this.closeSidebar() this.closeSidebar()
}, },
closeSidebar () { closeSidebar () {
this.$store.commit('open/working_form/closeEditFieldSidebar') this.workingFormStore.closeEditFieldSidebar()
}, },
generateUUID () { generateUUID () {
let d = new Date().getTime()// Timestamp let d = new Date().getTime()// Timestamp

View File

@ -186,7 +186,7 @@
<text-area-input v-model="optionsText" :name="field.id+'_options_text'" class="mt-3" <text-area-input v-model="optionsText" :name="field.id+'_options_text'" class="mt-3"
label="Set selection options" label="Set selection options"
help="Add one option per line" help="Add one option per line"
@input="onFieldOptionsChange" @update:model-value="onFieldOptionsChange"
/> />
<v-checkbox v-model="field.allow_creation" <v-checkbox v-model="field.allow_creation"
name="allow_creation" help="" @update:model-value="onFieldAllowCreationChange" name="allow_creation" help="" @update:model-value="onFieldAllowCreationChange"

View File

@ -78,6 +78,7 @@
</template> </template>
<script> <script>
import { useWorkingFormStore } from '../../../stores/working_form'
import OpenText from './components/OpenText.vue' import OpenText from './components/OpenText.vue'
import OpenUrl from './components/OpenUrl.vue' import OpenUrl from './components/OpenUrl.vue'
import OpenSelect from './components/OpenSelect.vue' import OpenSelect from './components/OpenSelect.vue'
@ -119,6 +120,13 @@ export default {
} }
}, },
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () { data () {
return { return {
tableHash: null, tableHash: null,
@ -129,10 +137,11 @@ export default {
computed: { computed: {
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
hasActions () { hasActions () {

View File

@ -31,18 +31,23 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
export default { export default {
setup () {
const authStore = useAuthStore()
return {
user : computed(() => authStore.user)
}
},
data: () => ({ data: () => ({
appName: window.config.appName, appName: window.config.appName,
currYear: new Date().getFullYear(), currYear: new Date().getFullYear(),
}), }),
computed: { computed: {
...mapGetters({
user: 'auth/user'
}),
helpUrl: () => window.config.links.help_url, helpUrl: () => window.config.links.help_url,
githubUrl: () => window.config.links.github_url, githubUrl: () => window.config.links.github_url,
forumUrl: () => window.config.links.github_forum_url, forumUrl: () => window.config.links.github_forum_url,

View File

@ -24,28 +24,34 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth';
import { useWorkspacesStore } from '../../stores/workspaces';
export default { export default {
setup () {
const authStore = useAuthStore()
const workspacesStore = useWorkspacesStore()
return {
authStore,
workspacesStore,
isImpersonating : computed(() => authStore.isImpersonating),
}
},
data: () => ({ data: () => ({
loading: false loading: false
}), }),
computed: { computed: {},
...mapGetters({
isImpersonating: 'auth/isImpersonating'
})
},
mounted () { mounted () {},
},
methods: { methods: {
reverseImpersonation () { reverseImpersonation () {
this.loading = true this.loading = true
this.$store.dispatch('auth/stopImpersonating') this.authStore.stopImpersonating().then(() => {
.then(() => { this.workspacesStore.set([])
this.$store.commit('open/workspaces/set', [])
this.$router.push({ name: 'settings.admin' }) this.$router.push({ name: 'settings.admin' })
this.loading = false this.loading = false
}) })

View File

@ -62,14 +62,22 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import axios from 'axios' import axios from 'axios'
import { mapGetters } from 'vuex' import { useAuthStore } from '../../../stores/auth';
import VTransition from '../../common/transitions/VTransition.vue' import VTransition from '../../common/transitions/VTransition.vue'
export default { export default {
components: { VTransition }, components: { VTransition },
props: {}, props: {},
setup () {
const authStore = useAuthStore()
return {
user : computed(() => authStore.user)
}
},
data: () => ({ data: () => ({
changelogEntries: [], changelogEntries: [],
showNewFeatures: false showNewFeatures: false
@ -80,9 +88,6 @@ export default {
}, },
computed: { computed: {
...mapGetters({
user: 'auth/user'
}),
requestFeatureLink () { requestFeatureLink () {
return window.config.links.feature_requests return window.config.links.feature_requests
}, },

View File

@ -122,8 +122,10 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import axios from 'axios' import axios from 'axios'
import {mapGetters, mapState} from 'vuex' import { useAuthStore } from '../../../../stores/auth'
import { useFormsStore } from '../../../../stores/forms'
import Dropdown from '../../../common/Dropdown.vue' import Dropdown from '../../../common/Dropdown.vue'
import FormTemplateModal from '../../../open/forms/components/templates/FormTemplateModal.vue' import FormTemplateModal from '../../../open/forms/components/templates/FormTemplateModal.vue'
@ -135,6 +137,15 @@ export default {
isMainPage: { type: Boolean, required: false, default: false } isMainPage: { type: Boolean, required: false, default: false }
}, },
setup () {
const authStore = useAuthStore()
const formsStore = useFormsStore()
return {
formsStore,
user : computed(() => authStore.user)
}
},
data: () => ({ data: () => ({
loadingDuplicate: false, loadingDuplicate: false,
loadingDelete: false, loadingDelete: false,
@ -143,9 +154,6 @@ export default {
}), }),
computed: { computed: {
...mapGetters({
user: 'auth/user'
}),
formEndpoint: () => '/api/open/forms/{id}', formEndpoint: () => '/api/open/forms/{id}',
}, },
@ -163,7 +171,7 @@ export default {
if (this.loadingDuplicate) return if (this.loadingDuplicate) return
this.loadingDuplicate = true this.loadingDuplicate = true
axios.post(this.formEndpoint.replace('{id}', this.form.id) + '/duplicate').then((response) => { axios.post(this.formEndpoint.replace('{id}', this.form.id) + '/duplicate').then((response) => {
this.$store.commit('open/forms/addOrUpdate', response.data.new_form) this.formsStore.addOrUpdate(response.data.new_form)
this.$router.push({name: 'forms.show', params: {slug: response.data.new_form.slug}}) this.$router.push({name: 'forms.show', params: {slug: response.data.new_form.slug}})
this.alertSuccess('Form was successfully duplicated.') this.alertSuccess('Form was successfully duplicated.')
this.loadingDuplicate = false this.loadingDuplicate = false
@ -173,7 +181,7 @@ export default {
if (this.loadingDelete) return if (this.loadingDelete) return
this.loadingDelete = true this.loadingDelete = true
axios.delete(this.formEndpoint.replace('{id}', this.form.id)).then(() => { axios.delete(this.formEndpoint.replace('{id}', this.form.id)).then(() => {
this.$store.commit('open/forms/remove', this.form) this.formsStore.remove(this.form)
this.$router.push({name: 'home'}) this.$router.push({name: 'home'})
this.alertSuccess('Form was deleted.') this.alertSuccess('Form was deleted.')
this.loadingDelete = false this.loadingDelete = false

View File

@ -72,7 +72,9 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import axios from 'axios' import axios from 'axios'
import { useFormsStore } from '../../../../stores/forms'
export default { export default {
name: 'RegenerateFormLink', name: 'RegenerateFormLink',
@ -81,6 +83,13 @@ export default {
form: { type: Object, required: true } form: { type: Object, required: true }
}, },
setup () {
const formsStore = useFormsStore()
return {
formsStore
}
},
data: () => ({ data: () => ({
loadingNewLink: false, loadingNewLink: false,
showGenerateFormLinkModal: false, showGenerateFormLinkModal: false,
@ -95,7 +104,7 @@ export default {
if (this.loadingNewLink) return if (this.loadingNewLink) return
this.loadingNewLink = true this.loadingNewLink = true
axios.put(this.formEndpoint.replace('{id}', this.form.id) + '/regenerate-link/' + option).then((response) => { axios.put(this.formEndpoint.replace('{id}', this.form.id) + '/regenerate-link/' + option).then((response) => {
this.$store.commit('open/forms/addOrUpdate', response.data.form) this.formsStore.addOrUpdate(response.data.form)
this.$router.push({name: 'forms.show', params: {slug: response.data.form.slug}}) this.$router.push({name: 'forms.show', params: {slug: response.data.form.slug}})
this.alertSuccess(response.data.message) this.alertSuccess(response.data.message)
this.loadingNewLink = false this.loadingNewLink = false

View File

@ -11,14 +11,14 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import TextInput from '../../forms/TextInput.vue'
import Form from 'vform'
import VButton from '../../common/Button.vue'
import axios from 'axios' import axios from 'axios'
import Form from 'vform'
import { useAuthStore } from '../../../stores/auth'
import TextInput from '../../forms/TextInput.vue'
import VButton from '../../common/Button.vue'
export default { export default {
components: { VButton, TextInput }, components: { VButton, TextInput },
props: { props: {
show: { show: {
@ -34,6 +34,14 @@ export default {
default: true default: true
} }
}, },
setup () {
const authStore = useAuthStore()
return {
user : computed(() => authStore.user)
}
},
data: () => ({ data: () => ({
form: new Form({ form: new Form({
name: '', name: '',
@ -88,10 +96,6 @@ export default {
} }
}, },
computed: { computed: {}
...mapGetters({
user: 'auth/user'
})
}
} }
</script> </script>

View File

@ -107,7 +107,8 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../../stores/auth'
import axios from 'axios' import axios from 'axios'
import MonthlyYearlySelector from './MonthlyYearlySelector.vue' import MonthlyYearlySelector from './MonthlyYearlySelector.vue'
import CheckoutDetailsModal from './CheckoutDetailsModal.vue' import CheckoutDetailsModal from './CheckoutDetailsModal.vue'
@ -127,6 +128,13 @@ export default {
default: false default: false
} }
}, },
setup () {
const authStore = useAuthStore()
return {
authenticated : computed(() => authStore.check),
user : computed(() => authStore.user)
}
},
data: () => ({ data: () => ({
isYearly: true, isYearly: true,
selectedPlan: 'pro', selectedPlan: 'pro',
@ -160,11 +168,6 @@ export default {
} }
}, },
computed: { computed: {}
...mapGetters({
authenticated: 'auth/check',
user: 'auth/user'
})
}
} }
</script> </script>

View File

@ -39,7 +39,8 @@
</template> </template>
<script> <script>
import store from '~/store' import { computed } from 'vue'
import { useTemplatesStore } from '../../../stores/templates'
import TemplateTags from './TemplateTags.vue' import TemplateTags from './TemplateTags.vue'
export default { export default {
@ -52,22 +53,29 @@ export default {
} }
}, },
setup () {
const templatesStore = useTemplatesStore()
return {
templatesStore
}
},
data: () => ({}), data: () => ({}),
computed: { computed: {
template () { template () {
return this.$store.getters['open/templates/getBySlug'](this.slug) return this.templatesStore.getBySlug(this.slug)
} }
}, },
watch: { watch: {
slug () { slug () {
store.dispatch('open/templates/loadTemplate', this.slug) this.templatesStore.loadTemplate(this.slug)
} }
}, },
mounted () { mounted () {
store.dispatch('open/templates/loadTemplate', this.slug) this.templatesStore.loadTemplate(this.slug)
}, },
methods: { methods: {

View File

@ -41,6 +41,9 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import { useTemplatesStore } from '../../../stores/templates'
export default { export default {
props: { props: {
slug: { slug: {
@ -53,19 +56,26 @@ export default {
} }
}, },
setup () {
const templatesStore = useTemplatesStore()
return {
templatesStore
}
},
data: () => ({}), data: () => ({}),
computed: { computed: {
template () { template () {
return this.$store.getters['open/templates/getBySlug'](this.slug) return this.templatesStore.getBySlug(this.slug)
}, },
types () { types () {
if (!this.template) return null if (!this.template) return null
return this.$store.getters['open/templates/getTemplateTypes'](this.template.types) return this.templatesStore.getTemplateTypes(this.template.types)
}, },
industries () { industries () {
if (!this.template) return null if (!this.template) return null
return this.$store.getters['open/templates/getTemplateIndustries'](this.template.industries) return this.templatesStore.getTemplateIndustries(this.template.industries)
} }
}, },

View File

@ -79,24 +79,25 @@
</template> </template>
<script> <script>
import store from '~/store' import { computed } from 'vue'
import { mapGetters, mapState } from 'vuex' import { useAuthStore } from '../../../stores/auth'
import { useTemplatesStore } from '../../../stores/templates'
import Form from 'vform' import Form from 'vform'
import Fuse from 'fuse.js' import Fuse from 'fuse.js'
import SingleTemplate from './SingleTemplate.vue' import SingleTemplate from './SingleTemplate.vue'
const loadTemplates = function (onlyMy) { const loadTemplates = function (onlyMy) {
const templatesStore = useTemplatesStore()
if(onlyMy){ if(onlyMy){
store.dispatch('open/templates/loadAll', {'onlymy':true}) templatesStore.loadAll({'onlymy':true})
} else { } else {
store.dispatch('open/templates/loadIfEmpty') templatesStore.loadIfEmpty()
} }
} }
export default { export default {
name: 'TemplatesList', name: 'TemplatesList',
components: { SingleTemplate }, components: { SingleTemplate },
props: { props: {
onlyMy: { onlyMy: {
type: Boolean, type: Boolean,
@ -104,6 +105,18 @@ export default {
} }
}, },
setup () {
const authStore = useAuthStore()
const templatesStore = useTemplatesStore()
return {
user : computed(() => authStore.user),
templates : computed(() => templatesStore.content),
templatesLoading : computed(() => templatesStore.loading),
industries : computed(() => templatesStore.industries),
types : computed(() => templatesStore.types)
}
},
data: () => ({ data: () => ({
selectedType: 'all', selectedType: 'all',
selectedIndustry: 'all', selectedIndustry: 'all',
@ -119,15 +132,6 @@ export default {
}, },
computed: { computed: {
...mapState({
templates: state => state['open/templates'].content,
templatesLoading: state => state['open/templates'].loading,
industries: state => state['open/templates'].industries,
types: state => state['open/templates'].types
}),
...mapGetters({
user: 'auth/user'
}),
industriesOptions () { industriesOptions () {
return [{ name: 'All Industries', value: 'all' }].concat(Object.values(this.industries).map((industry) => { return [{ name: 'All Industries', value: 'all' }].concat(Object.values(this.industries).map((industry) => {
return { return {

View File

@ -87,16 +87,19 @@
</template> </template>
<script> <script>
import {mapGetters} from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../../stores/auth'
export default { export default {
setup () {
const authStore = useAuthStore()
return {
authenticated : computed(() => authStore.check)
}
},
props: {}, props: {},
data: () => ({}), data: () => ({}),
computed: { computed: {},
...mapGetters({
authenticated: 'auth/check'
}),
},
methods: {} methods: {}
} }
</script> </script>

View File

@ -36,19 +36,23 @@
</template> </template>
<script> <script>
import store from '~/store' import { computed } from 'vue'
import { mapGetters, mapState } from 'vuex' import { useTemplatesStore } from '../../../stores/templates'
import SingleTemplate from '../templates/SingleTemplate.vue' import SingleTemplate from '../templates/SingleTemplate.vue'
export default { export default {
components: { SingleTemplate }, components: { SingleTemplate },
props: { }, props: { },
setup () {
const templatesStore = useTemplatesStore()
return {
templatesStore,
templates : computed(() => templatesStore.content)
}
},
data: () => ({}), data: () => ({}),
computed: { computed: {
...mapState({
templates: state => state['open/templates'].content
}),
sliderTemplates () { sliderTemplates () {
return this.templates.slice(0, 20) return this.templates.slice(0, 20)
} }
@ -66,7 +70,7 @@ export default {
}, },
mounted() { mounted() {
store.dispatch('open/templates/loadAll', { limit: 20 }) this.templatesStore.loadAll({ limit: 20 })
}, },
methods: { methods: {

View File

@ -1,12 +1,21 @@
<template /> <template />
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
export default { export default {
name: 'Amplitude', name: 'Amplitude',
setup () {
const authStore = useAuthStore()
return {
authStore,
authenticated : computed(() => authStore.check),
user : computed(() => authStore.user)
}
},
data: function () { data: function () {
return { return {
loaded: false, loaded: false,
@ -14,12 +23,7 @@ export default {
} }
}, },
computed: { computed: {},
...mapGetters({
authenticated: 'auth/check',
user: 'auth/user'
})
},
watch: { watch: {
$route () { $route () {

View File

@ -1,12 +1,19 @@
<template /> <template />
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
export default { export default {
name: 'Hotjar', name: 'Hotjar',
setup () {
const authStore = useAuthStore()
return {
authenticated : computed(() => authStore.check),
}
},
watch: { watch: {
authenticated () { authenticated () {
if (this.authenticated) { if (this.authenticated) {
@ -38,9 +45,6 @@ export default {
}, },
computed: { computed: {
...mapGetters({
authenticated: 'auth/check'
}),
isIframe () { isIframe () {
return window.location !== window.parent.location || window.frameElement return window.location !== window.parent.location || window.frameElement
} }

View File

@ -21,23 +21,27 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../../stores/auth'
import VButton from '../../common/Button.vue' import VButton from '../../common/Button.vue'
export default { export default {
name: 'AppSumoBilling', name: 'AppSumoBilling',
components: { VButton }, components: { VButton },
setup () {
const authStore = useAuthStore()
return {
user : computed(() => authStore.user),
}
},
data () { data () {
return { return {
} }
}, },
computed: { computed: {
...mapGetters({
user: 'auth/user'
}),
licenseTier () { licenseTier () {
return this.user?.active_license?.meta?.tier return this.user?.active_license?.meta?.tier
}, },
@ -67,10 +71,9 @@ export default {
mounted () {}, mounted () {},
created () { created () {},
},
destroyed () { destroyed () {},
},
methods: {} methods: {}
} }

View File

@ -1,7 +1,8 @@
import store from '~/store' import { useAuthStore } from '../stores/auth';
export default (to, from, next) => { export default (to, from, next) => {
if (!store.getters['auth/user'].admin) { const authStore = useAuthStore()
if (!authStore.user?.admin) {
next({ name: 'home' }) next({ name: 'home' })
} else { } else {
next() next()

View File

@ -1,8 +1,9 @@
import store from '~/store' import { useAuthStore } from '../stores/auth';
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
export default async (to, from, next) => { export default async (to, from, next) => {
if (!store.getters['auth/check']) { const authStore = useAuthStore()
if (!authStore.check) {
Cookies.set('intended_url', to.path) Cookies.set('intended_url', to.path)
next({ name: 'login' }) next({ name: 'login' })

View File

@ -1,4 +1,4 @@
import store from '~/store' import { useAuthStore } from '../stores/auth';
import * as Sentry from '@sentry/vue' import * as Sentry from '@sentry/vue'
export function initCrisp (user) { export function initCrisp (user) {
@ -36,12 +36,13 @@ export function initSentry (user) {
} }
export default async (to, from, next) => { export default async (to, from, next) => {
if (!store.getters['auth/check'] && const authStore = useAuthStore()
store.getters['auth/token'] !== null && if (!authStore.check &&
store.getters['auth/token'] !== undefined authStore.token !== null &&
authStore.token !== undefined
) { ) {
try { try {
store.dispatch('auth/fetchUser').then((user) => { authStore.fetchUser().then((user) => {
initCrisp(user) initCrisp(user)
initSentry(user) initSentry(user)
}) })

View File

@ -1,7 +1,8 @@
import store from '~/store' import { useAuthStore } from '../stores/auth';
export default (to, from, next) => { export default (to, from, next) => {
if (store.getters['auth/check']) { const authStore = useAuthStore()
if (authStore.check) {
next({ name: 'home' }) next({ name: 'home' })
} else { } else {
next() next()

View File

@ -1,7 +1,8 @@
import store from '~/store' import { useAuthStore } from '../stores/auth';
export default async (to, from, next) => { export default async (to, from, next) => {
if (store.getters['auth/check'] && store.getters['auth/user'].workspaces_count === 0) { const authStore = useAuthStore()
if (authStore.check && authStore.user?.workspaces_count === 0) {
if ([ if ([
'forms.create', 'forms.create',
'forms.show', 'forms.show',

View File

@ -1,4 +1,4 @@
import store from '~/store' import { useAuthStore } from '../stores/auth';
/** /**
* This is middleware to check the current user role. * This is middleware to check the current user role.
@ -7,14 +7,13 @@ import store from '~/store'
*/ */
export default (to, from, next, roles) => { export default (to, from, next, roles) => {
// Grab the user const authStore = useAuthStore()
const user = store.getters['auth/user']
// Split roles into an array // Split roles into an array
roles = roles.split(',') roles = roles.split(',')
// Check if the user has one of the required roles... // Check if the user has one of the required roles...
if (!roles.includes(user.role)) { if (!roles.includes(authStore.user?.role)) {
next('/unauthorized') next('/unauthorized')
} }

View File

@ -1,7 +1,8 @@
import store from '~/store' import { useAuthStore } from '../stores/auth';
export default (to, from, next) => { export default (to, from, next) => {
if (!store.getters['auth/user'].is_subscribed) { const authStore = useAuthStore()
if (!authStore.user?.is_subscribed) {
next({ name: 'pricing' }) next({ name: 'pricing' })
} else { } else {
next() next()

View File

@ -476,7 +476,8 @@
</template> </template>
<script> <script>
import {mapGetters} from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../stores/auth'
import OpenFormFooter from '../components/pages/OpenFormFooter.vue' import OpenFormFooter from '../components/pages/OpenFormFooter.vue'
import SeoMeta from '../mixins/seo-meta.js' import SeoMeta from '../mixins/seo-meta.js'
@ -484,6 +485,14 @@ export default {
components: {OpenFormFooter}, components: {OpenFormFooter},
layout: 'default', layout: 'default',
mixins: [SeoMeta], mixins: [SeoMeta],
setup () {
const authStore = useAuthStore()
return {
authenticated : computed(() => authStore.check),
}
},
data: () => ({ data: () => ({
title: window.config.appName, title: window.config.appName,
metaTitle: 'AI form builder for free', metaTitle: 'AI form builder for free',
@ -494,9 +503,6 @@ export default {
methods: {}, methods: {},
computed: { computed: {
...mapGetters({
authenticated: 'auth/check'
}),
configLinks: () => window.config.links configLinks: () => window.config.links
} }
} }

View File

@ -41,8 +41,10 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import Form from 'vform' import Form from 'vform'
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import { useAuthStore } from '../../../stores/auth'
import OpenFormFooter from '../../../components/pages/OpenFormFooter.vue' import OpenFormFooter from '../../../components/pages/OpenFormFooter.vue'
import Testimonials from '../../../components/pages/welcome/Testimonials.vue' import Testimonials from '../../../components/pages/welcome/Testimonials.vue'
import ForgotPasswordModal from '../ForgotPasswordModal.vue' import ForgotPasswordModal from '../ForgotPasswordModal.vue'
@ -62,6 +64,13 @@ export default {
} }
}, },
setup () {
const authStore = useAuthStore()
return {
authStore
}
},
data: () => ({ data: () => ({
form: new Form({ form: new Form({
email: '', email: '',
@ -77,13 +86,10 @@ export default {
const { data } = await this.form.post('/api/login') const { data } = await this.form.post('/api/login')
// Save the token. // Save the token.
this.$store.dispatch('auth/saveToken', { this.authStore.saveToken(data.token, this.remember)
token: data.token,
remember: this.remember
})
// Fetch the user. // Fetch the user.
await this.$store.dispatch('auth/fetchUser') await this.authStore.fetchUser()
// Redirect home. // Redirect home.
this.redirect() this.redirect()

View File

@ -48,7 +48,9 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import Form from 'vform' import Form from 'vform'
import { useAuthStore } from '../../../stores/auth'
import { initCrisp } from '../../../middleware/check-auth.js' import { initCrisp } from '../../../middleware/check-auth.js'
export default { export default {
@ -62,6 +64,13 @@ export default {
} }
}, },
setup () {
const authStore = useAuthStore()
return {
authStore
}
},
data: () => ({ data: () => ({
form: new Form({ form: new Form({
name: '', name: '',
@ -112,10 +121,10 @@ export default {
const { data: { token } } = await this.form.post('/api/login') const { data: { token } } = await this.form.post('/api/login')
// Save the token. // Save the token.
this.$store.dispatch('auth/saveToken', { token }) this.authStore.saveToken(token)
// Update the user. // Update the user.
await this.$store.dispatch('auth/updateUser', { user: data }) await this.authStore.updateUser(data)
// Track event // Track event
this.$logEvent('register', { source: this.form.hear_about_us }) this.$logEvent('register', { source: this.form.hear_about_us })

View File

@ -24,18 +24,21 @@
</template> </template>
<script> <script>
import store from '~/store' import { computed } from 'vue'
import Form from 'vform' import Form from 'vform'
import { mapState, mapActions } from 'vuex' import { useTemplatesStore } from '../../stores/templates'
import { useWorkingFormStore } from '../../stores/working_form'
import { useWorkspacesStore } from '../../stores/workspaces'
import QuickRegister from '../auth/components/QuickRegister.vue' import QuickRegister from '../auth/components/QuickRegister.vue'
import initForm from '../../mixins/form_editor/initForm.js' import initForm from '../../mixins/form_editor/initForm.js'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
import CreateFormBaseModal from '../../components/pages/forms/create/CreateFormBaseModal.vue' import CreateFormBaseModal from '../../components/pages/forms/create/CreateFormBaseModal.vue'
const loadTemplates = function () { const loadTemplates = function () {
store.commit('open/templates/startLoading') const templatesStore = useTemplatesStore()
store.dispatch('open/templates/loadIfEmpty').then(() => { templatesStore.startLoading()
store.commit('open/templates/stopLoading') templatesStore.loadIfEmpty().then(() => {
templatesStore.stopLoading()
}) })
} }
@ -45,13 +48,25 @@ export default {
QuickRegister, CreateFormBaseModal QuickRegister, CreateFormBaseModal
}, },
mixins: [initForm, SeoMeta], mixins: [initForm, SeoMeta],
middleware: 'guest',
beforeRouteEnter (to, from, next) { beforeRouteEnter (to, from, next) {
loadTemplates() loadTemplates()
next() next()
}, },
middleware: 'guest', setup () {
const templatesStore = useTemplatesStore()
const workingFormStore = useWorkingFormStore()
const workspacesStore = useWorkspacesStore()
return {
templatesStore,
workingFormStore,
workspacesStore,
workspaces : computed(() => workspacesStore.content),
workspacesLoading : computed(() => workspacesStore.loading)
}
},
data () { data () {
return { return {
@ -66,21 +81,17 @@ export default {
}, },
computed: { computed: {
...mapState({
workspaces: state => state['open/workspaces'].content,
workspacesLoading: state => state['open/workspaces'].loading
}),
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
workspace () { workspace () {
return this.$store.getters['open/workspaces/getCurrent']() return this.workspacesStore.getCurrent()
} }
}, },
@ -100,12 +111,12 @@ export default {
is_enterprise: false, is_enterprise: false,
is_pro: false is_pro: false
} }
this.$store.commit('open/workspaces/set', [guestWorkspace]) this.workspacesStore.set([guestWorkspace])
this.$store.commit('open/workspaces/setCurrentId', guestWorkspace.id) this.workspacesStore.setCurrentId(guestWorkspace.id)
this.initForm() this.initForm()
if (this.$route.query.template !== undefined && this.$route.query.template) { if (this.$route.query.template !== undefined && this.$route.query.template) {
const template = this.$store.getters['open/templates/getBySlug'](this.$route.query.template) const template = this.templatesStore.getBySlug(this.$route.query.template)
if (template && template.structure) { if (template && template.structure) {
this.form = new Form({ ...this.form.data(), ...template.structure }) this.form = new Form({ ...this.form.data(), ...template.structure })
} }
@ -121,16 +132,13 @@ export default {
unmounted () {}, unmounted () {},
methods: { methods: {
...mapActions({
loadWorkspaces: 'open/workspaces/load'
}),
openRegister () { openRegister () {
this.registerModal = true this.registerModal = true
}, },
afterLogin () { afterLogin () {
this.registerModal = false this.registerModal = false
this.isGuest = false this.isGuest = false
this.loadWorkspaces() this.workspacesStore.load()
setTimeout(() => { setTimeout(() => {
if (this.$refs.editor) { if (this.$refs.editor) {
this.$refs.editor.saveFormCreate() this.$refs.editor.saveFormCreate()

View File

@ -19,17 +19,21 @@
</template> </template>
<script> <script>
import store from '~/store'
import Form from 'vform' import Form from 'vform'
import { mapState, mapActions } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
import { useTemplatesStore } from '../../stores/templates'
import { useWorkingFormStore } from '../../stores/working_form'
import { useWorkspacesStore } from '../../stores/workspaces'
import initForm from '../../mixins/form_editor/initForm.js' import initForm from '../../mixins/form_editor/initForm.js'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
import CreateFormBaseModal from '../../components/pages/forms/create/CreateFormBaseModal.vue' import CreateFormBaseModal from '../../components/pages/forms/create/CreateFormBaseModal.vue'
const loadTemplates = function () { const loadTemplates = function () {
store.commit('open/templates/startLoading') const templatesStore = useTemplatesStore()
store.dispatch('open/templates/loadIfEmpty').then(() => { templatesStore.startLoading()
store.commit('open/templates/stopLoading') templatesStore.loadIfEmpty().then(() => {
templatesStore.stopLoading()
}) })
} }
@ -38,6 +42,7 @@ export default {
components: { CreateFormBaseModal }, components: { CreateFormBaseModal },
mixins: [initForm, SeoMeta], mixins: [initForm, SeoMeta],
middleware: 'auth',
beforeRouteEnter (to, from, next) { beforeRouteEnter (to, from, next) {
loadTemplates() loadTemplates()
@ -54,7 +59,20 @@ export default {
next() next()
}, },
middleware: 'auth', setup () {
const authStore = useAuthStore()
const templatesStore = useTemplatesStore()
const workingFormStore = useWorkingFormStore()
const workspacesStore = useWorkspacesStore()
return {
templatesStore,
workingFormStore,
workspacesStore,
user: computed(() => authStore.user),
workspaces : computed(() => workspacesStore.content),
workspacesLoading : computed(() => workspacesStore.loading)
}
},
data () { data () {
return { return {
@ -68,22 +86,17 @@ export default {
}, },
computed: { computed: {
...mapState({
workspaces: state => state['open/workspaces'].content,
workspacesLoading: state => state['open/workspaces'].loading,
user: state => state.auth.user
}),
form: { form: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
workspace () { workspace () {
return this.$store.getters['open/workspaces/getCurrent']() return this.workspacesStore.getCurrent()
} }
}, },
@ -108,7 +121,7 @@ export default {
this.initForm() this.initForm()
this.formInitialHash = this.hashString(JSON.stringify(this.form.data())) this.formInitialHash = this.hashString(JSON.stringify(this.form.data()))
if (this.$route.query.template !== undefined && this.$route.query.template) { if (this.$route.query.template !== undefined && this.$route.query.template) {
const template = this.$store.getters['open/templates/getBySlug'](this.$route.query.template) const template = this.templatesStore.getBySlug(this.$route.query.template)
if (template && template.structure) { if (template && template.structure) {
this.form = new Form({ ...this.form.data(), ...template.structure }) this.form = new Form({ ...this.form.data(), ...template.structure })
} }
@ -117,7 +130,7 @@ export default {
this.showInitialFormModal = true this.showInitialFormModal = true
} }
this.closeAlert() this.closeAlert()
this.loadWorkspaces() this.workspacesStore.loadIfEmpty()
this.stateReady = this.user !== null this.stateReady = this.user !== null
}, },
@ -126,9 +139,6 @@ export default {
unmounted () {}, unmounted () {},
methods: { methods: {
...mapActions({
loadWorkspaces: 'open/workspaces/loadIfEmpty'
}),
formGenerated (form) { formGenerated (form) {
this.form = new Form({ ...this.form.data(), ...form }) this.form = new Form({ ...this.form.data(), ...form })
}, },

View File

@ -14,17 +14,20 @@
</template> </template>
<script> <script>
import axios from 'axios' import { computed } from 'vue'
import store from '~/store'
import Breadcrumb from '../../components/common/Breadcrumb.vue'
import Form from 'vform' import Form from 'vform'
import { mapState } from 'vuex' import { useFormsStore } from '../../stores/forms'
import { useWorkingFormStore } from '../../stores/working_form'
import { useWorkspacesStore } from '../../stores/workspaces'
import Breadcrumb from '../../components/common/Breadcrumb.vue'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
const loadForms = function () { const loadForms = function () {
store.commit('open/forms/startLoading') const formsStore = useFormsStore()
store.dispatch('open/workspaces/loadIfEmpty').then(() => { const workspacesStore = useWorkspacesStore()
store.dispatch('open/forms/load', store.state['open/workspaces'].currentId) formsStore.startLoading()
workspacesStore.loadIfEmpty().then(() => {
formsStore.load(workspacesStore.currentId)
}) })
} }
@ -32,12 +35,15 @@ export default {
name: 'EditForm', name: 'EditForm',
components: { Breadcrumb }, components: { Breadcrumb },
mixins: [SeoMeta], mixins: [SeoMeta],
middleware: 'auth',
beforeRouteEnter (to, from, next) { beforeRouteEnter (to, from, next) {
if (!store.getters['open/forms/getBySlug'](to.params.slug)) { const formsStore = useFormsStore()
const workingFormStore = useWorkingFormStore()
if (!formsStore.getBySlug(to.params.slug)) {
loadForms() loadForms()
} }
store.commit('open/working_form/set', null) // Reset old working form workingFormStore.set(null) // Reset old working form
next() next()
}, },
@ -51,7 +57,17 @@ export default {
next() next()
}, },
middleware: 'auth', setup () {
const formsStore = useFormsStore()
const workingFormStore = useWorkingFormStore()
const workspacesStore = useWorkspacesStore()
return {
formsStore,
workingFormStore,
workspacesStore,
formsLoading : computed(() => formsStore.loading)
}
},
data () { data () {
return { return {
@ -62,20 +78,17 @@ export default {
}, },
computed: { computed: {
...mapState({
formsLoading: state => state['open/forms'].loading
}),
updatedForm: { updatedForm: {
get () { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
/* We add a setter */ /* We add a setter */
set (value) { set (value) {
this.$store.commit('open/working_form/set', value) this.workingFormStore.set(value)
} }
}, },
form () { form () {
return this.$store.getters['open/forms/getBySlug'](this.$route.params.slug) return this.formsStore.getBySlug(this.$route.params.slug)
}, },
pageLoaded () { pageLoaded () {
return !this.loading && this.updatedForm !== null return !this.loading && this.updatedForm !== null

View File

@ -50,8 +50,9 @@
<script> <script>
import axios from 'axios' import axios from 'axios'
import store from '~/store' import { computed } from 'vue'
import { mapState } from 'vuex' import { useFormsStore } from '../../stores/forms'
import { useRecordsStore } from '../../stores/records'
import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm.vue' import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm.vue'
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import sha256 from 'js-sha256' import sha256 from 'js-sha256'
@ -93,11 +94,12 @@ function handleTransparentMode (form) {
} }
function loadForm (slug) { function loadForm (slug) {
if (store.state['open/forms'].loading) return const formsStore = useFormsStore()
store.commit('open/forms/startLoading') if (formsStore.loading) return
formsStore.startLoading()
return axios.get('/api/forms/' + slug).then((response) => { return axios.get('/api/forms/' + slug).then((response) => {
const form = response.data const form = response.data
store.commit('open/forms/set', [response.data]) formsStore.set([response.data])
// Custom code injection // Custom code injection
if (form.custom_code) { if (form.custom_code) {
@ -108,9 +110,9 @@ function loadForm (slug) {
handleDarkMode(form) handleDarkMode(form)
handleTransparentMode(form) handleTransparentMode(form)
store.commit('open/forms/stopLoading') formsStore.stopLoading()
}).catch(() => { }).catch(() => {
store.commit('open/forms/stopLoading') formsStore.stopLoading()
}) })
} }
@ -132,6 +134,17 @@ export default {
next() next()
}, },
setup () {
const formsStore = useFormsStore()
const recordsStore = useRecordsStore()
return {
formsStore,
forms : computed(() => formsStore.content),
formLoading : computed(() => formsStore.loading),
recordLoading : computed(() => recordsStore.loading)
}
},
data () { data () {
return { return {
submitted: false submitted: false
@ -166,16 +179,11 @@ export default {
}, },
computed: { computed: {
...mapState({
forms: state => state['open/forms'].content,
formLoading: state => state['open/forms'].loading,
recordLoading: state => state['open/records'].loading
}),
formSlug () { formSlug () {
return this.$route.params.slug return this.$route.params.slug
}, },
form () { form () {
return this.$store.getters['open/forms/getBySlug'](this.formSlug) return this.formsStore.getBySlug(this.formSlug)
}, },
isIframe () { isIframe () {
return window.location !== window.parent.location || window.frameElement return window.location !== window.parent.location || window.frameElement

View File

@ -116,10 +116,12 @@
</template> </template>
<script> <script>
import axios from 'axios' import { computed } from 'vue'
import store from '~/store'
import Form from 'vform' import Form from 'vform'
import {mapGetters, mapState} from 'vuex' import { useAuthStore } from '../../../stores/auth'
import { useFormsStore } from '../../../stores/forms'
import { useWorkingFormStore } from '../../../stores/working_form'
import { useWorkspacesStore } from '../../../stores/workspaces'
import ProTag from '../../../components/common/ProTag.vue' import ProTag from '../../../components/common/ProTag.vue'
import VButton from "../../../components/common/Button.vue"; import VButton from "../../../components/common/Button.vue";
import ExtraMenu from '../../../components/pages/forms/show/ExtraMenu.vue' import ExtraMenu from '../../../components/pages/forms/show/ExtraMenu.vue'
@ -127,9 +129,11 @@ import SeoMeta from '../../../mixins/seo-meta.js'
import FormCleanings from '../../../components/pages/forms/show/FormCleanings.vue' import FormCleanings from '../../../components/pages/forms/show/FormCleanings.vue'
const loadForms = function () { const loadForms = function () {
store.commit('open/forms/startLoading') const formsStore = useFormsStore()
store.dispatch('open/workspaces/loadIfEmpty').then(() => { const workspacesStore = useWorkspacesStore()
store.dispatch('open/forms/loadIfEmpty', store.state['open/workspaces'].currentId) formsStore.startLoading()
workspacesStore.loadIfEmpty().then(() => {
formsStore.loadIfEmpty(workspacesStore.currentId)
}) })
} }
@ -154,6 +158,21 @@ export default {
}, },
middleware: 'auth', middleware: 'auth',
setup () {
const authStore = useAuthStore()
const formsStore = useFormsStore()
const workingFormStore = useWorkingFormStore()
const workspacesStore = useWorkspacesStore()
return {
formsStore,
workingFormStore,
workspacesStore,
user : computed(() => authStore.user),
formsLoading : computed(() => formsStore.loading),
workspacesLoading : computed(() => workspacesStore.loading)
}
},
data() { data() {
return { return {
metaTitle: 'Home', metaTitle: 'Home',
@ -175,27 +194,21 @@ export default {
}, },
computed: { computed: {
...mapGetters({
user: 'auth/user'
}),
...mapState({
formsLoading: state => state['open/forms'].loading,
workspacesLoading: state => state['open/workspaces'].loading
}),
workingForm: { workingForm: {
get() { get () {
return this.$store.state['open/working_form'].content return this.workingFormStore.content
}, },
set(value) { /* We add a setter */
this.$store.commit('open/working_form/set', value) set (value) {
this.workingFormStore.set(value)
} }
}, },
workspace() { workspace() {
if (!this.form) return null if (!this.form) return null
return this.$store.getters['open/workspaces/getById'](this.form.workspace_id) return this.workspacesStore.getById(this.form.workspace_id)
}, },
form() { form() {
return this.$store.getters['open/forms/getBySlug'](this.$route.params.slug) return this.formsStore.getBySlug(this.$route.params.slug)
}, },
formEndpoint: () => '/api/open/forms/{id}', formEndpoint: () => '/api/open/forms/{id}',
loading() { loading() {

View File

@ -104,8 +104,10 @@
</template> </template>
<script> <script>
import store from '~/store' import { computed } from 'vue'
import { mapGetters, mapState } from 'vuex' import { useAuthStore } from '../stores/auth';
import { useFormsStore } from '../stores/forms';
import { useWorkspacesStore } from '../stores/workspaces';
import Fuse from 'fuse.js' import Fuse from 'fuse.js'
import Form from 'vform' import Form from 'vform'
import TextInput from '../components/forms/TextInput.vue' import TextInput from '../components/forms/TextInput.vue'
@ -113,9 +115,11 @@ import OpenFormFooter from '../components/pages/OpenFormFooter.vue'
import ExtraMenu from '../components/pages/forms/show/ExtraMenu.vue' import ExtraMenu from '../components/pages/forms/show/ExtraMenu.vue'
const loadForms = function () { const loadForms = function () {
store.commit('open/forms/startLoading') const formsStore = useFormsStore()
store.dispatch('open/workspaces/loadIfEmpty').then(() => { const workspacesStore = useWorkspacesStore()
store.dispatch('open/forms/loadIfEmpty', store.state['open/workspaces'].currentId) formsStore.startLoading()
workspacesStore.loadIfEmpty().then(() => {
formsStore.loadIfEmpty(workspacesStore.currentId)
}) })
} }
@ -133,6 +137,19 @@ export default {
metaDescription: { type: String, default: 'All of your OpnForm are here. Create new forms, or update your existing one!' } metaDescription: { type: String, default: 'All of your OpnForm are here. Create new forms, or update your existing one!' }
}, },
setup () {
const authStore = useAuthStore()
const formsStore = useFormsStore()
const workspacesStore = useWorkspacesStore()
return {
formsStore,
workspacesStore,
user : computed(() => authStore.user),
forms : computed(() => formsStore.content),
formsLoading : computed(() => formsStore.loading)
}
},
data () { data () {
return { return {
showEditFormModal: false, showEditFormModal: false,
@ -165,19 +182,12 @@ export default {
}, },
computed: { computed: {
...mapGetters({
user: 'auth/user'
}),
...mapState({
forms: state => state['open/forms'].content,
formsLoading: state => state['open/forms'].loading
}),
isFilteringForms () { isFilteringForms () {
return (this.searchForm.search !== '' && this.searchForm.search !== null) || this.selectedTags.length > 0 return (this.searchForm.search !== '' && this.searchForm.search !== null) || this.selectedTags.length > 0
}, },
enrichedForms () { enrichedForms () {
let enrichedForms = this.forms.map((form) => { let enrichedForms = this.forms.map((form) => {
form.workspace = this.$store.getters['open/workspaces/getById'](form.workspace_id) form.workspace = this.workspacesStore.getById(form.workspace_id)
return form return form
}) })
@ -206,7 +216,7 @@ export default {
}) })
}, },
allTags () { allTags () {
return this.$store.getters['open/forms/getAllTags'] return this.formsStore.getAllTags
} }
} }
} }

View File

@ -236,14 +236,14 @@
</template> </template>
<script> <script>
import {mapGetters} from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../stores/auth';
import OpenFormFooter from '../components/pages/OpenFormFooter.vue' import OpenFormFooter from '../components/pages/OpenFormFooter.vue'
import PricingTable from '../components/pages/pricing/PricingTable.vue' import PricingTable from '../components/pages/pricing/PricingTable.vue'
import SeoMeta from '../mixins/seo-meta.js' import SeoMeta from '../mixins/seo-meta.js'
export default { export default {
components: {OpenFormFooter, PricingTable}, components: {OpenFormFooter, PricingTable},
mixins: [SeoMeta], mixins: [SeoMeta],
layout: 'default', layout: 'default',
@ -257,20 +257,23 @@ export default {
next() next()
}, },
setup () {
const authStore = useAuthStore()
return {
user : computed(() => authStore.user),
authenticated : computed(() => authStore.check)
}
},
data: () => ({ data: () => ({
metaTitle: 'Pricing', metaTitle: 'Pricing',
metaDescription: 'All of our core features are free, and there is no quantity limit. You can also created more advanced and customized forms with OpnForms Pro.', metaDescription: 'All of our core features are free, and there is no quantity limit. You can also created more advanced and customized forms with OpnForms Pro.',
}), }),
mounted() { mounted() {},
},
computed: {},
computed: {
...mapGetters({
authenticated: 'auth/check',
user: 'auth/user'
})
},
methods: { methods: {
contactUs() { contactUs() {
window.$crisp.push(['do', 'chat:show']) window.$crisp.push(['do', 'chat:show'])

View File

@ -18,12 +18,20 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import axios from 'axios' import axios from 'axios'
import { useAuthStore } from '../../stores/auth'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
export default { export default {
scrollToTop: false, scrollToTop: false,
mixins: [SeoMeta], mixins: [SeoMeta],
setup () {
const authStore = useAuthStore()
return {
authStore
}
},
data: () => ({ data: () => ({
metaTitle: 'Account', metaTitle: 'Account',
form: new Form({ form: new Form({
@ -39,7 +47,7 @@ export default {
this.loading = false this.loading = false
this.alertSuccess(response.data.message) this.alertSuccess(response.data.message)
// Log out the user. // Log out the user.
await this.$store.dispatch('auth/logout') await this.authStore.logout()
// Redirect to login. // Redirect to login.
this.$router.push({ name: 'login' }) this.$router.push({ name: 'login' })

View File

@ -37,6 +37,8 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import axios from 'axios' import axios from 'axios'
import { useAuthStore } from '../../stores/auth'
import { useWorkspacesStore } from '../../stores/workspaces'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
export default { export default {
@ -45,6 +47,15 @@ export default {
scrollToTop: false, scrollToTop: false,
mixins: [SeoMeta], mixins: [SeoMeta],
setup () {
const authStore = useAuthStore()
const workspacesStore = useWorkspacesStore()
return {
authStore,
workspacesStore
}
},
data: () => ({ data: () => ({
metaTitle: 'Admin', metaTitle: 'Admin',
form: new Form({ form: new Form({
@ -56,21 +67,18 @@ export default {
methods: { methods: {
async impersonate () { async impersonate () {
this.loading = true this.loading = true
this.$store.commit('auth/startImpersonating') this.authStore.startImpersonating()
axios.get('/api/admin/impersonate/' + encodeURI(this.form.identifier)).then(async (response) => { axios.get('/api/admin/impersonate/' + encodeURI(this.form.identifier)).then(async (response) => {
this.loading = false this.loading = false
// Save the token. // Save the token.
this.$store.dispatch('auth/saveToken', { this.authStore.saveToken(response.data.token, false)
token: response.data.token,
remember: false
})
// Fetch the user. // Fetch the user.
await this.$store.dispatch('auth/fetchUser') await this.authStore.fetchUser()
// Redirect to the dashboard. // Redirect to the dashboard.
this.$store.commit('open/workspaces/set', []) this.workspacesStore.set([])
this.$router.push({ name: 'home' }) this.$router.push({ name: 'home' })
}).catch((error) => { }).catch((error) => {
this.alertError(error.response.data.message) this.alertError(error.response.data.message)

View File

@ -21,9 +21,10 @@
<script> <script>
import axios from 'axios' import axios from 'axios'
import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
import VButton from '../../components/common/Button.vue' import VButton from '../../components/common/Button.vue'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
import { mapGetters } from 'vuex'
import AppSumoBilling from '../../components/vendor/appsumo/AppSumoBilling.vue' import AppSumoBilling from '../../components/vendor/appsumo/AppSumoBilling.vue'
export default { export default {
@ -31,6 +32,13 @@ export default {
mixins: [SeoMeta], mixins: [SeoMeta],
scrollToTop: false, scrollToTop: false,
setup () {
const authStore = useAuthStore()
return {
user : computed(() => authStore.user)
}
},
data: () => ({ data: () => ({
metaTitle: 'Billing', metaTitle: 'Billing',
billingLoading: false billingLoading: false
@ -50,10 +58,6 @@ export default {
} }
}, },
computed: { computed: {}
...mapGetters({
user: 'auth/user'
})
}
} }
</script> </script>

View File

@ -38,20 +38,25 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
export default { export default {
middleware: 'auth', middleware: 'auth',
setup () {
const authStore = useAuthStore()
return {
user : computed(() => authStore.user)
}
},
data () { data () {
return { return {
} }
}, },
computed: { computed: {
...mapGetters({
user: 'auth/user'
}),
tabsList () { tabsList () {
const tabs = [ const tabs = [
{ {

View File

@ -24,13 +24,22 @@
<script> <script>
import Form from 'vform' import Form from 'vform'
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
export default { export default {
mixins: [SeoMeta], mixins: [SeoMeta],
scrollToTop: false, scrollToTop: false,
setup () {
const authStore = useAuthStore()
return {
authStore,
user : computed(() => authStore.user)
}
},
data: () => ({ data: () => ({
metaTitle: 'Profile', metaTitle: 'Profile',
form: new Form({ form: new Form({
@ -39,10 +48,6 @@ export default {
}) })
}), }),
computed: mapGetters({
user: 'auth/user'
}),
created () { created () {
// Fill the form with user data. // Fill the form with user data.
this.form.keys().forEach(key => { this.form.keys().forEach(key => {
@ -54,7 +59,7 @@ export default {
async update () { async update () {
const { data } = await this.form.patch('/api/settings/profile') const { data } = await this.form.patch('/api/settings/profile')
this.$store.dispatch('auth/updateUser', { user: data }) this.authStore.updateUser(data)
} }
} }
} }

View File

@ -73,8 +73,10 @@
</template> </template>
<script> <script>
import { computed } from 'vue'
import Form from 'vform' import Form from 'vform'
import {mapActions, mapState} from 'vuex' import { useFormsStore } from '../../stores/forms'
import { useWorkspacesStore } from '../../stores/workspaces'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
export default { export default {
@ -82,6 +84,17 @@ export default {
scrollToTop: false, scrollToTop: false,
mixins: [SeoMeta], mixins: [SeoMeta],
setup () {
const formsStore = useFormsStore()
const workspacesStore = useWorkspacesStore()
return {
formsStore,
workspacesStore,
workspaces: computed(() => workspacesStore.content),
loading: computed(() => workspacesStore.loading)
}
},
data: () => ({ data: () => ({
metaTitle: 'Workspaces', metaTitle: 'Workspaces',
form: new Form({ form: new Form({
@ -92,28 +105,20 @@ export default {
}), }),
mounted() { mounted() {
this.loadWorkspaces() this.workspacesStore.loadIfEmpty()
}, },
computed: { computed: {},
...mapState({
workspaces: state => state['open/workspaces'].content,
loading: state => state['open/workspaces'].loading
})
},
methods: { methods: {
...mapActions({
loadWorkspaces: 'open/workspaces/loadIfEmpty'
}),
switchWorkspace(workspace) { switchWorkspace(workspace) {
this.$store.commit('open/workspaces/setCurrentId', workspace.id) this.workspacesStore.setCurrentId(workspace.id)
this.$router.push({name: 'home'}) this.$router.push({name: 'home'})
this.$store.dispatch('open/forms/load', workspace.id) this.formsStore.load(workspace.id)
}, },
deleteWorkspace(workspace) { deleteWorkspace(workspace) {
this.alertConfirm('Do you really want to delete this workspace? All forms created in this workspace will be removed.', () => { this.alertConfirm('Do you really want to delete this workspace? All forms created in this workspace will be removed.', () => {
this.$store.dispatch('open/workspaces/delete', workspace.id).then(() => { this.workspacesStore.delete(workspace.id).then(() => {
this.alertSuccess('Workspace successfully removed.') this.alertSuccess('Workspace successfully removed.')
}) })
}) })
@ -129,7 +134,7 @@ export default {
}, },
async createWorkspace() { async createWorkspace() {
const {data} = await this.form.post('/api/open/workspaces/create') const {data} = await this.form.post('/api/open/workspaces/create')
this.$store.dispatch('open/workspaces/load') this.workspacesStore.load()
this.workspaceModal = false this.workspaceModal = false
} }
} }

View File

@ -1,7 +1,8 @@
<template /> <template />
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
export default { export default {
@ -10,6 +11,13 @@ export default {
middleware: 'auth', middleware: 'auth',
mixins: [SeoMeta], mixins: [SeoMeta],
setup () {
const authStore = useAuthStore()
return {
authenticated : computed(() => authStore.check),
}
},
data: () => ({ data: () => ({
metaTitle: 'Error', metaTitle: 'Error',
}), }),
@ -19,10 +27,6 @@ export default {
this.alertError('Unfortunately we could not confirm your subscription. Please try again and contact us if the issue persists.') this.alertError('Unfortunately we could not confirm your subscription. Please try again and contact us if the issue persists.')
}, },
computed: { computed: {}
...mapGetters({
authenticated: 'auth/check'
})
}
} }
</script> </script>

View File

@ -16,7 +16,8 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
import OpenFormFooter from '../../components/pages/OpenFormFooter.vue' import OpenFormFooter from '../../components/pages/OpenFormFooter.vue'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
@ -26,6 +27,15 @@ export default {
layout: 'default', layout: 'default',
middleware: 'auth', middleware: 'auth',
setup () {
const authStore = useAuthStore()
return {
authStore,
authenticated : computed(() => authStore.check),
user : computed(() => authStore.user)
}
},
data: () => ({ data: () => ({
metaTitle: 'Subscription Success', metaTitle: 'Subscription Success',
interval: null interval: null
@ -43,7 +53,7 @@ export default {
methods: { methods: {
async checkSubscription () { async checkSubscription () {
// Fetch the user. // Fetch the user.
await this.$store.dispatch('auth/fetchUser') await this.authStore.fetchUser()
this.redirectIfSubscribed() this.redirectIfSubscribed()
}, },
redirectIfSubscribed () { redirectIfSubscribed () {
@ -63,11 +73,6 @@ export default {
} }
}, },
computed: { computed: {}
...mapGetters({
authenticated: 'auth/check',
user: 'auth/user'
})
}
} }
</script> </script>

View File

@ -93,19 +93,21 @@
</template> </template>
<script> <script>
import store from '~/store'
import Form from 'vform' import Form from 'vform'
import Fuse from 'fuse.js' import Fuse from 'fuse.js'
import { mapGetters, mapState } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
import { useTemplatesStore } from '../../stores/templates'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
import OpenFormFooter from '../../components/pages/OpenFormFooter.vue' import OpenFormFooter from '../../components/pages/OpenFormFooter.vue'
import Breadcrumb from '../../components/common/Breadcrumb.vue' import Breadcrumb from '../../components/common/Breadcrumb.vue'
import SingleTemplate from '../../components/pages/templates/SingleTemplate.vue' import SingleTemplate from '../../components/pages/templates/SingleTemplate.vue'
const loadTemplates = function () { const loadTemplates = function () {
store.commit('open/templates/startLoading') const templatesStore = useTemplatesStore()
store.dispatch('open/templates/loadIfEmpty').then(() => { templatesStore.startLoading()
store.commit('open/templates/stopLoading') templatesStore.loadIfEmpty().then(() => {
templatesStore.stopLoading()
}) })
} }
@ -118,6 +120,20 @@ export default {
next() next()
}, },
setup () {
const authStore = useAuthStore()
const templatesStore = useTemplatesStore()
return {
authStore,
authenticated : computed(() => authStore.check),
user : computed(() => authStore.user),
templates : computed(() => templatesStore.content),
templatesLoading : computed(() => templatesStore.loading),
industries : computed(() => templatesStore.industries),
types : computed(() => templatesStore.types)
}
},
data () { data () {
return { return {
selectedType: 'all', selectedType: 'all',
@ -130,16 +146,6 @@ export default {
mounted () {}, mounted () {},
computed: { computed: {
...mapGetters({
authenticated: 'auth/check',
user: 'auth/user'
}),
...mapState({
templates: state => state['open/templates'].content,
templatesLoading: state => state['open/templates'].loading,
industries: state => state['open/templates'].industries,
types: state => state['open/templates'].types
}),
breadcrumbs () { breadcrumbs () {
if (!this.industry) { if (!this.industry) {
return [{ route: { name: 'templates' }, label: 'Templates' }] return [{ route: { name: 'templates' }, label: 'Templates' }]

View File

@ -196,9 +196,10 @@
</template> </template>
<script> <script>
import store from '~/store'
import Form from 'vform' import Form from 'vform'
import { mapGetters, mapState } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
import { useTemplatesStore } from '../../stores/templates'
import OpenFormFooter from '../../components/pages/OpenFormFooter.vue' import OpenFormFooter from '../../components/pages/OpenFormFooter.vue'
import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm.vue' import OpenCompleteForm from '../../components/open/forms/OpenCompleteForm.vue'
import Breadcrumb from '../../components/common/Breadcrumb.vue' import Breadcrumb from '../../components/common/Breadcrumb.vue'
@ -213,21 +214,32 @@ export default {
mixins: [SeoMeta], mixins: [SeoMeta],
beforeRouteEnter (to, from, next) { beforeRouteEnter (to, from, next) {
const templatesStore = useTemplatesStore()
if (to.params?.slug) { if (to.params?.slug) {
store.dispatch('open/templates/loadTemplate', to.params?.slug) templatesStore.loadTemplate(to.params?.slug)
store.dispatch('open/templates/loadTypesAndIndustries') templatesStore.loadTypesAndIndustries()
} }
next() next()
}, },
setup () {
const authStore = useAuthStore()
const templatesStore = useTemplatesStore()
return {
templatesStore,
authenticated : computed(() => authStore.check),
user : computed(() => authStore.user),
templatesLoading : computed(() => templatesStore.loading)
}
},
data () { data () {
return { return {
showFormTemplateModal: false showFormTemplateModal: false
} }
}, },
mounted () { mounted () {},
},
methods: { methods: {
cleanQuotes (str) { cleanQuotes (str) {
@ -247,13 +259,6 @@ export default {
}, },
computed: { computed: {
...mapGetters({
authenticated: 'auth/check',
user: 'auth/user'
}),
...mapState({
templatesLoading: state => state['open/templates'].loading
}),
breadcrumbs () { breadcrumbs () {
if (!this.template) { if (!this.template) {
return [{ route: { name: 'templates' }, label: 'Templates' }] return [{ route: { name: 'templates' }, label: 'Templates' }]
@ -261,7 +266,7 @@ export default {
return [{ route: { name: 'templates' }, label: 'Templates' }, { label: this.template.name }] return [{ route: { name: 'templates' }, label: 'Templates' }, { label: this.template.name }]
}, },
template () { template () {
return this.$store.getters['open/templates/getBySlug'](this.$route.params.slug) return this.templatesStore.getBySlug(this.$route.params.slug)
}, },
form () { form () {
return this.template ? new Form(this.template.structure) : null return this.template ? new Form(this.template.structure) : null

View File

@ -93,19 +93,21 @@
</template> </template>
<script> <script>
import store from '~/store'
import Form from 'vform' import Form from 'vform'
import Fuse from 'fuse.js' import Fuse from 'fuse.js'
import { mapGetters, mapState } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
import { useTemplatesStore } from '../../stores/templates'
import SeoMeta from '../../mixins/seo-meta.js' import SeoMeta from '../../mixins/seo-meta.js'
import OpenFormFooter from '../../components/pages/OpenFormFooter.vue' import OpenFormFooter from '../../components/pages/OpenFormFooter.vue'
import Breadcrumb from '../../components/common/Breadcrumb.vue' import Breadcrumb from '../../components/common/Breadcrumb.vue'
import SingleTemplate from '../../components/pages/templates/SingleTemplate.vue' import SingleTemplate from '../../components/pages/templates/SingleTemplate.vue'
const loadTemplates = function () { const loadTemplates = function () {
store.commit('open/templates/startLoading') const templatesStore = useTemplatesStore()
store.dispatch('open/templates/loadIfEmpty').then(() => { templatesStore.startLoading()
store.commit('open/templates/stopLoading') templatesStore.loadIfEmpty().then(() => {
templatesStore.stopLoading()
}) })
} }
@ -118,6 +120,19 @@ export default {
next() next()
}, },
setup () {
const authStore = useAuthStore()
const templatesStore = useTemplatesStore()
return {
authenticated : computed(() => authStore.check),
user : computed(() => authStore.user),
templates : computed(() => templatesStore.content),
templatesLoading : computed(() => templatesStore.loading),
industries : computed(() => templatesStore.industries),
types : computed(() => templatesStore.types)
}
},
data () { data () {
return { return {
selectedIndustry: 'all', selectedIndustry: 'all',
@ -130,16 +145,6 @@ export default {
mounted () {}, mounted () {},
computed: { computed: {
...mapGetters({
authenticated: 'auth/check',
user: 'auth/user'
}),
...mapState({
templates: state => state['open/templates'].content,
templatesLoading: state => state['open/templates'].loading,
industries: state => state['open/templates'].industries,
types: state => state['open/templates'].types
}),
breadcrumbs () { breadcrumbs () {
if (!this.type) { if (!this.type) {
return [{ route: { name: 'templates' }, label: 'Templates' }] return [{ route: { name: 'templates' }, label: 'Templates' }]

View File

@ -181,7 +181,8 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { computed } from 'vue'
import { useAuthStore } from '../stores/auth'
import Features from '~/components/pages/welcome/Features.vue' import Features from '~/components/pages/welcome/Features.vue'
import MoreFeatures from '~/components/pages/welcome/MoreFeatures.vue' import MoreFeatures from '~/components/pages/welcome/MoreFeatures.vue'
import PricingTable from '../components/pages/pricing/PricingTable.vue' import PricingTable from '../components/pages/pricing/PricingTable.vue'
@ -193,11 +194,16 @@ import SeoMeta from '../mixins/seo-meta.js'
export default { export default {
components: { Testimonials, OpenFormFooter, Features, MoreFeatures, PricingTable, AiFeature, TemplatesSlider }, components: { Testimonials, OpenFormFooter, Features, MoreFeatures, PricingTable, AiFeature, TemplatesSlider },
mixins: [SeoMeta], mixins: [SeoMeta],
layout: 'default', layout: 'default',
setup () {
const authStore = useAuthStore()
return {
authenticated : computed(() => authStore.check)
}
},
data: () => ({ data: () => ({
title: window.config.appName, title: window.config.appName,
metaTitle: 'Create beautiful & open-source forms for free' metaTitle: 'Create beautiful & open-source forms for free'
@ -215,9 +221,6 @@ export default {
}, },
computed: { computed: {
...mapGetters({
authenticated: 'auth/check'
}),
configLinks: () => window.config.links, configLinks: () => window.config.links,
paidPlansEnabled () { paidPlansEnabled () {
return window.config.paid_plans_enabled return window.config.paid_plans_enabled

View File

@ -1,5 +1,5 @@
import axios from 'axios' import axios from 'axios'
import store from '~/store' import { useAuthStore } from '../stores/auth';
import router from '~/router' import router from '~/router'
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
@ -17,16 +17,13 @@ function addPasswordToFormRequest (request) {
// Request interceptor // Request interceptor
axios.interceptors.request.use(request => { axios.interceptors.request.use(request => {
const token = store.getters['auth/token'] const authStore = useAuthStore()
const token = authStore.token
if (token) { if (token) {
request.headers.common.Authorization = `Bearer ${token}` request.headers.common.Authorization = `Bearer ${token}`
} }
const locale = store.getters['lang/locale'] request.headers.common['Accept-Language'] = 'en-US'
if (locale) {
request.headers.common['Accept-Language'] = locale
}
// request.headers['X-Socket-Id'] = Echo.socketId() // request.headers['X-Socket-Id'] = Echo.socketId()
request = addPasswordToFormRequest(request) request = addPasswordToFormRequest(request)
@ -36,14 +33,14 @@ axios.interceptors.request.use(request => {
// Response interceptor // Response interceptor
axios.interceptors.response.use(response => response, error => { axios.interceptors.response.use(response => response, error => {
const authStore = useAuthStore()
const { status } = error.response const { status } = error.response
if (status >= 500) { if (status >= 500) {
console.log(status) console.log(status)
} }
if (status === 401 && store.getters['auth/check']) { if (status === 401 && authStore.check) {
store.commit('auth/LOGOUT') authStore.logout()
router.push({ name: 'login' }) router.push({ name: 'login' })
} }

View File

@ -1,7 +1,7 @@
import routes from './routes' import routes from './routes'
import { createWebHistory, createRouter } from 'vue-router' import { createWebHistory, createRouter } from 'vue-router'
import * as Sentry from '@sentry/vue' import * as Sentry from '@sentry/vue'
import store from '../store' import { useAppStore } from '../stores/app'
import { defineComponent, nextTick } from 'vue' import { defineComponent, nextTick } from 'vue'
// The middleware for every page of the application. // The middleware for every page of the application.
@ -45,6 +45,8 @@ async function getMatchedComponents (to) {
* @param {Function} next * @param {Function} next
*/ */
async function beforeEach (to, from, next) { async function beforeEach (to, from, next) {
const appStore = useAppStore()
// Sentry tracking // Sentry tracking
if (window.config.sentry_dsn) { if (window.config.sentry_dsn) {
Sentry.configureScope((scope) => scope.setTransactionName(to?.name || 'Unknown route name')) Sentry.configureScope((scope) => scope.setTransactionName(to?.name || 'Unknown route name'))
@ -75,7 +77,7 @@ async function beforeEach (to, from, next) {
// Start the loading bar. // Start the loading bar.
if (components[components.length - 1].loading !== false) { if (components[components.length - 1].loading !== false) {
nextTick(() => store.commit('app/loaderStart')) nextTick(() => appStore.loaderStart())
} }
// Get the middleware for all the matched components. // Get the middleware for all the matched components.
@ -86,11 +88,11 @@ async function beforeEach (to, from, next) {
// Set the application layout only if "next()" was called with no args. // Set the application layout only if "next()" was called with no args.
if (args.length === 0) { if (args.length === 0) {
if (components[0].layout) { if (components[0].layout) {
store.commit('app/setLayout', components[0].layout) appStore.setLayout(components[0].layout)
} else if (components[0].default && components[0].default.layout) { } else if (components[0].default && components[0].default.layout) {
store.commit('app/setLayout', components[0].default.layout) appStore.setLayout(components[0].default.layout)
} else { } else {
store.commit('app/setLayout', null) appStore.setLayout(null)
} }
} }
@ -106,7 +108,8 @@ async function beforeEach (to, from, next) {
* @param {Function} next * @param {Function} next
*/ */
async function afterEach (to, from, next) { async function afterEach (to, from, next) {
nextTick(() => store.commit('app/loaderFinish')) const appStore = useAppStore()
nextTick(() => appStore.loaderFinish())
} }
/** /**
@ -118,13 +121,14 @@ async function afterEach (to, from, next) {
* @param {Function} next * @param {Function} next
*/ */
function callMiddleware (middleware, to, from, next) { function callMiddleware (middleware, to, from, next) {
const appStore = useAppStore()
const stack = middleware.reverse() const stack = middleware.reverse()
const _next = (...args) => { const _next = (...args) => {
// Stop if "_next" was called with an argument or the stack is empty. // Stop if "_next" was called with an argument or the stack is empty.
if (args.length > 0 || stack.length === 0) { if (args.length > 0 || stack.length === 0) {
if (args.length > 0) { if (args.length > 0) {
store.commit('app/loaderFinish') appStore.loaderFinish()
} }
return next(...args) return next(...args)

View File

@ -1,22 +0,0 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// Load store modules dynamically.
const requireContext = import.meta.glob('./modules/**/*.js', {eager: true})
const modules = Object.keys(requireContext)
.map(file =>
[file.replace(/(^.\/)|(\.js$)/g, '').replace('modules/',''), requireContext[file]]
)
.reduce((modules, [name, module]) => {
if (module.namespaced === undefined) {
module = {...module, namespaced: true}
}
return { ...modules, [name]: module }
}, {})
export default new Vuex.Store({
modules
})

View File

@ -1,73 +0,0 @@
import { nextTick } from 'vue'
export const state = {
layout: 'default',
// App Loader
loader: {
percent: 0,
show: false,
canSuccess: true,
duration: 3000,
_timer: null,
_cut: null
}
}
export const mutations = {
setLayout (state, layout) {
state.layout = layout ?? 'default'
},
loaderStart (state) {
state.loader.show = true
state.loader.canSuccess = true
if (state.loader._timer) {
clearInterval(state.loader._timer)
state.loader.percent = 0
}
state.loader._cut = 10000 / Math.floor(state.loader.duration)
},
loaderIncrease (state, num) {
state.loader.percent = state.loader.percent + Math.floor(num)
},
loaderDecrease (state, num) {
state.loader.percent = state.loader.percent - Math.floor(num)
},
loaderFinish (state) {
state.loader.percent = 100
mutations.loaderHide(state)
},
loaderSetTimer (state, timerVal) {
state._timer = timerVal
},
loaderPause (state) {
clearInterval(state.loader._timer)
},
loaderHide (state) {
clearInterval(state.loader._timer)
state.loader._timer = null
setTimeout(() => {
state.loader.show = false
nextTick(() => {
setTimeout(() => {
state.loader.percent = 0
}, 200)
})
}, 500)
},
loaderFail () {
state.loader.canSuccess = false
}
}
export const actions = {
loaderStart ({ commit, dispatch }) {
mutations.loaderStart()
mutations.loaderSetTimer(setInterval(() => {
mutations.loaderIncrease(state.loader._cut * Math.random())
if (state.loader.percent > 95) {
mutations.loaderFinish()
}
}, 100))
}
}

View File

@ -1,104 +0,0 @@
import axios from 'axios'
import Cookies from 'js-cookie'
import * as types from '../mutation-types'
// state
export const state = {
user: null,
token: Cookies.get('token'),
// For admin impersonation
admin_token: Cookies.get('admin_token') ?? null
}
// getters
export const getters = {
user: state => state.user,
token: state => state.token,
check: state => state.user !== null,
isImpersonating: state => state.admin_token !== null
}
// mutations
export const mutations = {
[types.SAVE_TOKEN] (state, { token, remember }) {
state.token = token
Cookies.set('token', token, { expires: remember ? 365 : null })
},
[types.FETCH_USER_SUCCESS] (state, { user }) {
state.user = user
},
[types.FETCH_USER_FAILURE] (state) {
state.token = null
Cookies.remove('token')
},
[types.LOGOUT] (state) {
state.user = null
state.token = null
Cookies.remove('token')
},
[types.UPDATE_USER] (state, { user }) {
state.user = user
},
// Stores admin token temporarily for impersonation
startImpersonating (state) {
state.admin_token = state.token
Cookies.set('admin_token', state.token, { expires: 365 })
},
// Stores admin token temporarily for impersonation
stopImpersonating (state) {
state.token = state.admin_token
state.admin_token = null
Cookies.set('token', state.token, { expires: 365 })
Cookies.remove('admin_token')
}
}
// actions
export const actions = {
saveToken ({ commit, dispatch }, payload) {
commit(types.SAVE_TOKEN, payload)
},
async fetchUser ({ commit }) {
try {
const { data } = await axios.get('/api/user')
commit(types.FETCH_USER_SUCCESS, { user: data })
return data
} catch (e) {
commit(types.FETCH_USER_FAILURE)
}
},
updateUser ({ commit }, payload) {
commit(types.UPDATE_USER, payload)
},
async logout ({ commit }) {
try {
await axios.post('/api/logout')
} catch (e) { }
commit(types.LOGOUT)
},
async fetchOauthUrl (ctx, { provider }) {
const { data } = await axios.post(`/api/oauth/${provider}`)
return data.url
},
// Reverse admin impersonation
stopImpersonating ({ commit, dispatch }, payload) {
commit('stopImpersonating')
return dispatch('fetchUser')
}
}

View File

@ -1,35 +0,0 @@
import Vue from 'vue'
export const namespaced = true
// state
export const state = {
content: []
}
// getters
export const getters = {
getById: (state) => (id) => {
if (state.content.length === 0) return null
return state.content.find(item => item.id === id)
}
}
// mutations
export const mutations = {
set (state, items) {
state.content = items
},
addOrUpdate (state, item) {
state.content = state.content.filter((val) => val.id !== item.id)
state.content.push(item)
},
remove (state, item) {
state.content = state.content.filter((val) => val.id !== item.id)
}
}
// actions
export const actions = {
}

View File

@ -1,27 +0,0 @@
import Vue from 'vue'
export const namespaced = true
// state
export const state = {
content: null
}
// getters
export const getters = {
}
// mutations
export const mutations = {
set (state, error) {
state.content = error
},
clear (state) {
state.content = null
}
}
// actions
export const actions = {
}

View File

@ -1,85 +0,0 @@
import axios from 'axios'
export const formsEndpoint = '/api/open/workspaces/{workspaceId}/forms'
export const namespaced = true
export let currentPage = 1
// state
export const state = {
content: [],
loading: false
}
// getters
export const getters = {
getById: (state) => (id) => {
if (state.content.length === 0) return null
return state.content.find(item => item.id === id)
},
getBySlug: (state) => (slug) => {
if (state.content.length === 0) return null
return state.content.find(item => item.slug === slug)
},
getAllTags: (state) => {
if (state.content.length === 0) return []
let allTags = []
state.content.forEach(form => {
if(form.tags && form.tags.length > 0){
allTags = allTags.concat(form.tags)
}
})
return allTags.filter((item, i, ar) => ar.indexOf(item) === i)
}
}
// mutations
export const mutations = {
set (state, items) {
state.content = items
},
append (state, items) {
state.content = state.content.concat(items)
},
addOrUpdate (state, item) {
state.content = state.content.filter((val) => val.id !== item.id)
state.content.push(item)
},
remove (state, item) {
state.content = state.content.filter((val) => val.id !== item.id)
},
startLoading (state) {
state.loading = true
},
stopLoading (state) {
state.loading = false
}
}
// actions
export const actions = {
resetState (context) {
context.commit('set', [])
context.commit('stopLoading')
currentPage = 1
},
load (context, workspaceId) {
context.commit('startLoading')
return axios.get(formsEndpoint.replace('{workspaceId}', workspaceId)+'?page='+currentPage).then((response) => {
context.commit((currentPage == 1) ? 'set' : 'append', response.data.data)
if (currentPage < response.data.meta.last_page) {
currentPage += 1
context.dispatch('load', workspaceId)
} else {
context.commit('stopLoading')
currentPage = 1
}
})
},
loadIfEmpty (context, workspaceId) {
if (context.state.content.length === 0) {
return context.dispatch('load', workspaceId)
}
context.commit('stopLoading')
return Promise.resolve()
}
}

View File

@ -1,58 +0,0 @@
import axios from 'axios'
export const namespaced = true
export const workspaceEndpoint = '/api/open/records/'
/**
* Loads records from database
*/
// state
export const state = {
content: [],
loading: false
}
// getters
export const getters = {
getById: (state) => (id) => {
if (state.content.length === 0) return null
return state.content.find(item => item.submission_id === id)
}
}
// mutations
export const mutations = {
set (state, items) {
state.content = items
},
addOrUpdate (state, item) {
state.content = state.content.filter((val) => val.id !== item.id)
state.content.push(item)
},
remove (state, itemId) {
state.content = state.content.filter((val) => val.id !== itemId)
},
startLoading () {
state.loading = true
},
stopLoading () {
state.loading = false
}
}
// actions
export const actions = {
resetState (context) {
context.commit('set', [])
context.commit('stopLoading')
},
loadRecord (context, request) {
context.commit('set', [])
context.commit('startLoading')
return request.then((data) => {
context.commit('addOrUpdate', data)
context.commit('stopLoading')
})
}
}

View File

@ -1,126 +0,0 @@
import axios from 'axios'
export const templatesEndpoint = '/api/templates'
export const namespaced = true
// state
export const state = {
content: [],
industries: {},
types: {},
allLoaded: false,
loading: false
}
// getters
export const getters = {
getBySlug: (state) => (slug) => {
if (state.content.length === 0) return null
return state.content.find(item => item.slug === slug)
},
getTemplateTypes: (state) => (slugs) => {
if (state.types.length === 0) return null
return Object.values(state.types).filter((val) => slugs.includes(val.slug)).map((item) => { return item.name })
},
getTemplateIndustries: (state) => (slugs) => {
if (state.industries.length === 0) return null
return Object.values(state.industries).filter((val) => slugs.includes(val.slug)).map((item) => { return item.name })
}
}
// mutations
export const mutations = {
set (state, items) {
state.content = items
state.allLoaded = true
},
append (state, items) {
const ids = items.map((item) => { return item.id })
state.content = state.content.filter((val) => !ids.includes(val.id))
state.content = state.content.concat(items)
},
addOrUpdate (state, item) {
state.content = state.content.filter((val) => val.id !== item.id)
state.content.push(item)
},
remove (state, item) {
state.content = state.content.filter((val) => val.id !== item.id)
},
startLoading (state) {
state.loading = true
},
stopLoading (state) {
state.loading = false
},
setAllLoaded (state, val) {
state.allLoaded = val
}
}
// actions
export const actions = {
resetState (context) {
context.commit('set', [])
context.commit('stopLoading')
},
loadTypesAndIndustries (context) {
if (Object.keys(context.state.industries).length === 0) {
import('@/data/forms/templates/industries.json').then((module) => {
context.state.industries = module.default
})
}
if (Object.keys(context.state.types).length === 0) {
import('@/data/forms/templates/types.json').then((module) => {
context.state.types = module.default
})
}
},
loadTemplate (context, slug) {
context.commit('startLoading')
context.dispatch('loadTypesAndIndustries')
if (context.getters.getBySlug(slug)) {
context.commit('stopLoading')
return Promise.resolve()
}
return axios.get(templatesEndpoint + '/' + slug).then((response) => {
context.commit('addOrUpdate', response.data)
context.commit('stopLoading')
}).catch((error) => {
context.commit('stopLoading')
})
},
loadAll (context, options=null) {
context.commit('startLoading')
context.dispatch('loadTypesAndIndustries')
// Prepare with options
let queryStr = ''
if(options !== null){
for (const [key, value] of Object.entries(options)) {
queryStr += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(value)
}
queryStr = queryStr.slice(1)
}
return axios.get((queryStr) ? templatesEndpoint + '?' + queryStr : templatesEndpoint).then((response) => {
if(options !== null){
context.commit('set', response.data)
context.commit('setAllLoaded', false)
} else {
context.commit('append', response.data)
context.commit('setAllLoaded', true)
}
context.commit('stopLoading')
}).catch((error) => {
context.commit('stopLoading')
})
},
loadIfEmpty (context) {
if (!context.state.allLoaded) {
return context.dispatch('loadAll')
}
context.commit('stopLoading')
return Promise.resolve()
}
}

View File

@ -1,49 +0,0 @@
export const namespaced = true
// state
export const state = {
content: null,
// Field being edited
selectedFieldIndex: null,
showEditFieldSidebar: null,
showAddFieldSidebar: null
}
// mutations
export const mutations = {
set (state, form) {
state.content = form
},
setProperties (state, properties) {
state.content.properties = properties
},
openSettingsForField (state, index) {
// If field is passed, compute index
if (typeof index === 'object') {
index = state.content.properties.findIndex(prop => prop.id === index.id)
}
state.selectedFieldIndex = index
state.showEditFieldSidebar = true
state.showAddFieldSidebar = false
},
closeEditFieldSidebar (state) {
state.selectedFieldIndex = null
state.showEditFieldSidebar = false
state.showAddFieldSidebar = false
},
openAddFieldSidebar (state, index) {
// If field is passed, compute index
if (index !== null && typeof index === 'object') {
index = state.content.properties.findIndex(prop => prop.id === index.id)
}
state.selectedFieldIndex = index
state.showAddFieldSidebar = true
state.showEditFieldSidebar = false
},
closeAddFieldSidebar (state) {
state.selectedFieldIndex = null
state.showAddFieldSidebar = false
state.showEditFieldSidebar = false
},
}

View File

@ -1,100 +0,0 @@
import Vue from 'vue'
import axios from 'axios'
export const namespaced = true
export const workspaceEndpoint = '/api/open/workspaces/'
const localStorageCurrentWorkspaceKey = 'currentWorkspace'
// state
export const state = {
content: [],
currentId: null,
loading: false
}
// getters
export const getters = {
getById: (state) => (id) => {
if (state.content.length === 0) return null
return state.content.find(item => item.id === id)
},
getCurrent: (state) => () => {
if (state.content.length === 0 || state.currentId === null) return null
return state.content.find(item => item.id === state.currentId)
}
}
// mutations
export const mutations = {
set (state, items) {
state.content = items
if (state.currentId == null && state.content.length > 0) {
// If one only, set it
if (state.content.length === 1) {
state.currentId = items[0].id
localStorage.setItem(localStorageCurrentWorkspaceKey, state.currentId)
} else if (localStorage.getItem(localStorageCurrentWorkspaceKey) && state.content.find(item => item.id === parseInt(localStorage.getItem(localStorageCurrentWorkspaceKey)))) {
// Check local storage for current workspace, or take first
state.currentId = parseInt(localStorage.getItem(localStorageCurrentWorkspaceKey))
localStorage.setItem(localStorageCurrentWorkspaceKey, state.currentId)
} else {
// Else, take first
state.currentId = items[0].id
localStorage.setItem(localStorageCurrentWorkspaceKey, state.currentId)
}
} else {
localStorage.removeItem(localStorageCurrentWorkspaceKey)
}
},
setCurrentId (state, id) {
state.currentId = id
localStorage.setItem(localStorageCurrentWorkspaceKey, id)
},
addOrUpdate (state, item) {
state.content = state.content.filter((val) => val.id !== item.id)
state.content.push(item)
if (state.currentId == null) {
state.currentId = item.id
localStorage.setItem(localStorageCurrentWorkspaceKey, state.currentId)
}
},
remove (state, itemId) {
state.content = state.content.filter((val) => val.id !== itemId)
},
startLoading () {
state.loading = true
},
stopLoading () {
state.loading = false
}
}
// actions
export const actions = {
resetState (context) {
context.commit('set', [])
context.commit('stopLoading')
},
load (context) {
context.commit('set', [])
context.commit('startLoading')
return axios.get(workspaceEndpoint).then((response) => {
context.commit('set', response.data)
context.commit('stopLoading')
})
},
loadIfEmpty ({ context, dispatch, state }) {
if (state.content.length === 0) {
return dispatch('load')
}
return Promise.resolve()
},
delete ({ commit, dispatch, state }, id) {
commit('startLoading')
return axios.delete(workspaceEndpoint + id).then((response) => {
commit('remove', response.data.workspace_id)
commit('stopLoading')
})
}
}

View File

@ -1,10 +0,0 @@
// auth.js
export const LOGOUT = 'LOGOUT'
export const SAVE_TOKEN = 'SAVE_TOKEN'
export const FETCH_USER = 'FETCH_USER'
export const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS'
export const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE'
export const UPDATE_USER = 'UPDATE_USER'
// lang.js
export const SET_LOCALE = 'SET_LOCALE'

71
resources/js/stores/app.js vendored Normal file
View File

@ -0,0 +1,71 @@
import { defineStore } from 'pinia'
import { nextTick } from 'vue'
export const useAppStore = defineStore('app', {
state: () => ({
layout: 'default',
// App Loader
loader: {
percent: 0,
show: false,
canSuccess: true,
duration: 3000,
_timer: null,
_cut: null
}
}),
actions: {
setLayout (layout) {
this.layout = layout ?? 'default'
},
loaderIncrease (num) {
this.loader.percent = this.loader.percent + Math.floor(num)
},
loaderDecrease (num) {
this.loader.percent = this.loader.percent - Math.floor(num)
},
loaderFinish () {
this.loader.percent = 100
this.loaderHide()
},
loaderSetTimer (timerVal) {
this._timer = timerVal
},
loaderPause () {
clearInterval(this.loader._timer)
},
loaderHide () {
clearInterval(this.loader._timer)
this.loader._timer = null
setTimeout(() => {
this.loader.show = false
nextTick(() => {
setTimeout(() => {
this.loader.percent = 0
}, 200)
})
}, 500)
},
loaderFail () {
this.loader.canSuccess = false
},
loaderStart () {
this.loader.show = true
this.loader.canSuccess = true
if (this.loader._timer) {
clearInterval(this.loader._timer)
this.loader.percent = 0
}
this.loader._cut = 10000 / Math.floor(this.loader.duration)
this.loaderSetTimer(setInterval(() => {
this.loaderIncrease(this.loader._cut * Math.random())
if (this.loader.percent > 95) {
this.loaderFinish()
}
}, 100))
}
}
})

67
resources/js/stores/auth.js vendored Normal file
View File

@ -0,0 +1,67 @@
import { defineStore } from 'pinia'
import axios from 'axios'
import Cookies from 'js-cookie'
export const useAuthStore = defineStore('auth', {
state: () => ({
user: null,
token: Cookies.get('token'),
// For admin impersonation
admin_token: Cookies.get('admin_token') ?? null
}),
getters: {
check: (state) => (state.user !== null && state.user !== undefined),
isImpersonating: (state) => (state.admin_token !== null && state.admin_token !== undefined)
},
actions: {
// Stores admin token temporarily for impersonation
startImpersonating () {
this.admin_token = this.token
Cookies.set('admin_token', this.token, { expires: 365 })
},
// Stop admin impersonation
stopImpersonating () {
this.token = this.admin_token
this.admin_token = null
Cookies.set('token', this.token, { expires: 365 })
Cookies.remove('admin_token')
this.fetchUser()
},
saveToken (token, remember) {
this.token = token
Cookies.set('token', token, { expires: remember ? 365 : null })
},
async fetchUser () {
try {
const { data } = await axios.get('/api/user')
this.user = data
return data
} catch (e) {
this.token = null
Cookies.remove('token')
}
},
updateUser (payload) {
this.user = payload
},
async logout () {
try {
await axios.post('/api/logout')
} catch (e) { }
this.user = null
this.token = null
Cookies.remove('token')
},
async fetchOauthUrl (provider) {
const { data } = await axios.post(`/api/oauth/${provider}`)
return data.url
}
}
})

17
resources/js/stores/errors.js vendored Normal file
View File

@ -0,0 +1,17 @@
import { defineStore } from 'pinia'
export const namespaced = true
export const useErrorsStore = defineStore('errors', {
state: () => ({
content: null
}),
actions: {
set (error) {
this.content = error
},
clear () {
this.content = null
}
}
})

Some files were not shown because too many files have changed in this diff Show More