@@ -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