Working on home page and modal
This commit is contained in:
parent
5640f43b9d
commit
933f95e944
|
@ -49,129 +49,123 @@
|
|||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import {useMotions} from '@vueuse/motion'
|
||||
import {watch} from "vue";
|
||||
|
||||
export default {
|
||||
name: 'Modal',
|
||||
|
||||
props: {
|
||||
show: {
|
||||
default: false
|
||||
},
|
||||
backdropBlur: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
iconColor: {
|
||||
default: 'blue'
|
||||
},
|
||||
maxWidth: {
|
||||
default: '2xl'
|
||||
},
|
||||
closeable: {
|
||||
default: true
|
||||
}
|
||||
const props = defineProps({
|
||||
show: {
|
||||
default: false
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
useHead({
|
||||
bodyAttrs: {
|
||||
class: {
|
||||
'overflow-hidden': props.show
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const closeOnEscape = (e) => {
|
||||
if (e.key === 'Escape' && this.show) {
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (process.server) return
|
||||
document.addEventListener('keydown', closeOnEscape)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (process.server) return
|
||||
document.removeEventListener('keydown', closeOnEscape)
|
||||
})
|
||||
|
||||
return {
|
||||
motions: useMotions(),
|
||||
}
|
||||
backdropBlur: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
iconColor: {
|
||||
default: 'blue'
|
||||
},
|
||||
maxWidth: {
|
||||
default: '2xl'
|
||||
},
|
||||
closeable: {
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
computed: {
|
||||
maxWidthClass() {
|
||||
return {
|
||||
sm: 'sm:max-w-sm',
|
||||
md: 'sm:max-w-md',
|
||||
lg: 'sm:max-w-lg',
|
||||
xl: 'sm:max-w-xl',
|
||||
'2xl': 'sm:max-w-2xl'
|
||||
}[this.maxWidth]
|
||||
},
|
||||
motionFadeIn() {
|
||||
return {
|
||||
initial: {
|
||||
opacity: 0,
|
||||
transition: {
|
||||
delay: 100,
|
||||
duration: 200,
|
||||
ease: 'easeIn'
|
||||
}
|
||||
},
|
||||
enter: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
const emits = defineEmits(['close'])
|
||||
|
||||
useHead({
|
||||
bodyAttrs: {
|
||||
class: {
|
||||
'overflow-hidden': props.show
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const closeOnEscape = (e) => {
|
||||
if (e.key === 'Escape' && this.show) {
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (process.server) return
|
||||
document.addEventListener('keydown', closeOnEscape)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (process.server) return
|
||||
document.removeEventListener('keydown', closeOnEscape)
|
||||
})
|
||||
|
||||
const motions = useMotions()
|
||||
|
||||
const maxWidthClass = computed(() => {
|
||||
return {
|
||||
sm: 'sm:max-w-sm',
|
||||
md: 'sm:max-w-md',
|
||||
lg: 'sm:max-w-lg',
|
||||
xl: 'sm:max-w-xl',
|
||||
'2xl': 'sm:max-w-2xl'
|
||||
}[props.maxWidth]
|
||||
})
|
||||
|
||||
const motionFadeIn = computed(() => {
|
||||
return {
|
||||
initial: {
|
||||
opacity: 0,
|
||||
transition: {
|
||||
delay: 100,
|
||||
duration: 200,
|
||||
ease: 'easeIn'
|
||||
}
|
||||
},
|
||||
motionSlideBottom() {
|
||||
return {
|
||||
initial: {
|
||||
y: 150,
|
||||
opacity: 0,
|
||||
transition: {
|
||||
ease: 'easeIn',
|
||||
duration: 200
|
||||
}
|
||||
},
|
||||
enter: {
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 250,
|
||||
ease: 'easeOut',
|
||||
delay: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
show(newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
if (!newVal) {
|
||||
this.motions.body.apply('initial')
|
||||
this.motions.backdrop.apply('initial')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
close() {
|
||||
if (this.closeable) {
|
||||
this.$emit('close')
|
||||
enter: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const motionSlideBottom = computed(() => {
|
||||
return {
|
||||
initial: {
|
||||
y: 150,
|
||||
opacity: 0,
|
||||
transition: {
|
||||
ease: 'easeIn',
|
||||
duration: 200
|
||||
}
|
||||
},
|
||||
enter: {
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 250,
|
||||
ease: 'easeOut',
|
||||
delay: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.show, (newVal, oldVal) => {
|
||||
if (newVal !== oldVal) {
|
||||
if (newVal) {
|
||||
motions.body.apply('enter')
|
||||
motions.backdrop.apply('enter')
|
||||
} else {
|
||||
motions.body.apply('initial')
|
||||
motions.backdrop.apply('initial')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const close = () => {
|
||||
if (props.closeable) {
|
||||
emits('close')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -161,7 +161,7 @@ export default {
|
|||
showFormErrorModal: false,
|
||||
validationErrorResponse: null,
|
||||
updateFormLoading: false,
|
||||
createdFormId: null
|
||||
createdFormSlug: null
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -176,7 +176,7 @@ export default {
|
|||
}
|
||||
},
|
||||
createdForm () {
|
||||
return this.formsStore.getById(this.createdFormId)
|
||||
return this.formsStore.getBySlug(this.createdFormSlug)
|
||||
},
|
||||
workspace () {
|
||||
return this.workspacesStore.getCurrent()
|
||||
|
@ -278,7 +278,7 @@ export default {
|
|||
this.form.post('/api/open/forms').then((response) => {
|
||||
this.formsStore.addOrUpdate(response.data.form)
|
||||
this.$emit('on-save')
|
||||
this.createdFormId = response.data.form.id
|
||||
this.createdFormSlug = response.data.form.slug
|
||||
|
||||
this.$logEvent('form_created', { form_id: response.data.form.id, form_slug: response.data.form.slug })
|
||||
this.$crisp.push(['set', 'session:event', [[['form_created', {
|
||||
|
|
|
@ -47,9 +47,9 @@
|
|||
<h3 class="w-full mt-4 text-center text-gray-900 font-semibold">
|
||||
No forms found
|
||||
</h3>
|
||||
<div v-if="isFilteringForms && enrichedForms.length === 0 && searchForm.search"
|
||||
<div v-if="isFilteringForms && enrichedForms.length === 0 && search"
|
||||
class="mt-2 w-full text-center">
|
||||
Your search "{{ searchForm.search }}" did not match any forms. Please try again.
|
||||
Your search "{{ search }}" did not match any forms. Please try again.
|
||||
</div>
|
||||
<v-button v-if="forms.length === 0" v-track.create_form_click class="mt-4" :to="{name:'forms-create'}">
|
||||
<svg class="w-4 h-4 text-white inline mr-1 -mt-1" viewBox="0 0 14 14" fill="none"
|
||||
|
@ -112,23 +112,18 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useAuthStore} from '../stores/auth';
|
||||
import {useFormsStore} from '../stores/forms';
|
||||
import {useWorkspacesStore} from '../stores/workspaces';
|
||||
import {useAuthStore} from '../stores/auth'
|
||||
import {useFormsStore} from '../stores/forms'
|
||||
import {useWorkspacesStore} from '../stores/workspaces'
|
||||
import Fuse from 'fuse.js'
|
||||
import TextInput from '../components/forms/TextInput.vue'
|
||||
import OpenFormFooter from '../components/pages/OpenFormFooter.vue'
|
||||
import ExtraMenu from '../components/pages/forms/show/ExtraMenu.vue'
|
||||
import {refDebounced} from "@vueuse/core";
|
||||
import {refDebounced} from "@vueuse/core"
|
||||
|
||||
const loadForms = function () {
|
||||
const formsStore = useFormsStore()
|
||||
const workspacesStore = useWorkspacesStore()
|
||||
formsStore.startLoading()
|
||||
workspacesStore.loadIfEmpty().then(() => {
|
||||
formsStore.loadIfEmpty(workspacesStore.currentId)
|
||||
})
|
||||
}
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
})
|
||||
|
||||
// metaTitle: {type: String, default: 'Your Forms'},
|
||||
// metaDescription: {
|
||||
|
@ -140,16 +135,16 @@ const authStore = useAuthStore()
|
|||
const formsStore = useFormsStore()
|
||||
const workspacesStore = useWorkspacesStore()
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth"
|
||||
onMounted(() => {
|
||||
formsStore.load(workspacesStore.currentId)
|
||||
})
|
||||
|
||||
// State
|
||||
const {content: forms, loading: formsLoading} = storeToRefs(formsStore)
|
||||
const {getAll: forms, loading: formsLoading} = storeToRefs(formsStore)
|
||||
const showEditFormModal = ref(false)
|
||||
const selectedForm = ref(null)
|
||||
const search = ref('')
|
||||
const debounceSearch = refDebounced(search, 500)
|
||||
const debouncedSearch = refDebounced(search, 500)
|
||||
const selectedTags = ref(new Set())
|
||||
|
||||
// Methods
|
||||
|
@ -173,13 +168,24 @@ const isFilteringForms = computed(() => {
|
|||
return (search.value !== '' && search.value !== null) || selectedTags.value.size > 0
|
||||
})
|
||||
const allTags = computed(() => {
|
||||
return formsStore.getAllTags
|
||||
let tags = []
|
||||
forms.value.forEach((form) => {
|
||||
console.log(form.tags)
|
||||
// TODO: check this works
|
||||
if (form.tags && form.tags.length) {
|
||||
tags = tags.concat(form.tags.split(','))
|
||||
}
|
||||
})
|
||||
return [...new Set(tags)]
|
||||
})
|
||||
const enrichedForms = computed(() => {
|
||||
let enrichedForms = forms.value.map((form) => {
|
||||
form.workspace = workspacesStore.getByKey(form.workspace_id)
|
||||
return form
|
||||
}).filter((form) => {
|
||||
if (selectedTags.value.size === 0) {
|
||||
return true
|
||||
}
|
||||
return form.tags && form.tags.length ? [...selectedTags].every(r => form.tags.includes(r)) : false
|
||||
})
|
||||
|
||||
|
@ -196,7 +202,7 @@ const enrichedForms = computed(() => {
|
|||
]
|
||||
}
|
||||
const fuse = new Fuse(enrichedForms, fuzeOptions)
|
||||
return fuse.search(search.value).map((res) => {
|
||||
return fuse.search(debouncedSearch.value).map((res) => {
|
||||
return res.item
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,82 +1,36 @@
|
|||
import {defineStore} from 'pinia'
|
||||
import {useContentStore} from "~/composables/stores/useContentStore.js";
|
||||
|
||||
export const formsEndpoint = '/open/workspaces/{workspaceId}/forms'
|
||||
export let currentPage = 1
|
||||
|
||||
export const useFormsStore = defineStore('forms', {
|
||||
state: () => ({
|
||||
content: [],
|
||||
loading: false
|
||||
}),
|
||||
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)
|
||||
export const useFormsStore = defineStore('forms', () => {
|
||||
|
||||
const contentStore = useContentStore('slug')
|
||||
contentStore.startLoading()
|
||||
const currentPage = ref(1)
|
||||
|
||||
const load = (workspaceId) => {
|
||||
contentStore.startLoading()
|
||||
return opnFetch(formsEndpoint.replace('{workspaceId}', workspaceId),{query: {page: currentPage.value}})
|
||||
.then((response) => {
|
||||
if (currentPage.value === 1) {
|
||||
contentStore.resetState()
|
||||
contentStore.save(response.data)
|
||||
} else {
|
||||
contentStore.save(response.data)
|
||||
}
|
||||
if (currentPage.value < response.meta.last_page) {
|
||||
currentPage.value++
|
||||
load(workspaceId)
|
||||
} else {
|
||||
contentStore.stopLoading()
|
||||
currentPage.value = 1
|
||||
}
|
||||
})
|
||||
return allTags.filter((item, i, ar) => ar.indexOf(item) === i)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
set(items) {
|
||||
this.content = items
|
||||
},
|
||||
append(items) {
|
||||
this.content = this.content.concat(items)
|
||||
},
|
||||
addOrUpdate(item) {
|
||||
this.content = this.content.filter((val) => val.id !== item.id)
|
||||
this.content.push(item)
|
||||
},
|
||||
remove(item) {
|
||||
this.content = this.content.filter((val) => val.id !== item.id)
|
||||
},
|
||||
startLoading() {
|
||||
this.loading = true
|
||||
},
|
||||
stopLoading() {
|
||||
this.loading = false
|
||||
},
|
||||
resetState() {
|
||||
this.set([])
|
||||
this.stopLoading()
|
||||
currentPage = 1
|
||||
},
|
||||
load(workspaceId) {
|
||||
this.startLoading()
|
||||
return useOpnApi(formsEndpoint.replace('{workspaceId}', workspaceId) + '?page=' + currentPage)
|
||||
.then(({data, error}) => {
|
||||
if (currentPage === 1) {
|
||||
this.set(data.value.data)
|
||||
} else {
|
||||
this.append(data.value.data)
|
||||
}
|
||||
if (currentPage < data.value.meta.last_page) {
|
||||
currentPage += 1
|
||||
this.load(workspaceId)
|
||||
} else {
|
||||
this.stopLoading()
|
||||
currentPage = 1
|
||||
}
|
||||
})
|
||||
},
|
||||
loadIfEmpty(workspaceId) {
|
||||
if (this.content.length === 0) {
|
||||
return this.load(workspaceId)
|
||||
}
|
||||
this.stopLoading()
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...contentStore,
|
||||
load
|
||||
}
|
||||
})
|
||||
|
|
|
@ -52,7 +52,6 @@ export const fetchAllTemplates = (options = {}) => {
|
|||
}
|
||||
|
||||
export const loadAllTemplates = async (store, options={}) => {
|
||||
console.log('in------',store, store.allLoaded)
|
||||
if (!store.allLoaded) {
|
||||
store.startLoading()
|
||||
store.initTypesAndIndustries()
|
||||
|
|
|
@ -22,7 +22,6 @@ export const useWorkspacesStore = defineStore('workspaces', () => {
|
|||
|
||||
const save = (items) => {
|
||||
contentStore.save(items)
|
||||
console.log('cookie issi', currentId.value, contentStore.length.value)
|
||||
if (currentId.value == null && contentStore.length.value) {
|
||||
setCurrentId(items[0].id)
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
</svg>
|
||||
</v-button>
|
||||
</template>
|
||||
<a v-if="isMainPage && user" v-track.view_form_click="{form_id:form.id, form_slug:form.slug}" :href="form.share_url"
|
||||
<a v-if="isMainPage && user" v-track.view_form_click="{form_id:form.id, form_slug:form.slug}"
|
||||
:href="form.share_url"
|
||||
target="_blank"
|
||||
class="block px-4 py-2 text-md text-gray-700 dark:text-white hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600 flex items-center"
|
||||
>
|
||||
|
@ -45,7 +46,9 @@
|
|||
:to="{name:'forms.edit', params: {slug: form.slug}}"
|
||||
class="block block px-4 py-2 text-md text-gray-700 dark:text-white hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600 flex items-center"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-2" width="18" height="17" viewBox="0 0 18 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg class="w-4 h-4 mr-2" width="18" height="17" viewBox="0 0 18 17" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.99998 15.6662H16.5M1.5 15.6662H2.89545C3.3031 15.6662 3.50693 15.6662 3.69874 15.6202C3.8688 15.5793 4.03138 15.512 4.1805 15.4206C4.34869 15.3175 4.49282 15.1734 4.78107 14.8852L15.25 4.4162C15.9404 3.72585 15.9404 2.60656 15.25 1.9162C14.5597 1.22585 13.4404 1.22585 12.75 1.9162L2.28105 12.3852C1.9928 12.6734 1.84867 12.8175 1.7456 12.9857C1.65422 13.1348 1.58688 13.2974 1.54605 13.4675C1.5 13.6593 1.5 13.8631 1.5 14.2708V15.6662Z"
|
||||
stroke="currentColor" stroke-width="1.67" stroke-linecap="round" stroke-linejoin="round"
|
||||
|
@ -58,7 +61,10 @@
|
|||
@click.prevent="copyLink"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-2" viewBox="0 0 16 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.00016 8.33317H4.66683C2.82588 8.33317 1.3335 6.84079 1.3335 4.99984C1.3335 3.15889 2.82588 1.6665 4.66683 1.6665H6.00016M10.0002 8.33317H11.3335C13.1744 8.33317 14.6668 6.84079 14.6668 4.99984C14.6668 3.15889 13.1744 1.6665 11.3335 1.6665H10.0002M4.66683 4.99984L11.3335 4.99984" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M6.00016 8.33317H4.66683C2.82588 8.33317 1.3335 6.84079 1.3335 4.99984C1.3335 3.15889 2.82588 1.6665 4.66683 1.6665H6.00016M10.0002 8.33317H11.3335C13.1744 8.33317 14.6668 6.84079 14.6668 4.99984C14.6668 3.15889 13.1744 1.6665 11.3335 1.6665H10.0002M4.66683 4.99984L11.3335 4.99984"
|
||||
stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
Copy link to share
|
||||
</a>
|
||||
|
@ -134,7 +140,9 @@
|
|||
</div>
|
||||
</modal>
|
||||
|
||||
<form-template-modal v-if="!isMainPage && user" :form="form" :show="showFormTemplateModal" @close="showFormTemplateModal=false" />
|
||||
<form-template-modal v-if="!isMainPage && user" :form="form" :show="showFormTemplateModal"
|
||||
@close="showFormTemplateModal=false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -159,6 +167,7 @@ export default {
|
|||
const formsStore = useFormsStore()
|
||||
return {
|
||||
formsStore,
|
||||
router: useRouter(),
|
||||
user: computed(() => authStore.user)
|
||||
}
|
||||
},
|
||||
|
@ -187,9 +196,9 @@ export default {
|
|||
duplicateForm () {
|
||||
if (this.loadingDuplicate) return
|
||||
this.loadingDuplicate = true
|
||||
axios.post(this.formEndpoint.replace('{id}', this.form.id) + '/duplicate').then((response) => {
|
||||
this.formsStore.addOrUpdate(response.data.new_form)
|
||||
this.$router.push({ name: 'forms.show', params: { slug: response.data.new_form.slug } })
|
||||
opnFetch(this.formEndpoint.replace('{id}', this.form.id) + '/duplicate', { method: 'POST' }).then((response) => {
|
||||
this.formsStore.save(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
|
||||
})
|
||||
|
@ -198,8 +207,8 @@ export default {
|
|||
if (this.loadingDelete) return
|
||||
this.loadingDelete = true
|
||||
axios.delete(this.formEndpoint.replace('{id}', this.form.id)).then(() => {
|
||||
this.formsStore.remove(this.form)
|
||||
this.$router.push({ name: 'home' })
|
||||
this.formsStore.remove(this.form.id)
|
||||
this.router.push({ name: 'home' })
|
||||
this.alertSuccess('Form was deleted.')
|
||||
this.loadingDelete = false
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue