Skip to content

Commit 62929ef

Browse files
authored
Update DataTableViewer.js
Added option for delete row button
1 parent a90fdf5 commit 62929ef

File tree

1 file changed

+118
-28
lines changed

1 file changed

+118
-28
lines changed

src/DataTableViewer.js

Lines changed: 118 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@ class DataTableViewer {
22
constructor(config) {
33
this.config = {
44
dataUrl: config.dataUrl,
5+
dataJson: config.dataJson,
56
editEndpoint: config.editEndpoint || '',
7+
deleteEndpoint: config.deleteEndpoint || '',
68
extraEditData: config.extraEditData || {},
9+
extraDeleteData: config.extraDeleteData || {},
710
onEditSuccess: config.onEditSuccess || ((response) => console.log('Edit success:', response)),
11+
onDeleteSuccess: config.onDeleteSuccess || ((response) => console.log('Delete success:', response)),
12+
allowDelete: config.allowDelete || false,
813
columns: config.columns || [],
914
rowsPerPage: config.rowsPerPage || 10,
10-
container: config.container || document.getElementById('tableContainer')
15+
container: config.container || document.getElementById('tableContainer'),
16+
customEditForm: config.customEditForm || null
1117
};
1218

1319
this.data = [];
@@ -34,9 +40,39 @@ class DataTableViewer {
3440
if (this.config.dataUrl) {
3541
const response = await fetch(this.config.dataUrl);
3642
this.data = await response.json();
43+
} else if(this.config.dataJson){
44+
this.data = this.config.dataJson;
3745
} else {
38-
// Use sample data for preview
39-
this.data = sampleData;
46+
// Check for existing table rows
47+
const existingTable = this.config.container.querySelector('table');
48+
if (existingTable) {
49+
// Get headers first to map column fields
50+
const headers = Array.from(existingTable.querySelectorAll('thead th'))
51+
.map(th => th.textContent.trim());
52+
53+
// If no columns were provided in config, create them from headers
54+
if (!this.config.columns.length) {
55+
this.config.columns = headers.map(header => ({
56+
field: header.toLowerCase().replace(/\s+/g, '_'),
57+
title: header
58+
}));
59+
}
60+
61+
// Parse existing rows into data array
62+
this.data = Array.from(existingTable.querySelectorAll('tbody tr')).map(row => {
63+
const rowData = {};
64+
row.querySelectorAll('td').forEach((cell, index) => {
65+
const field = this.config.columns[index]?.field;
66+
if (field) {
67+
rowData[field] = cell.textContent.trim();
68+
}
69+
});
70+
return rowData;
71+
});
72+
} else {
73+
// No URL and no existing table, use empty array
74+
this.data = [];
75+
}
4076
}
4177
this.filteredData = [...this.data];
4278
} catch (error) {
@@ -99,6 +135,7 @@ class DataTableViewer {
99135
}
100136
});
101137
}
138+
102139
filterData() {
103140
this.filteredData = this.data.filter(row => {
104141
// Global search
@@ -149,15 +186,15 @@ class DataTableViewer {
149186
this.render();
150187
}
151188

152-
async handleEdit(rowIndex, row) {
189+
async handleEdit(rowIndex, newRowData) {
153190
try {
154191
const response = await fetch(this.config.editEndpoint, {
155192
method: 'POST',
156193
headers: {
157194
'Content-Type': 'application/json'
158195
},
159196
body: JSON.stringify({
160-
...row,
197+
...newRowData,
161198
...this.config.extraEditData
162199
})
163200
});
@@ -168,7 +205,7 @@ class DataTableViewer {
168205
this.config.onEditSuccess(result);
169206

170207
// Update local data
171-
Object.assign(this.data[rowIndex], row);
208+
Object.assign(this.data[rowIndex], newRowData);
172209
this.filterData();
173210
this.render();
174211

@@ -178,6 +215,38 @@ class DataTableViewer {
178215
}
179216
}
180217

218+
async handleDelete(rowIndex, row) {
219+
//let confirm =
220+
if (!await showConfirm('Confirm','Are you sure you want to delete this row?')) return;
221+
222+
try {
223+
const response = await fetch(this.config.deleteEndpoint, {
224+
method: 'POST',
225+
headers: {
226+
'Content-Type': 'application/json'
227+
},
228+
body: JSON.stringify({
229+
...row,
230+
...this.config.extraDeleteData
231+
})
232+
});
233+
234+
if (!response.ok) throw new Error('Delete failed');
235+
236+
const result = await response.json();
237+
this.config.onDeleteSuccess(result);
238+
239+
// Remove from local data
240+
this.data.splice(rowIndex, 1);
241+
this.filterData();
242+
this.render();
243+
244+
} catch (error) {
245+
console.error('Error deleting row:', error);
246+
alert('Failed to delete row');
247+
}
248+
}
249+
181250
render() {
182251
const visibleColumns = this.config.columns.filter(col => !this.hiddenColumns.has(col.field));
183252
const startIndex = (this.currentPage - 1) * this.config.rowsPerPage;
@@ -201,7 +270,8 @@ class DataTableViewer {
201270
${pageData.map((row, idx) => `
202271
<tr id="row-${idx}">
203272
<td>
204-
<button class="edit-btn" data-row="${idx}">Edit</button>
273+
<button class="my-1 xp-2 edit-btn" data-row="${idx}" data-tooltip="Edit Row"><i class="bi bi-check-lg"></i></button>
274+
${this.config.allowDelete ? `<button class="my-1 xp-2 contrast delete-btn" data-tooltip="Delete Row" data-row="${idx}"><i class="bi bi-x-lg"></i></button>` : ''}
205275
</td>
206276
${visibleColumns.map(col => `
207277
<td class="data-cell" data-column="${col.field}">${row[col.field]}</td>
@@ -221,9 +291,11 @@ class DataTableViewer {
221291
`<option value="${value}" ${this.columnSearchValues[col.field] === value ? 'selected' : ''}>${value}</option>`
222292
).join('')}
223293
</select>` :
294+
col.searchType === 'text' ?
224295
`<input type="text" class="column-search" data-column="${col.field}"
225296
value="${this.columnSearchValues[col.field] || ''}"
226297
placeholder="Search ${col.title}...">`
298+
: ''
227299
}
228300
</td>
229301
`).join('')}
@@ -281,33 +353,51 @@ class DataTableViewer {
281353
const rowIndex = parseInt(btn.dataset.row);
282354
const row = pageData[rowIndex];
283355

284-
const tr = document.getElementById(`row-${rowIndex}`);
285-
const originalContent = tr.innerHTML;
356+
if (this.config.customEditForm) {
357+
// Call the custom edit form function with the row data and a callback
358+
this.config.customEditForm(row, (updatedRowData) => {
359+
this.handleEdit(rowIndex, updatedRowData);
360+
});
361+
} else {
362+
// Original inline editing logic
363+
const tr = document.getElementById(`row-${rowIndex}`);
364+
const originalContent = tr.innerHTML;
286365

287-
tr.innerHTML = `
288-
<td>
289-
<button class="save-btn">Save</button>
290-
<button class="cancel-btn">Cancel</button>
291-
</td>
292-
${visibleColumns.map(col => `
366+
tr.innerHTML = `
293367
<td>
294-
<input type="text" value="${row[col.field]}" data-field="${col.field}">
368+
<button class="save-btn">Save</button>
369+
<button class="cancel-btn">Cancel</button>
295370
</td>
296-
`).join('')}
297-
`;
371+
${visibleColumns.map(col => `
372+
<td>
373+
<input type="text" value="${row[col.field]}" data-field="${col.field}">
374+
</td>
375+
`).join('')}
376+
`;
377+
378+
tr.querySelector('.save-btn').addEventListener('click', () => {
379+
const updatedRow = { ...row };
380+
tr.querySelectorAll('input[data-field]').forEach(input => {
381+
updatedRow[input.dataset.field] = input.value;
382+
});
383+
this.handleEdit(rowIndex, updatedRow);
384+
});
298385

299-
tr.querySelector('.save-btn').addEventListener('click', () => {
300-
const updatedRow = { ...row };
301-
tr.querySelectorAll('input[data-field]').forEach(input => {
302-
updatedRow[input.dataset.field] = input.value;
386+
tr.querySelector('.cancel-btn').addEventListener('click', () => {
387+
tr.innerHTML = originalContent;
303388
});
304-
this.handleEdit(rowIndex, updatedRow);
305-
});
389+
}
390+
});
391+
});
306392

307-
tr.querySelector('.cancel-btn').addEventListener('click', () => {
308-
tr.innerHTML = originalContent;
393+
if (this.config.allowDelete) {
394+
this.config.container.querySelectorAll('.delete-btn').forEach(btn => {
395+
btn.addEventListener('click', () => {
396+
const rowIndex = parseInt(btn.dataset.row);
397+
const row = pageData[rowIndex];
398+
this.handleDelete(rowIndex, row);
309399
});
310400
});
311-
});
401+
}
312402
}
313-
}
403+
}

0 commit comments

Comments
 (0)