250 lines
7.4 KiB
Vue
250 lines
7.4 KiB
Vue
<template>
|
|
<div
|
|
class="my-4 w-full mx-auto"
|
|
>
|
|
<h3 class="font-semibold mb-4 text-xl">
|
|
Form Submissions
|
|
</h3>
|
|
|
|
<!-- Table columns modal -->
|
|
<modal :show="showColumnsModal" @close="showColumnsModal=false">
|
|
<template #icon>
|
|
<svg class="w-8 h-8" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M16 5H8C4.13401 5 1 8.13401 1 12C1 15.866 4.13401 19 8 19H16C19.866 19 23 15.866 23 12C23 8.13401 19.866 5 16 5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
<path d="M8 15C9.65685 15 11 13.6569 11 12C11 10.3431 9.65685 9 8 9C6.34315 9 5 10.3431 5 12C5 13.6569 6.34315 15 8 15Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
</svg>
|
|
</template>
|
|
<template #title>
|
|
Display columns
|
|
</template>
|
|
|
|
<div class="px-4">
|
|
<template v-if="properties.length > 0">
|
|
<h4 class="font-bold mb-2">
|
|
Form Fields
|
|
</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" @update:model-value="onChangeDisplayColumns" />
|
|
</div>
|
|
</template>
|
|
<template v-if="removed_properties.length > 0">
|
|
<h4 class="font-bold mb-2 mt-4">
|
|
Removed Fields
|
|
</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" @update:model-value="onChangeDisplayColumns" />
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</modal>
|
|
|
|
<Loader v-if="!form || !formInitDone" class="h-6 w-6 text-nt-blue mx-auto" />
|
|
<div v-else>
|
|
<div v-if="form && tableData.length > 0" class="flex flex-wrap items-end">
|
|
<div class="flex-grow">
|
|
<text-input class="w-64" :form="searchForm" name="search" placeholder="Search..." />
|
|
</div>
|
|
<div class="font-semibold flex gap-4">
|
|
<p class="float-right text-xs uppercase mb-2">
|
|
<a
|
|
href="javascript:void(0);" class="text-gray-500" @click="showColumnsModal=true"
|
|
>Display columns</a>
|
|
</p>
|
|
<p class="text-right text-xs uppercase">
|
|
<a
|
|
:href="exportUrl" target="_blank"
|
|
>Export as CSV</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<scroll-shadow
|
|
ref="shadows"
|
|
class="border max-h-full h-full notion-database-renderer"
|
|
:shadow-top-offset="0"
|
|
:hide-scrollbar="true"
|
|
>
|
|
<open-table
|
|
ref="table"
|
|
class="max-h-full"
|
|
:data="filteredData"
|
|
:loading="isLoading"
|
|
@resize="dataChanged()"
|
|
@deleted="onDeleteRecord()"
|
|
/>
|
|
</scroll-shadow>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import axios from 'axios'
|
|
import Fuse from 'fuse.js'
|
|
import Form from 'vform'
|
|
import { useWorkingFormStore } from '../../../../stores/working_form'
|
|
import ScrollShadow from '~/components/global/ScrollShadow.vue'
|
|
import OpenTable from '../../tables/OpenTable.vue'
|
|
import clonedeep from 'clone-deep'
|
|
import VSwitch from '../../../forms/components/VSwitch.vue'
|
|
|
|
export default {
|
|
name: 'FormSubmissions',
|
|
components: { ScrollShadow, OpenTable, VSwitch },
|
|
props: {},
|
|
|
|
setup () {
|
|
const workingFormStore = useWorkingFormStore()
|
|
return {
|
|
workingFormStore
|
|
}
|
|
},
|
|
|
|
data () {
|
|
return {
|
|
formInitDone: false,
|
|
isLoading: false,
|
|
tableData: [],
|
|
currentPage: 1,
|
|
fullyLoaded: false,
|
|
showColumnsModal: false,
|
|
properties: [],
|
|
removed_properties: [],
|
|
displayColumns: {},
|
|
searchForm: new Form({
|
|
search: ''
|
|
})
|
|
}
|
|
},
|
|
computed: {
|
|
form: {
|
|
get () {
|
|
return this.workingFormStore.content
|
|
},
|
|
set (value) {
|
|
this.workingFormStore.set(value)
|
|
}
|
|
},
|
|
exportUrl () {
|
|
if (!this.form) {
|
|
return ''
|
|
}
|
|
return '/api/open/forms/' + this.form.id + '/submissions/export'
|
|
},
|
|
filteredData () {
|
|
if (!this.tableData) return []
|
|
|
|
const filteredData = clonedeep(this.tableData)
|
|
|
|
if (this.searchForm.search === '' || this.searchForm.search === null) {
|
|
return filteredData
|
|
}
|
|
|
|
// Fuze search
|
|
const fuzeOptions = {
|
|
keys: this.form.properties.map((field) => field.id)
|
|
}
|
|
const fuse = new Fuse(filteredData, fuzeOptions)
|
|
return fuse.search(this.searchForm.search).map((res) => {
|
|
return res.item
|
|
})
|
|
}
|
|
},
|
|
watch: {
|
|
'form.id' () {
|
|
if (this.form === null) {
|
|
return
|
|
}
|
|
this.initFormStructure()
|
|
this.getSubmissionsData()
|
|
}
|
|
},
|
|
mounted () {
|
|
this.initFormStructure()
|
|
this.getSubmissionsData()
|
|
},
|
|
methods: {
|
|
initFormStructure () {
|
|
if (!this.form || !this.form.properties || this.formInitDone) {
|
|
return
|
|
}
|
|
|
|
// check if form properties already has a created_at column
|
|
let hasCreatedAt = false
|
|
this.form.properties.forEach((property) => {
|
|
if (property.id === 'created_at') {
|
|
hasCreatedAt = true
|
|
}
|
|
})
|
|
|
|
if (!hasCreatedAt) {
|
|
// Add a "created at" column
|
|
const columns = clonedeep(this.form.properties)
|
|
columns.push({
|
|
name: 'Created at',
|
|
id: 'created_at',
|
|
type: 'date',
|
|
width: 140
|
|
})
|
|
this.form.properties = columns
|
|
}
|
|
this.formInitDone = true
|
|
|
|
this.properties = clonedeep(this.form.properties)
|
|
this.removed_properties = (this.form.removed_properties) ? clonedeep(this.form.removed_properties) : []
|
|
|
|
// Get display columns from local storage
|
|
const tmpColumns = window.localStorage.getItem('display-columns-formid-' + this.form.id)
|
|
if (tmpColumns !== null && tmpColumns) {
|
|
this.displayColumns = JSON.parse(tmpColumns)
|
|
this.onChangeDisplayColumns()
|
|
} else {
|
|
this.form.properties.forEach((field) => {
|
|
this.displayColumns[field.id] = true
|
|
})
|
|
}
|
|
},
|
|
getSubmissionsData () {
|
|
if (!this.form || this.fullyLoaded) {
|
|
return
|
|
}
|
|
this.isLoading = true
|
|
opnFetch('/open/forms/' + this.form.id + '/submissions?page=' + this.currentPage).then((resData) => {
|
|
this.tableData = this.tableData.concat(resData.data.map((record) => record.data))
|
|
this.dataChanged()
|
|
|
|
if (this.currentPage < resData.meta.last_page) {
|
|
this.currentPage += 1
|
|
this.getSubmissionsData()
|
|
} else {
|
|
this.isLoading = false
|
|
this.fullyLoaded = true
|
|
}
|
|
}).catch((error) => {
|
|
console.error(error)
|
|
this.isLoading = false
|
|
})
|
|
},
|
|
dataChanged () {
|
|
if (this.$refs.shadows) {
|
|
this.$refs.shadows.toggleShadow()
|
|
this.$refs.shadows.calcDimensions()
|
|
}
|
|
},
|
|
onChangeDisplayColumns () {
|
|
if (process.client)
|
|
window.localStorage.setItem('display-columns-formid-' + this.form.id, JSON.stringify(this.displayColumns))
|
|
this.form.properties = this.properties.concat(this.removed_properties).filter((field) => {
|
|
return this.displayColumns[field.id] === true
|
|
})
|
|
},
|
|
onDeleteRecord () {
|
|
this.fullyLoaded = false
|
|
this.tableData = []
|
|
this.getSubmissionsData()
|
|
}
|
|
}
|
|
}
|
|
</script>
|