opnform/client/pages/home.vue

201 lines
8.0 KiB
Vue
Raw Permalink Normal View History

2023-12-09 14:47:03 +00:00
<template>
<div class="bg-white">
<div class="flex bg-gray-50 pb-5 border-b">
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl p-4">
<div class="pt-4 pb-0">
<div class="flex">
<h2 class="flex-grow text-gray-900">
Your Forms
</h2>
2023-12-14 15:53:05 +00:00
<v-button v-track.create_form_click :to="{name:'forms-create'}">
2023-12-16 18:21:03 +00:00
<svg class="w-4 h-4 text-white inline mr-1 -mt-1" viewBox="0 0 14 14" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M6.99996 1.1665V12.8332M1.16663 6.99984H12.8333" stroke="currentColor" stroke-width="1.67"
stroke-linecap="round" stroke-linejoin="round"/>
2023-12-09 14:47:03 +00:00
</svg>
Create a new form
</v-button>
</div>
<small class="flex text-gray-500">Manage your forms and submissions.</small>
</div>
</div>
</div>
<div class="flex bg-white">
2024-01-17 13:26:31 +00:00
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl">
2023-12-09 14:47:03 +00:00
<div class="mt-8 pb-0">
2024-01-17 13:26:31 +00:00
<text-input v-if="forms.length > 0" class="mb-6 px-4" v-model="search" name="search" label="Search a form"
2023-12-09 14:47:03 +00:00
placeholder="Name of form to search"
/>
2024-01-17 13:26:31 +00:00
<div v-if="allTags.length > 0" class="mb-4 px-6">
2023-12-09 14:47:03 +00:00
<div v-for="tag in allTags" :key="tag"
:class="[
'inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ring-1 ring-inset cursor-pointer mr-2',
2024-01-16 11:20:05 +00:00
{'bg-blue-50 text-blue-600 ring-blue-500/10 dark:bg-blue-400':selectedTags.has(tag),
'bg-gray-50 text-gray-600 ring-gray-500/10 dark:bg-gray-700 hover:bg-blue-50 hover:text-blue-600 hover:ring-blue-500/10 hover:dark:bg-blue-400':!selectedTags.has(tag)}
2023-12-09 14:47:03 +00:00
]"
title="Click for filter by tag(s)"
@click="onTagClick(tag)"
>
{{ tag }}
</div>
</div>
<div v-if="!formsLoading && enrichedForms.length === 0" class="flex flex-wrap justify-center max-w-4xl">
2024-01-10 15:17:47 +00:00
<NuxtImg class="w-56"
2024-03-09 21:27:15 +00:00
:src="`${useRuntimeConfig().public.appUrl}/img/pages/forms/search_notfound.png`" alt="search-not-found"
/>
2023-12-09 14:47:03 +00:00
<h3 class="w-full mt-4 text-center text-gray-900 font-semibold">
No forms found
</h3>
2023-12-19 18:42:02 +00:00
<div v-if="isFilteringForms && enrichedForms.length === 0 && search"
2023-12-16 18:21:03 +00:00
class="mt-2 w-full text-center">
2023-12-19 18:42:02 +00:00
Your search "{{ search }}" did not match any forms. Please try again.
2023-12-09 14:47:03 +00:00
</div>
2023-12-14 15:53:05 +00:00
<v-button v-if="forms.length === 0" v-track.create_form_click class="mt-4" :to="{name:'forms-create'}">
2023-12-16 18:21:03 +00:00
<svg class="w-4 h-4 text-white inline mr-1 -mt-1" viewBox="0 0 14 14" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M6.99996 1.1665V12.8332M1.16663 6.99984H12.8333" stroke="currentColor" stroke-width="1.67"
stroke-linecap="round" stroke-linejoin="round"/>
2023-12-09 14:47:03 +00:00
</svg>
Create a new form
</v-button>
</div>
<div v-else-if="forms.length > 0" class="mb-10">
<div v-if="enrichedForms && enrichedForms.length">
<div v-for="(form) in enrichedForms" :key="form.id"
2023-12-19 17:57:31 +00:00
class="mt-4 p-4 flex group bg-white hover:bg-gray-50 dark:bg-notion-dark items-center relative"
2023-12-09 14:47:03 +00:00
>
2023-12-24 08:51:22 +00:00
<div class="flex-grow items-center truncate cursor-pointer relative">
<NuxtLink :to="{name:'forms-slug-show-submissions', params: {slug:form.slug}}"
class="absolute inset-0"/>
2023-12-09 14:47:03 +00:00
<span class="font-semibold text-gray-900 dark:text-white">{{ form.title }}</span>
<ul class="flex text-gray-500">
<li class="pr-1">
{{ form.views_count }} view{{ form.views_count > 0 ? 's' : '' }}
</li>
<li class="list-disc ml-6 pr-1">
{{ form.submissions_count }}
submission{{ form.submissions_count > 0 ? 's' : '' }}
</li>
<li class="list-disc ml-6">
Edited {{ form.last_edited_human }}
</li>
</ul>
2023-12-16 18:21:03 +00:00
<div v-if="['draft','closed'].includes(form.visibility) || (form.tags && form.tags.length > 0)"
class="mt-1 flex items-center flex-wrap gap-3">
2023-12-09 14:47:03 +00:00
<span v-if="form.visibility=='draft'"
2023-12-16 18:21:03 +00:00
class="inline-flex items-center rounded-full bg-yellow-100 px-2 py-1 text-xs font-medium text-yellow-600 ring-1 ring-inset ring-gray-500/10 dark:text-white dark:bg-gray-700">
2023-12-09 14:47:03 +00:00
Draft
</span>
<span v-else-if="form.visibility=='closed'"
2023-12-16 18:21:03 +00:00
class="inline-flex items-center rounded-full bg-yellow-100 px-2 py-1 text-xs font-medium text-yellow-600 ring-1 ring-inset ring-gray-500/10 dark:text-white dark:bg-gray-700">
2023-12-09 14:47:03 +00:00
Closed
</span>
<span v-for="(tag,i) in form.tags" :key="tag"
class="inline-flex items-center rounded-full bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:text-white dark:bg-gray-700"
>
{{ tag }}
</span>
</div>
</div>
2023-12-16 18:21:03 +00:00
<extra-menu :form="form" :is-main-page="true"/>
2023-12-09 14:47:03 +00:00
</div>
</div>
</div>
<div v-if="formsLoading" class="text-center">
2023-12-16 18:21:03 +00:00
<Loader class="h-6 w-6 text-nt-blue mx-auto"/>
2023-12-09 14:47:03 +00:00
</div>
</div>
</div>
</div>
2023-12-16 18:21:03 +00:00
<open-form-footer class="mt-8 border-t"/>
2023-12-09 14:47:03 +00:00
</div>
</template>
2023-12-19 17:57:31 +00:00
<script setup>
2023-12-19 18:42:02 +00:00
import {useAuthStore} from '../stores/auth'
import {useFormsStore} from '../stores/forms'
import {useWorkspacesStore} from '../stores/workspaces'
2023-12-09 14:47:03 +00:00
import Fuse from 'fuse.js'
import TextInput from '../components/forms/TextInput.vue'
import ExtraMenu from '../components/pages/forms/show/ExtraMenu.vue'
2023-12-19 18:42:02 +00:00
import {refDebounced} from "@vueuse/core"
2023-12-09 14:47:03 +00:00
2023-12-19 18:42:02 +00:00
definePageMeta({
middleware: "auth"
})
2023-12-09 14:47:03 +00:00
useOpnSeoMeta({
title: 'Your Forms',
description: 'All of your OpnForm are here. Create new forms, or update your existing forms.'
})
2023-12-09 14:47:03 +00:00
2023-12-19 17:57:31 +00:00
const authStore = useAuthStore()
const formsStore = useFormsStore()
const workspacesStore = useWorkspacesStore()
2023-12-20 17:38:43 +00:00
formsStore.startLoading()
2023-12-09 14:47:03 +00:00
2023-12-19 18:42:02 +00:00
onMounted(() => {
2023-12-20 15:10:32 +00:00
if (!formsStore.allLoaded) {
2023-12-20 17:38:43 +00:00
formsStore.loadAll(workspacesStore.currentId)
} else {
formsStore.stopLoading()
2023-12-20 15:10:32 +00:00
}
2023-12-19 17:57:31 +00:00
})
2023-12-16 18:21:03 +00:00
2023-12-19 17:57:31 +00:00
// State
2023-12-20 15:10:32 +00:00
const {getAll: forms, loading: formsLoading, allTags} = storeToRefs(formsStore)
2023-12-19 17:57:31 +00:00
const showEditFormModal = ref(false)
const selectedForm = ref(null)
const search = ref('')
2023-12-19 18:42:02 +00:00
const debouncedSearch = refDebounced(search, 500)
2023-12-19 17:57:31 +00:00
const selectedTags = ref(new Set())
2023-12-16 18:21:03 +00:00
2023-12-19 17:57:31 +00:00
// Methods
const editForm = (form) => {
selectedForm.value = form
showEditFormModal.value = true
}
const onTagClick = (tag) => {
2024-02-10 11:46:17 +00:00
if (selectedTags?.value?.has(tag)) {
2023-12-19 17:57:31 +00:00
selectedTags.value.remove(tag)
} else {
selectedTags.value.add(tag)
}
}
2023-12-09 14:47:03 +00:00
2023-12-19 17:57:31 +00:00
// Computed
const isFilteringForms = computed(() => {
return (search.value !== '' && search.value !== null) || selectedTags.value.size > 0
})
2023-12-20 15:10:32 +00:00
2023-12-19 17:57:31 +00:00
const enrichedForms = computed(() => {
let enrichedForms = forms.value.map((form) => {
form.workspace = workspacesStore.getByKey(form.workspace_id)
return form
}).filter((form) => {
2023-12-19 18:42:02 +00:00
if (selectedTags.value.size === 0) {
return true
}
2024-01-16 11:20:05 +00:00
return form.tags && form.tags.length ? [...selectedTags.value].every(r => form.tags.includes(r)) : false
2023-12-19 17:57:31 +00:00
})
2023-12-09 14:47:03 +00:00
2023-12-19 17:57:31 +00:00
if (!isFilteringForms || search.value === '' || search.value === null) {
return enrichedForms
}
2023-12-09 14:47:03 +00:00
2023-12-19 17:57:31 +00:00
// Fuze search
const fuzeOptions = {
keys: [
'title',
'slug',
'tags'
]
2023-12-09 14:47:03 +00:00
}
2023-12-19 17:57:31 +00:00
const fuse = new Fuse(enrichedForms, fuzeOptions)
2023-12-19 18:42:02 +00:00
return fuse.search(debouncedSearch.value).map((res) => {
2023-12-19 17:57:31 +00:00
return res.item
})
})
2023-12-09 14:47:03 +00:00
</script>