Enhance form submissions column management (#691)
* Enhance form submissions column management * Modernize UI components with Nuxt UI and improved styling - Refactor RecordOperations component to use UButtonGroup and UButton - Update FormSubmissions component with Nuxt UI buttons and modal - Improve table cell styling in OpenTable component - Simplify column management and export functionality --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
aae28d09cc
commit
29ef44d50e
|
|
@ -1,46 +1,20 @@
|
|||
<template>
|
||||
<div class="flex items-center justify-center space-x-1">
|
||||
<button
|
||||
<UButtonGroup size="xs" orientation="horizontal">
|
||||
<UButton
|
||||
v-track.delete_record_click
|
||||
class="border rounded py-1 px-2 text-gray-500 dark:text-gray-400 hover:text-blue-700"
|
||||
size="sm"
|
||||
color="white"
|
||||
icon="heroicons:pencil-square"
|
||||
@click="showEditSubmissionModal = true"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
/>
|
||||
<UButton
|
||||
v-track.delete_record_click
|
||||
class="border rounded py-1 px-2 text-gray-500 dark:text-gray-400 hover:text-red-700"
|
||||
size="sm"
|
||||
color="white"
|
||||
icon="heroicons:trash"
|
||||
@click="onDeleteClick"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
/>
|
||||
</UButtonGroup>
|
||||
<EditSubmissionModal
|
||||
:show="showEditSubmissionModal"
|
||||
:form="form"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,243 @@
|
|||
<template>
|
||||
<modal
|
||||
compact-header
|
||||
:show="show"
|
||||
@close="$emit('close')"
|
||||
>
|
||||
<template #icon>
|
||||
<Icon name="heroicons:adjustments-horizontal" class="w-8 h-8" />
|
||||
</template>
|
||||
<template #title>
|
||||
Manage Columns
|
||||
</template>
|
||||
|
||||
<div class="px-4">
|
||||
<template
|
||||
v-for="(section, sectionIndex) in sections"
|
||||
:key="sectionIndex"
|
||||
>
|
||||
<template v-if="section.fields.length > 0">
|
||||
<h4
|
||||
class="font-semibold mb-2"
|
||||
:class="{ 'mt-4': sectionIndex > 0 }"
|
||||
>
|
||||
{{ section.title }}
|
||||
</h4>
|
||||
<div class="border border-gray-300">
|
||||
<div class="grid grid-cols-[1fr,auto,auto] gap-4 px-4 py-2 bg-gray-50 border-b border-gray-300">
|
||||
<div class="text-sm">
|
||||
Name
|
||||
</div>
|
||||
<div class="text-sm text-center w-20">
|
||||
Display
|
||||
</div>
|
||||
<div class="text-sm text-center w-20">
|
||||
Wrap Text
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(field, index) in section.fields"
|
||||
:key="field.id"
|
||||
class="grid grid-cols-[1fr,auto,auto] gap-4 px-4 py-2 items-center"
|
||||
:class="{ 'border-t border-gray-300': index !== 0 }"
|
||||
>
|
||||
<p class="truncate text-sm">
|
||||
{{ field.name }}
|
||||
</p>
|
||||
<div class="flex justify-center w-20">
|
||||
<ToggleSwitchInput
|
||||
v-model="displayColumns[field.id]"
|
||||
wrapper-class="mb-0"
|
||||
label=""
|
||||
:name="`display-${field.id}`"
|
||||
@update:model-value="onChangeDisplayColumns"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-center w-20">
|
||||
<ToggleSwitchInput
|
||||
v-model="wrapColumns[field.id]"
|
||||
wrapper-class="mb-0"
|
||||
label=""
|
||||
:name="`wrap-${field.id}`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import clonedeep from 'clone-deep'
|
||||
|
||||
const props = defineProps({
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close', 'update:columns', 'update:displayColumns', 'update:wrapColumns'])
|
||||
|
||||
const candidatesProperties = computed(() => {
|
||||
if (!props.form?.properties) return []
|
||||
|
||||
const properties = clonedeep(props.form.properties).filter((field) => {
|
||||
return !['nf-text', 'nf-code', 'nf-page-break', 'nf-divider', 'nf-image'].includes(field.type)
|
||||
})
|
||||
|
||||
// Add created_at column if not present
|
||||
if (!properties.find(property => property.id === 'created_at')) {
|
||||
properties.push({
|
||||
name: 'Created at',
|
||||
id: 'created_at',
|
||||
type: 'date',
|
||||
width: 140
|
||||
})
|
||||
}
|
||||
|
||||
return properties
|
||||
})
|
||||
|
||||
const sections = computed(() => [
|
||||
{
|
||||
title: 'Form Fields',
|
||||
fields: candidatesProperties.value
|
||||
},
|
||||
{
|
||||
title: 'Removed Fields',
|
||||
fields: props.form?.removed_properties || []
|
||||
}
|
||||
])
|
||||
|
||||
// Column preferences storage
|
||||
const columnPreferences = useStorage(
|
||||
computed(() => props.form ? `column-preferences-formid-${props.form.id}` : null),
|
||||
{
|
||||
display: {},
|
||||
wrap: {},
|
||||
widths: {}
|
||||
}
|
||||
)
|
||||
|
||||
const displayColumns = computed({
|
||||
get: () => columnPreferences.value.display,
|
||||
set: (val) => {
|
||||
columnPreferences.value.display = val
|
||||
emit('update:displayColumns', val)
|
||||
}
|
||||
})
|
||||
|
||||
const wrapColumns = computed({
|
||||
get: () => columnPreferences.value.wrap,
|
||||
set: (val) => {
|
||||
columnPreferences.value.wrap = val
|
||||
emit('update:wrapColumns', val)
|
||||
}
|
||||
})
|
||||
|
||||
// Helper function to preserve column widths
|
||||
function preserveColumnWidths(newColumns, existingColumns = []) {
|
||||
if (!columnPreferences.value) {
|
||||
columnPreferences.value = { display: {}, wrap: {}, widths: {} }
|
||||
}
|
||||
if (!columnPreferences.value.widths) {
|
||||
columnPreferences.value.widths = {}
|
||||
}
|
||||
|
||||
return newColumns.map(col => {
|
||||
// First try to find width in storage
|
||||
const storedWidth = columnPreferences.value?.widths?.[col.id]
|
||||
// Then try current table columns
|
||||
const currentCol = props.columns?.find(c => c.id === col.id)
|
||||
// Then fallback to form properties
|
||||
const existing = existingColumns?.find(e => e.id === col.id)
|
||||
|
||||
const width = storedWidth || currentCol?.cell_width || currentCol?.width || existing?.cell_width || existing?.width || col.width || 150
|
||||
|
||||
return {
|
||||
...col,
|
||||
width,
|
||||
cell_width: width
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Watch for column width changes
|
||||
watch(() => props.columns, (newColumns) => {
|
||||
if (!newColumns?.length) return
|
||||
|
||||
if (!columnPreferences.value) {
|
||||
columnPreferences.value = { display: {}, wrap: {}, widths: {} }
|
||||
}
|
||||
if (!columnPreferences.value.widths) {
|
||||
columnPreferences.value.widths = {}
|
||||
}
|
||||
|
||||
const widths = {}
|
||||
newColumns.forEach(col => {
|
||||
if (col.cell_width) {
|
||||
widths[col.id] = col.cell_width
|
||||
}
|
||||
})
|
||||
|
||||
columnPreferences.value.widths = widths
|
||||
}, { deep: true })
|
||||
|
||||
// Initialize display and wrap columns when form changes
|
||||
watch(() => props.form, (newForm) => {
|
||||
if (!newForm) return
|
||||
|
||||
const properties = newForm.properties || []
|
||||
const storedPrefs = columnPreferences.value
|
||||
|
||||
// Initialize display columns if not set
|
||||
if (!Object.keys(storedPrefs.display).length) {
|
||||
properties.forEach((field) => {
|
||||
storedPrefs.display[field.id] = true
|
||||
})
|
||||
}
|
||||
|
||||
// Initialize wrap columns if not set
|
||||
if (!Object.keys(storedPrefs.wrap).length) {
|
||||
properties.forEach((field) => {
|
||||
storedPrefs.wrap[field.id] = false
|
||||
})
|
||||
}
|
||||
|
||||
// Emit initial values
|
||||
emit('update:displayColumns', storedPrefs.display)
|
||||
emit('update:wrapColumns', storedPrefs.wrap)
|
||||
|
||||
// Emit initial columns (all visible by default)
|
||||
const initialColumns = clonedeep(candidatesProperties.value)
|
||||
.concat(props.form?.removed_properties || [])
|
||||
.filter((field) => storedPrefs.display[field.id] !== false) // Show all columns by default unless explicitly hidden
|
||||
|
||||
// Preserve any existing column widths
|
||||
const columnsWithWidths = preserveColumnWidths(initialColumns, props.form.properties)
|
||||
emit('update:columns', columnsWithWidths)
|
||||
}, { immediate: true })
|
||||
|
||||
function onChangeDisplayColumns() {
|
||||
if (!import.meta.client) return
|
||||
const properties = clonedeep(candidatesProperties.value)
|
||||
.concat(props.form?.removed_properties || [])
|
||||
.filter((field) => displayColumns.value[field.id] === true)
|
||||
|
||||
// Preserve existing column widths when toggling visibility
|
||||
const columnsWithWidths = preserveColumnWidths(properties, props.form.properties)
|
||||
emit('update:columns', columnsWithWidths)
|
||||
}
|
||||
</script>
|
||||
|
|
@ -8,89 +8,16 @@
|
|||
Form Submissions
|
||||
</h3>
|
||||
|
||||
<!-- Table columns modal -->
|
||||
<modal
|
||||
<!-- Settings Modal -->
|
||||
<form-columns-settings-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="form.properties.length > 0">
|
||||
<h4 class="font-bold mb-2">
|
||||
Form Fields
|
||||
</h4>
|
||||
<div class="border border-gray-300 rounded-md">
|
||||
<div
|
||||
v-for="(field,index) in candidatesProperties"
|
||||
:key="field.id"
|
||||
class="p-2 border-gray-300 flex items-center"
|
||||
:class="{'border-t':index!=0}"
|
||||
>
|
||||
<p class="flex-grow truncate">
|
||||
{{ field.name }}
|
||||
</p>
|
||||
<ToggleSwitchInput
|
||||
v-model="displayColumns[field.id]"
|
||||
wrapper-class="mb-0"
|
||||
label=""
|
||||
name="field.id"
|
||||
@update:model-value="onChangeDisplayColumns"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="removed_properties.length > 0">
|
||||
<h4 class="font-bold mb-2 mt-4">
|
||||
Removed Fields
|
||||
</h4>
|
||||
<div class="border border-gray-300 rounded-md">
|
||||
<div
|
||||
v-for="(field,index) in removed_properties"
|
||||
:key="field.id"
|
||||
class="p-2 border-gray-300 flex items-center"
|
||||
:class="{'border-t':index!=0}"
|
||||
>
|
||||
<p class="flex-grow truncate">
|
||||
{{ field.name }}
|
||||
</p>
|
||||
<ToggleSwitchInput
|
||||
v-model="displayColumns[field.id]"
|
||||
wrapper-class="mb-0"
|
||||
label=""
|
||||
name="field.id"
|
||||
@update:model-value="onChangeDisplayColumns"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</modal>
|
||||
:form="form"
|
||||
:columns="properties"
|
||||
v-model:display-columns="displayColumns"
|
||||
v-model:wrap-columns="wrapColumns"
|
||||
@close="showColumnsModal = false"
|
||||
@update:columns="onColumnUpdated"
|
||||
/>
|
||||
|
||||
<Loader
|
||||
v-if="!form"
|
||||
|
|
@ -99,7 +26,7 @@
|
|||
<div v-else>
|
||||
<div
|
||||
v-if="form && tableData.length > 0"
|
||||
class="flex flex-wrap items-end"
|
||||
class="flex flex-wrap items-center mb-4"
|
||||
>
|
||||
<div class="flex-grow">
|
||||
<VForm size="sm">
|
||||
|
|
@ -111,26 +38,22 @@
|
|||
/>
|
||||
</VForm>
|
||||
</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
|
||||
v-if="!exportLoading"
|
||||
class="text-right cursor-pointer text-xs uppercase"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="downloadAsCsv"
|
||||
>Export as CSV</a>
|
||||
</p>
|
||||
<p v-else>
|
||||
<loader class="w-3 h-3 text-blue-500" />
|
||||
</p>
|
||||
<div class="font-semibold flex gap-2">
|
||||
<UButton
|
||||
size="sm"
|
||||
color="white"
|
||||
icon="heroicons:adjustments-horizontal"
|
||||
label="Edit columns"
|
||||
@click="showColumnsModal=true"
|
||||
/>
|
||||
<UButton
|
||||
size="sm"
|
||||
color="white"
|
||||
icon="heroicons:arrow-down-tray"
|
||||
label="Export"
|
||||
:loading="exportLoading"
|
||||
@click="downloadAsCsv"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -147,6 +70,7 @@
|
|||
ref="table"
|
||||
class="max-h-full"
|
||||
:columns="properties"
|
||||
:wrap-columns="wrapColumns"
|
||||
:data="filteredData"
|
||||
:loading="isLoading"
|
||||
:scroll-parent="parentPage"
|
||||
|
|
@ -164,20 +88,24 @@
|
|||
import Fuse from 'fuse.js'
|
||||
import clonedeep from 'clone-deep'
|
||||
import OpenTable from '../../tables/OpenTable.vue'
|
||||
import FormColumnsSettingsModal from './FormColumnsSettingsModal.vue'
|
||||
|
||||
export default {
|
||||
name: 'FormSubmissions',
|
||||
components: {OpenTable},
|
||||
components: { OpenTable, FormColumnsSettingsModal },
|
||||
props: {},
|
||||
|
||||
setup() {
|
||||
const workingFormStore = useWorkingFormStore()
|
||||
const recordStore = useRecordsStore()
|
||||
const form = storeToRefs(workingFormStore).content
|
||||
const tableData = storeToRefs(recordStore).getAll
|
||||
|
||||
return {
|
||||
workingFormStore,
|
||||
recordStore,
|
||||
form: storeToRefs(workingFormStore).content,
|
||||
tableData: storeToRefs(recordStore).getAll,
|
||||
form,
|
||||
tableData,
|
||||
runtimeConfig: useRuntimeConfig(),
|
||||
slug: useRoute().params.slug
|
||||
}
|
||||
|
|
@ -188,14 +116,15 @@ export default {
|
|||
fullyLoaded: false,
|
||||
showColumnsModal: false,
|
||||
properties: [],
|
||||
removed_properties: [],
|
||||
displayColumns: {},
|
||||
exportLoading: false,
|
||||
searchForm: useForm({
|
||||
search: ''
|
||||
}),
|
||||
displayColumns: {},
|
||||
wrapColumns: {},
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
parentPage() {
|
||||
if (import.meta.server) {
|
||||
|
|
@ -203,11 +132,6 @@ export default {
|
|||
}
|
||||
return window
|
||||
},
|
||||
candidatesProperties() {
|
||||
return clonedeep(this.form.properties).filter((field) => {
|
||||
return !['nf-text', 'nf-code', 'nf-page-break', 'nf-divider', 'nf-image'].includes(field.type)
|
||||
})
|
||||
},
|
||||
exportUrl() {
|
||||
if (!this.form) {
|
||||
return ''
|
||||
|
|
@ -234,8 +158,9 @@ export default {
|
|||
return fuse.search(this.searchForm.search).map((res) => {
|
||||
return res.item
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
'form.id'() {
|
||||
this.onFormChange()
|
||||
|
|
@ -244,46 +169,19 @@ export default {
|
|||
this.dataChanged()
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.onFormChange()
|
||||
},
|
||||
|
||||
methods: {
|
||||
onFormChange() {
|
||||
if (this.form === null || this.form.slug !== this.slug) {
|
||||
return
|
||||
}
|
||||
this.fullyLoaded = false
|
||||
this.initFormStructure()
|
||||
this.getSubmissionsData()
|
||||
},
|
||||
initFormStructure() {
|
||||
// check if form properties already has a created_at column
|
||||
if (!this.properties.find((property) => {
|
||||
if (property.id === 'created_at') {
|
||||
return true
|
||||
}
|
||||
})) {
|
||||
// Add a "created at" column
|
||||
this.candidatesProperties.push({
|
||||
name: 'Created at',
|
||||
id: 'created_at',
|
||||
type: 'date',
|
||||
width: 140
|
||||
})
|
||||
}
|
||||
this.properties = this.candidatesProperties
|
||||
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.properties.forEach((field) => {
|
||||
this.displayColumns[field.id] = true
|
||||
})
|
||||
}
|
||||
},
|
||||
getSubmissionsData() {
|
||||
if (this.fullyLoaded) {
|
||||
return
|
||||
|
|
@ -328,13 +226,6 @@ export default {
|
|||
onColumnUpdated(columns) {
|
||||
this.properties = columns
|
||||
},
|
||||
onChangeDisplayColumns() {
|
||||
if (!import.meta.client) return
|
||||
window.localStorage.setItem('display-columns-formid-' + this.form.id, JSON.stringify(this.displayColumns))
|
||||
this.properties = clonedeep(this.candidatesProperties).concat(this.removed_properties).filter((field) => {
|
||||
return this.displayColumns[field.id] === true
|
||||
})
|
||||
},
|
||||
onUpdateRecord(submission) {
|
||||
this.recordStore.save(submission)
|
||||
this.dataChanged()
|
||||
|
|
@ -355,16 +246,16 @@ export default {
|
|||
columns: this.displayColumns
|
||||
}
|
||||
}).then(blob => {
|
||||
const filename = `${this.form.slug}-${Date.now()}-submissions.csv`
|
||||
const a = document.createElement("a")
|
||||
document.body.appendChild(a)
|
||||
a.style = "display: none"
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
a.href = url
|
||||
a.download = filename
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
}).catch((error) => {
|
||||
const filename = `${this.form.slug}-${Date.now()}-submissions.csv`
|
||||
const a = document.createElement("a")
|
||||
document.body.appendChild(a)
|
||||
a.style = "display: none"
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
a.href = url
|
||||
a.download = filename
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
}).catch((error) => {
|
||||
console.error(error)
|
||||
}).finally(() => {
|
||||
this.exportLoading = false
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@
|
|||
{
|
||||
'border-b': index !== data.length - 1,
|
||||
'border-r': colIndex !== columns.length - 1 || hasActions,
|
||||
'whitespace-normal break-words': wrapColumns[col.id] === true,
|
||||
},
|
||||
colClasses(col),
|
||||
]"
|
||||
|
|
@ -90,13 +91,15 @@
|
|||
class="n-table-cell border-gray-100 dark:border-gray-900 text-sm p-2 border-b"
|
||||
style="width: 100px"
|
||||
>
|
||||
<record-operations
|
||||
:form="form"
|
||||
:structure="columns"
|
||||
:submission="row"
|
||||
@deleted="(submission) => $emit('deleted', submission)"
|
||||
@updated="(submission) => $emit('updated', submission)"
|
||||
/>
|
||||
<div class="flex justify-center">
|
||||
<record-operations
|
||||
:form="form"
|
||||
:structure="columns"
|
||||
:submission="row"
|
||||
@deleted="(submission) => $emit('deleted', submission)"
|
||||
@updated="(submission) => $emit('updated', submission)"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
|
|
@ -158,6 +161,10 @@ export default {
|
|||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
wrapColumns: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
|
|
|
|||
Loading…
Reference in New Issue