Working on home page and modal
This commit is contained in:
@@ -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
|
||||
})
|
||||
})
|
||||
|
||||
102
client/stores/forms.js
vendored
102
client/stores/forms.js
vendored
@@ -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
|
||||
}
|
||||
})
|
||||
|
||||
1
client/stores/templates.js
vendored
1
client/stores/templates.js
vendored
@@ -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()
|
||||
|
||||
1
client/stores/workspaces.js
vendored
1
client/stores/workspaces.js
vendored
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user