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

9
resources/js/app.js vendored
View File

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

View File

@ -37,6 +37,8 @@
</template>
<script>
import { computed } from 'vue'
import { useAppStore } from '../stores/app'
import Loading from './Loading.vue'
import Hotjar from './service/Hotjar.vue'
import Amplitude from './service/Amplitude.vue'
@ -44,7 +46,6 @@ import Crisp from './service/Crisp.vue'
import StopImpersonation from './pages/StopImpersonation.vue'
import Notifications from './common/Notifications.vue'
import SeoMeta from '../mixins/seo-meta.js'
import { mapState } from 'vuex'
// Load layout components dynamically.
const requireContext = import.meta.glob('../layouts/**.vue', { eager: true })
@ -74,6 +75,13 @@ export default {
mixins: [SeoMeta],
setup () {
const appStore = useAppStore()
return {
layout : computed(() => appStore.layout)
}
},
data: () => ({
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.',
@ -95,9 +103,6 @@ export default {
isOnboardingPage () {
return this.$route.name === 'onboarding'
},
...mapState({
layout: state => state.app.layout
}),
layoutComponent () {
return layouts[this.layout]
}

View File

@ -10,7 +10,8 @@
<script>
// 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 {
data: () => ({
@ -19,12 +20,13 @@ export default {
failedColor: 'red'
}),
computed: {
...mapState({
percent: state => state.app.loader.percent,
canSuccess: state => state.app.loader.canSuccess,
show: state => state.app.loader.show
})
setup () {
const appStore = useAppStore()
return {
percent : computed(() => appStore.loader.percent),
canSuccess : computed(() => appStore.loader.canSuccess),
show : computed(() => appStore.loader.show)
}
}
}
</script>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@
:data="countries"
:disabled="disabled || countries.length===1" :searchable="true" :search-keys="['name']" :option-key="'code'" :color="color"
: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">
<div class="flex items-center space-x-2 hover:text-white">
@ -29,7 +29,7 @@
</v-select>
<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 }]"
:placeholder="placeholder" :style="inputStyle" @input="onInput"
:placeholder="placeholder" :style="inputStyle" @update:model-value="onInput"
>
</div>

View File

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

View File

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

View File

@ -62,6 +62,9 @@
<script>
import axios from 'axios'
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 clonedeep from 'clone-deep'
import FormLogicPropertyResolver from '../../../forms/FormLogicPropertyResolver.js'
@ -100,6 +103,16 @@ export default {
},
adminPreview: { type: Boolean, default: false } // If used in FormEditorPreview
},
setup () {
const recordsStore = useRecordsStore()
const workingFormStore = useWorkingFormStore()
return {
recordsStore,
workingFormStore
}
},
data () {
return {
dataForm: null,
@ -154,8 +167,8 @@ export default {
newFields.push(...group)
}
})
// set the properties on working_form vuex
this.$store.commit('open/working_form/setProperties', newFields)
// set the properties on working_form store
this.workingFormStore.setProperties(newFields)
}
},
/**
@ -293,12 +306,12 @@ export default {
if (!this.form || !this.form.editable_submissions || !this.form.submission_id) {
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) => {
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 () {
if (this.isPublicFormPage && this.form.editable_submissions) {

View File

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

View File

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

View File

@ -85,7 +85,11 @@
</template>
<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 FormFieldEditSidebar from '../fields/FormFieldEditSidebar.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 () {
return {
showFormErrorModal: false,
@ -153,23 +170,20 @@ export default {
},
computed: {
...mapGetters({
user: 'auth/user'
}),
form: {
get () {
return this.$store.state['open/working_form'].content
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.$store.commit('open/working_form/set', value)
this.workingFormStore.set(value)
}
},
createdForm () {
return this.$store.getters['open/forms/getById'](this.createdFormId)
return this.formsStore.getById(this.createdFormId)
},
workspace () {
return this.$store.getters['open/workspaces/getCurrent']()
return this.workspacesStore.getCurrent()
},
steps () {
return [
@ -245,7 +259,7 @@ export default {
this.validationErrorResponse = null
this.form.put('/api/open/forms/{id}/'.replace('{id}', this.form.id)).then((response) => {
const data = response.data
this.$store.commit('open/forms/addOrUpdate', data.form)
this.formsStore.addOrUpdate(data.form)
this.$emit('on-save')
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 })
@ -266,7 +280,7 @@ export default {
this.updateFormLoading = true
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.createdFormId = response.data.form.id

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -43,11 +43,18 @@
</template>
<script>
import { useWorkingFormStore } from '../../../../../../stores/working_form'
import ProTag from '../../../../../common/ProTag.vue'
export default {
components: { ProTag },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore
}
},
data () {
return {
showModal: false
@ -57,11 +64,11 @@ export default {
computed: {
form: {
get () {
return this.$store.state['open/working_form'].content
return this.workingFormStore.content
},
/* We add a setter */
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..."
help="Action(s) triggerred when above conditions are true"
:options="actionOptions"
@input="onActionInput"
@update:model-value="onActionInput"
/>
<modal :show="showCopyFormModal" @close="showCopyFormModal = false">

