Working on home page and modal

This commit is contained in:
Julien Nahum 2023-12-19 19:42:02 +01:00
parent 5640f43b9d
commit 933f95e944
7 changed files with 183 additions and 222 deletions

View File

@ -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>

View File

@ -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', {

View File

@ -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
View File

@ -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
}
})

View File

@ -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()

View File

@ -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)
}

View File

@ -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
})