213 lines
5.3 KiB
Vue
213 lines
5.3 KiB
Vue
<script setup>
|
|
import { ref } from 'vue'
|
|
import AgGridTable from '@/views/demos/forms/tables/data-table/AgGridTable.vue';
|
|
|
|
const props = defineProps({
|
|
apiUrl: {
|
|
type: String,
|
|
default: 'http://megategra.com:8001/dev/data/tasks'
|
|
},
|
|
tableId: {
|
|
type: String,
|
|
default: 'tasks-table'
|
|
},
|
|
tableTitle: {
|
|
type: String,
|
|
default: 'Tasks Management'
|
|
},
|
|
height: {
|
|
type: String,
|
|
default: '500px'
|
|
},
|
|
enableExport: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
enableEdit: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
enableDelete: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
enableSearch: {
|
|
type: Boolean,
|
|
default: true
|
|
}
|
|
})
|
|
|
|
const ganttColumns = ref([])
|
|
|
|
const generateColumnsFromData = (data, maxCellTextLength = 30) => {
|
|
if (!data || data.length === 0) return []
|
|
|
|
const firstItem = data[0]
|
|
const sampleSize = Math.min(10, data.length)
|
|
|
|
const columns = Object.keys(firstItem).map(key => {
|
|
const sampleValues = data.slice(0, sampleSize)
|
|
.map(item => item[key])
|
|
.filter(val => val !== undefined && val !== null)
|
|
|
|
const column = {
|
|
field: key,
|
|
headerName: key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1'),
|
|
sortable: true,
|
|
filter: true,
|
|
flex: 1
|
|
}
|
|
|
|
if (key === 'id') {
|
|
column.hide = true
|
|
column.width = 0
|
|
column.flex = 0
|
|
column.suppressColumnsToolPanel = true
|
|
column.suppressHeaderMenuButton = true
|
|
return column
|
|
}
|
|
|
|
if (key === 'completed' && typeof firstItem[key] === 'boolean') {
|
|
column.width = 120
|
|
column.flex = undefined
|
|
column.cellRenderer = (params) => {
|
|
return params.value ?
|
|
'<span style="color: green; font-weight: bold;">✓ Completed</span>' :
|
|
'<span style="color: orange; font-weight: bold;">⏳ Pending</span>'
|
|
}
|
|
return column
|
|
}
|
|
|
|
if (key === 'userId') {
|
|
column.width = 100
|
|
column.flex = undefined
|
|
return column
|
|
}
|
|
|
|
const isDateField = (val) => {
|
|
if (typeof val !== 'string') return false
|
|
const datePatterns = [
|
|
/^\d{4}-\d{2}-\d{2}/,
|
|
/T\d{2}:\d{2}:\d{2}/,
|
|
/^\d{2}\/\d{2}\/\d{4}/,
|
|
/^\d{1,2}-\d{1,2}-\d{4}/
|
|
]
|
|
return datePatterns.some(pattern => pattern.test(val)) || !isNaN(Date.parse(val))
|
|
}
|
|
|
|
const hasLongText = sampleValues.some(val =>
|
|
typeof val === 'string' && val.length > maxCellTextLength
|
|
)
|
|
|
|
const firstValue = sampleValues[0]
|
|
|
|
if (isDateField(firstValue)) {
|
|
column.width = 140
|
|
column.valueFormatter = (params) => {
|
|
if (!params.value) return ''
|
|
try {
|
|
const date = new Date(params.value)
|
|
if (isNaN(date.getTime())) return String(params.value)
|
|
return date.toISOString().split('T')[0]
|
|
} catch (e) {
|
|
return String(params.value)
|
|
}
|
|
}
|
|
column.tooltipValueGetter = (params) => {
|
|
if (!params.value) return ''
|
|
try {
|
|
const date = new Date(params.value)
|
|
return isNaN(date.getTime()) ?
|
|
String(params.value) :
|
|
date.toLocaleString('en-US', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
})
|
|
} catch (e) {
|
|
return String(params.value)
|
|
}
|
|
}
|
|
return column
|
|
}
|
|
|
|
if (hasLongText) {
|
|
column.flex = 1
|
|
column.minWidth = 150
|
|
column.maxWidth = 300
|
|
column.cellRenderer = 'LongTextCellRenderer'
|
|
column.cellRendererParams = {
|
|
maxLength: maxCellTextLength
|
|
}
|
|
column.tooltipField = key
|
|
return column
|
|
}
|
|
|
|
if (typeof firstValue === 'number') {
|
|
column.filter = 'agNumberColumnFilter'
|
|
column.width = 120
|
|
column.valueFormatter = (params) => {
|
|
if (typeof params.value !== 'number') return params.value || ''
|
|
return params.value.toLocaleString('en-US')
|
|
}
|
|
return column
|
|
}
|
|
|
|
column.flex = 1
|
|
column.minWidth = 100
|
|
column.maxWidth = 250
|
|
|
|
return column
|
|
}).filter(col => col.field !== 'action')
|
|
|
|
return columns
|
|
}
|
|
|
|
const processAPIData = (response) => {
|
|
const rawData = response?.data?.data || response?.data || []
|
|
|
|
const data = rawData.map((item, index) => {
|
|
const source = item._source || item
|
|
return {
|
|
id: item._id || item.id || `task_${Date.now()}_${index}`,
|
|
name: source.name || 'Unnamed',
|
|
complete: source.complete,
|
|
effort: parseFloat(source.effort) || 0,
|
|
effortleft: parseFloat(source.effortleft) || 0,
|
|
start: source.start,
|
|
end: source.end,
|
|
}
|
|
})
|
|
|
|
ganttColumns.value = generateColumnsFromData(data, 25).map(col => {
|
|
if (col.field === 'name') {
|
|
return {
|
|
...col,
|
|
flex: 2,
|
|
minWidth: 200,
|
|
maxWidth: 500,
|
|
wrapText: true,
|
|
autoHeight: true,
|
|
cellStyle: {
|
|
whiteSpace: 'normal',
|
|
lineHeight: '1.2',
|
|
wordWrap: 'break-word'
|
|
},
|
|
tooltipField: 'name'
|
|
}
|
|
}
|
|
return col
|
|
})
|
|
|
|
return data
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<AgGridTable :columns="ganttColumns" :api-url="apiUrl" :data-processor="processAPIData" :enable-export="enableExport"
|
|
:enable-edit="enableEdit" :enable-delete="enableDelete" :enable-search="enableSearch" :height="height"
|
|
:table-id="tableId" :table-title="tableTitle" />
|
|
</template> |