View File

@ -72,10 +72,11 @@
</template>
<script>
import Form from 'vform'
import store from '~/store'
import { mapState, mapGetters } from 'vuex'
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'
export default {
@ -87,6 +88,18 @@ export 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: () => ({
templateForm: null
}),
@ -104,18 +117,10 @@ export default {
related_templates: null,
questions: []
})
store.dispatch('open/templates/loadIfEmpty')
this.templatesStore.loadIfEmpty()
},
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 () {
return Object.values(this.types).map((type) => {
return {
@ -156,7 +161,7 @@ export default {
if (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')
})
},
@ -166,7 +171,7 @@ export default {
if (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')
})
},
@ -177,7 +182,7 @@ export default {
this.alertSuccess(response.data.message)
}
this.$router.push({ name: 'templates' })
this.$store.commit('open/templates/remove', this.template)
this.templatesStore.remove(this.template)
this.$emit('close')
})
}

View File

@ -70,8 +70,9 @@
</template>
<script>
import { mapState } from 'vuex'
import { computed } from 'vue'
import clonedeep from 'clone-deep'
import { useWorkingFormStore } from '../../../../stores/working_form'
import ChangeFieldType from './components/ChangeFieldType.vue'
import FieldOptions from './components/FieldOptions.vue'
import BlockOptions from './components/BlockOptions.vue'
@ -80,6 +81,14 @@ export default {
name: 'FormFieldEditSidebar',
components: { ChangeFieldType, FieldOptions, BlockOptions },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore,
selectedFieldIndex : computed(() => workingFormStore.selectedFieldIndex),
showEditFieldSidebar : computed(() => workingFormStore.showEditFieldSidebar)
}
},
data () {
return {
@ -87,17 +96,13 @@ export default {
},
computed: {
...mapState({
selectedFieldIndex: state => state['open/working_form'].selectedFieldIndex,
showEditFieldSidebar: state => state['open/working_form'].showEditFieldSidebar
}),
form: {
get () {
return this.$store.state['open/working_form'].content
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.$store.commit('open/working_form/set', value)
this.workingFormStore.set(value)
}
},
field () {
@ -145,7 +150,7 @@ export default {
this.closeSidebar()
},
closeSidebar () {
this.$store.commit('open/working_form/closeEditFieldSidebar')
this.workingFormStore.closeEditFieldSidebar()
},
generateUUID () {
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"
label="Set selection options"
help="Add one option per line"
@input="onFieldOptionsChange"
@update:model-value="onFieldOptionsChange"
/>
<v-checkbox v-model="field.allow_creation"
name="allow_creation" help="" @update:model-value="onFieldAllowCreationChange"

View File

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

View File

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

View File

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

View File

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

View File

@ -122,8 +122,10 @@
</template>
<script>
import { computed } from 'vue'
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 FormTemplateModal from '../../../open/forms/components/templates/FormTemplateModal.vue'
@ -135,6 +137,15 @@ export default {
isMainPage: { type: Boolean, required: false, default: false }
},
setup () {
const authStore = useAuthStore()
const formsStore = useFormsStore()
return {
formsStore,
user : computed(() => authStore.user)
}
},
data: () => ({
loadingDuplicate: false,
loadingDelete: false,
@ -143,9 +154,6 @@ export default {
}),
computed: {
...mapGetters({
user: 'auth/user'
}),
formEndpoint: () => '/api/open/forms/{id}',
},
@ -163,7 +171,7 @@ export default {
if (this.loadingDuplicate) return
this.loadingDuplicate = true
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.alertSuccess('Form was successfully duplicated.')
this.loadingDuplicate = false
@ -173,7 +181,7 @@ export default {
if (this.loadingDelete) return
this.loadingDelete = true
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.alertSuccess('Form was deleted.')
this.loadingDelete = false

View File

@ -72,7 +72,9 @@
</template>
<script>
import { computed } from 'vue'
import axios from 'axios'
import { useFormsStore } from '../../../../stores/forms'
export default {
name: 'RegenerateFormLink',
@ -81,6 +83,13 @@ export default {
form: { type: Object, required: true }
},
setup () {
const formsStore = useFormsStore()
return {
formsStore
}
},
data: () => ({
loadingNewLink: false,
showGenerateFormLinkModal: false,
@ -95,7 +104,7 @@ export default {
if (this.loadingNewLink) return
this.loadingNewLink = true
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.alertSuccess(response.data.message)
this.loadingNewLink = false

View File

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

View File

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

View File

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

View File

@ -41,6 +41,9 @@
</template>
<script>
import { computed } from 'vue'
import { useTemplatesStore } from '../../../stores/templates'
export default {
props: {
slug: {
@ -53,19 +56,26 @@ export default {
}
},
setup () {
const templatesStore = useTemplatesStore()
return {
templatesStore
}
},
data: () => ({}),
computed: {
template () {
return this.$store.getters['open/templates/getBySlug'](this.slug)
return this.templatesStore.getBySlug(this.slug)
},
types () {
if (!this.template) return null
return this.$store.getters['open/templates/getTemplateTypes'](this.template.types)
return this.templatesStore.getTemplateTypes(this.template.types)
},
industries () {
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>
<script>
import store from '~/store'
import { mapGetters, mapState } from 'vuex'
import { computed } from 'vue'
import { useAuthStore } from '../../../stores/auth'
import { useTemplatesStore } from '../../../stores/templates'
import Form from 'vform'
import Fuse from 'fuse.js'
import SingleTemplate from './SingleTemplate.vue'
const loadTemplates = function (onlyMy) {
const templatesStore = useTemplatesStore()
if(onlyMy){
store.dispatch('open/templates/loadAll', {'onlymy':true})
templatesStore.loadAll({'onlymy':true})
} else {
store.dispatch('open/templates/loadIfEmpty')
templatesStore.loadIfEmpty()
}
}
export default {
name: 'TemplatesList',
components: { SingleTemplate },
props: {
onlyMy: {
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: () => ({
selectedType: 'all',
selectedIndustry: 'all',
@ -119,15 +132,6 @@ export default {
},
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 () {
return [{ name: 'All Industries', value: 'all' }].concat(Object.values(this.industries).map((industry) => {
return {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,8 @@
import store from '~/store'
import { useAuthStore } from '../stores/auth';
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 ([
'forms.create',
'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.
@ -7,14 +7,13 @@ import store from '~/store'
*/
export default (to, from, next, roles) => {
// Grab the user
const user = store.getters['auth/user']
const authStore = useAuthStore()
// Split roles into an array
roles = roles.split(',')
// Check if the user has one of the required roles...
if (!roles.includes(user.role)) {
if (!roles.includes(authStore.user?.role)) {
next('/unauthorized')
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -116,10 +116,12 @@
</template>
<script>
import axios from 'axios'
import store from '~/store'
import { computed } from 'vue'
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 VButton from "../../../components/common/Button.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'
const loadForms = function () {
store.commit('open/forms/startLoading')
store.dispatch('open/workspaces/loadIfEmpty').then(() => {
store.dispatch('open/forms/loadIfEmpty', store.state['open/workspaces'].currentId)
const formsStore = useFormsStore()
const workspacesStore = useWorkspacesStore()
formsStore.startLoading()
workspacesStore.loadIfEmpty().then(() => {
formsStore.loadIfEmpty(workspacesStore.currentId)
})
}
@ -154,6 +158,21 @@ export default {
},
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() {
return {
metaTitle: 'Home',
@ -175,27 +194,21 @@ export default {
},
computed: {
...mapGetters({
user: 'auth/user'
}),
...mapState({
formsLoading: state => state['open/forms'].loading,
workspacesLoading: state => state['open/workspaces'].loading
}),
workingForm: {
get() {
return this.$store.state['open/working_form'].content
get () {
return this.workingFormStore.content
},
set(value) {
this.$store.commit('open/working_form/set', value)
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
},
workspace() {
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() {
return this.$store.getters['open/forms/getBySlug'](this.$route.params.slug)
return this.formsStore.getBySlug(this.$route.params.slug)
},
formEndpoint: () => '/api/open/forms/{id}',
loading() {

View File

@ -104,8 +104,10 @@
</template>
<script>
import store from '~/store'
import { mapGetters, mapState } from 'vuex'
import { computed } from 'vue'
import { useAuthStore } from '../stores/auth';
import { useFormsStore } from '../stores/forms';
import { useWorkspacesStore } from '../stores/workspaces';
import Fuse from 'fuse.js'
import Form from 'vform'
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'
const loadForms = function () {
store.commit('open/forms/startLoading')
store.dispatch('open/workspaces/loadIfEmpty').then(() => {
store.dispatch('open/forms/loadIfEmpty', store.state['open/workspaces'].currentId)
const formsStore = useFormsStore()
const workspacesStore = useWorkspacesStore()
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!' }
},
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 () {
return {
showEditFormModal: false,
@ -165,19 +182,12 @@ export default {
},
computed: {
...mapGetters({
user: 'auth/user'
}),
...mapState({
forms: state => state['open/forms'].content,
formsLoading: state => state['open/forms'].loading
}),
isFilteringForms () {
return (this.searchForm.search !== '' && this.searchForm.search !== null) || this.selectedTags.length > 0
},
enrichedForms () {
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
})
@ -206,7 +216,7 @@ export default {
})
},
allTags () {
return this.$store.getters['open/forms/getAllTags']
return this.formsStore.getAllTags
}
}
}

View File

@ -236,14 +236,14 @@
</template>
<script>
import {mapGetters} from 'vuex'
import { computed } from 'vue'
import { useAuthStore } from '../stores/auth';
import OpenFormFooter from '../components/pages/OpenFormFooter.vue'
import PricingTable from '../components/pages/pricing/PricingTable.vue'
import SeoMeta from '../mixins/seo-meta.js'
export default {
components: {OpenFormFooter, PricingTable},
mixins: [SeoMeta],
layout: 'default',
@ -257,20 +257,23 @@ export default {
next()
},
setup () {
const authStore = useAuthStore()
return {
user : computed(() => authStore.user),
authenticated : computed(() => authStore.check)
}
},
data: () => ({
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.',
}),
mounted() {
},
mounted() {},
computed: {},
computed: {
...mapGetters({
authenticated: 'auth/check',
user: 'auth/user'
})
},
methods: {
contactUs() {
window.$crisp.push(['do', 'chat:show'])

View File

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

View File

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

View File

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

View File

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

View File

@ -24,13 +24,22 @@
<script>
import Form from 'vform'
import { mapGetters } from 'vuex'
import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
import SeoMeta from '../../mixins/seo-meta.js'
export default {
mixins: [SeoMeta],
scrollToTop: false,
setup () {
const authStore = useAuthStore()
return {
authStore,
user : computed(() => authStore.user)
}
},
data: () => ({
metaTitle: 'Profile',
form: new Form({
@ -39,10 +48,6 @@ export default {
})
}),
computed: mapGetters({
user: 'auth/user'
}),
created () {
// Fill the form with user data.
this.form.keys().forEach(key => {
@ -54,7 +59,7 @@ export default {
async update () {
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>
<script>
import { computed } from 'vue'
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'
export default {
@ -82,6 +84,17 @@ export default {
scrollToTop: false,
mixins: [SeoMeta],
setup () {
const formsStore = useFormsStore()
const workspacesStore = useWorkspacesStore()
return {
formsStore,
workspacesStore,
workspaces: computed(() => workspacesStore.content),
loading: computed(() => workspacesStore.loading)
}
},
data: () => ({
metaTitle: 'Workspaces',
form: new Form({
@ -92,28 +105,20 @@ export default {
}),
mounted() {
this.loadWorkspaces()
this.workspacesStore.loadIfEmpty()
},
computed: {
...mapState({
workspaces: state => state['open/workspaces'].content,
loading: state => state['open/workspaces'].loading
})
},
computed: {},
methods: {
...mapActions({
loadWorkspaces: 'open/workspaces/loadIfEmpty'
}),
switchWorkspace(workspace) {
this.$store.commit('open/workspaces/setCurrentId', workspace.id)
this.workspacesStore.setCurrentId(workspace.id)
this.$router.push({name: 'home'})
this.$store.dispatch('open/forms/load', workspace.id)
this.formsStore.load(workspace.id)
},
deleteWorkspace(workspace) {
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.')
})
})
@ -129,7 +134,7 @@ export default {
},
async createWorkspace() {
const {data} = await this.form.post('/api/open/workspaces/create')
this.$store.dispatch('open/workspaces/load')
this.workspacesStore.load()
this.workspaceModal = false
}
}

View File

@ -1,7 +1,8 @@
<template />
<script>
import { mapGetters } from 'vuex'
import { computed } from 'vue'
import { useAuthStore } from '../../stores/auth'
import SeoMeta from '../../mixins/seo-meta.js'
export default {
@ -10,6 +11,13 @@ export default {
middleware: 'auth',
mixins: [SeoMeta],
setup () {
const authStore = useAuthStore()
return {
authenticated : computed(() => authStore.check),
}
},
data: () => ({
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.')
},
computed: {
...mapGetters({
authenticated: 'auth/check'
})
}
computed: {}
}
</script>

View File

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

View File

@ -93,19 +93,21 @@
</template>
<script>
import store from '~/store'
import Form from 'vform'
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 OpenFormFooter from '../../components/pages/OpenFormFooter.vue'
import Breadcrumb from '../../components/common/Breadcrumb.vue'
import SingleTemplate from '../../components/pages/templates/SingleTemplate.vue'
const loadTemplates = function () {
store.commit('open/templates/startLoading')
store.dispatch('open/templates/loadIfEmpty').then(() => {
store.commit('open/templates/stopLoading')
const templatesStore = useTemplatesStore()
templatesStore.startLoading()
templatesStore.loadIfEmpty().then(() => {
templatesStore.stopLoading()
})
}
@ -118,6 +120,20 @@ export default {
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 () {
return {
selectedType: 'all',
@ -130,16 +146,6 @@ export default {
mounted () {},
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 () {
if (!this.industry) {
return [{ route: { name: 'templates' }, label: 'Templates' }]

View File

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

View File

@ -93,19 +93,21 @@
</template>
<script>
import store from '~/store'
import Form from 'vform'
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 OpenFormFooter from '../../components/pages/OpenFormFooter.vue'
import Breadcrumb from '../../components/common/Breadcrumb.vue'
import SingleTemplate from '../../components/pages/templates/SingleTemplate.vue'
const loadTemplates = function () {
store.commit('open/templates/startLoading')
store.dispatch('open/templates/loadIfEmpty').then(() => {
store.commit('open/templates/stopLoading')
const templatesStore = useTemplatesStore()
templatesStore.startLoading()
templatesStore.loadIfEmpty().then(() => {
templatesStore.stopLoading()
})
}
@ -118,6 +120,19 @@ export default {
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 () {
return {
selectedIndustry: 'all',
@ -130,16 +145,6 @@ export default {
mounted () {},
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 () {
if (!this.type) {
return [{ route: { name: 'templates' }, label: 'Templates' }]

View File

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

View File

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

View File

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