KEXEL commited on
Commit
6b87022
·
verified ·
1 Parent(s): 1746d61
Files changed (1) hide show
  1. excel-file-reader-editor.html +595 -0
excel-file-reader-editor.html ADDED
@@ -0,0 +1,595 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="pt-br">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>XLSX File Reader & Editor</title>
8
+ <!-- Bootstrap 5 CSS -->
9
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css" rel="stylesheet">
10
+ <!-- Bootstrap Icons -->
11
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1/font/bootstrap-icons.css">
12
+ <!-- XLSX Library -->
13
+ <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
14
+ <!-- FileSaver.js -->
15
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
16
+ <style>
17
+ .dropzone {
18
+ border: 2px dashed #0d6efd;
19
+ transition: all 0.3s ease;
20
+ cursor: pointer;
21
+ }
22
+
23
+ .dropzone.active {
24
+ border-color: #198754;
25
+ background-color: rgba(25, 135, 84, 0.05);
26
+ }
27
+
28
+ .table-container {
29
+ /*max-height: 70vh;*/
30
+ overflow-y: auto;
31
+ }
32
+
33
+ .editable-cell {
34
+ min-width: 120px;
35
+ border: 1px solid #dee2e6;
36
+ padding: 0.5rem;
37
+ }
38
+
39
+ .editable-cell:focus {
40
+ outline: 2px solid #0d6efd;
41
+ background-color: rgba(13, 110, 253, 0.1);
42
+ }
43
+
44
+ .sheet-tab {
45
+ cursor: pointer;
46
+ padding: 0.5rem 1rem;
47
+ border-radius: 0.375rem;
48
+ margin-right: 0.5rem;
49
+ background-color: #f8f9fa;
50
+ transition: all 0.2s ease;
51
+ }
52
+
53
+ .sheet-tab:hover {
54
+ background-color: #e9ecef;
55
+ }
56
+
57
+ .sheet-tab.active {
58
+ background-color: #0d6efd;
59
+ color: white;
60
+ }
61
+
62
+ .file-info {
63
+ background-color: #f8f9fa;
64
+ border-radius: 0.375rem;
65
+ padding: 1rem;
66
+ }
67
+
68
+ .how-to-use-card {
69
+ height: 100%;
70
+ }
71
+ </style>
72
+ </head>
73
+
74
+ <body class="bg-light">
75
+ <div class="container py-5">
76
+ <div class="text-center mb-5">
77
+ <h1 class="display-5 fw-bold text-dark mb-3">Excel File Reader & Editor</h1>
78
+ <p class="lead text-muted">Upload, view, edit and export Excel files directly in your browser</p>
79
+ </div>
80
+
81
+ <div class="shadow-sm mb-5">
82
+ <div class="">
83
+ <div class="row">
84
+
85
+ <!-- File Upload Section -->
86
+ <div class="col-md">
87
+ <div id="dropzone" class="dropzone rounded-0 p-3 text-center mb-4">
88
+ <div class="d-flex flex-column align-items-center justify-content-center">
89
+ <i class="bi bi-file-earmark-excel text-primary fs-1 mb-3"></i>
90
+ <p class="text-muted mb-2">Drag & drop your Excel file here</p>
91
+ <p class="text-muted small mb-3">or</p>
92
+ <!--<label for="fileInput" class="btn btn-primary">
93
+ <i class="bi bi-upload me-2"></i> Browse Files
94
+ </label>-->
95
+ <input type="file" id="fileInput" class="d-none" accept=".xlsx, .xls, .csv" />
96
+ </div>
97
+ </div>
98
+
99
+
100
+ </div>
101
+ <div class="col-md-3">
102
+ <h5 class="fw-bold mb-3">File Information</h5>
103
+ <div class="file-info">
104
+ <div class="d-flex align-items-center mb-2">
105
+ <i class="bi bi-file-earmark text-muted me-2"></i>
106
+ <span id="fileName" class="text-dark">No file selected</span>
107
+ </div>
108
+ <div class="d-flex align-items-center mb-2">
109
+ <i class="bi bi-collection text-muted me-2"></i>
110
+ <span id="sheetCount" class="text-dark">0 sheets</span>
111
+ </div>
112
+ <div class="d-flex align-items-center">
113
+ <i class="bi bi-table text-muted me-2"></i>
114
+ <span id="rowCount" class="text-dark">0 rows</span>
115
+ </div>
116
+ </div>
117
+ </div>
118
+
119
+
120
+ <!-- Sheet Navigation -->
121
+ <div class="col-ms">
122
+ <div id="sheetTabs" class="d-flex overflow-auto py-2 mb-3">
123
+ <!-- Sheets will be added here dynamically -->
124
+ </div>
125
+
126
+ <!-- Table Container -->
127
+ <div id="tableContainer" class="table-container border rounded-0 overflow-hidden">
128
+ <div class="text-center py-5 text-muted" id="emptyState">
129
+ <i class="bi bi-file-earmark-excel display-4 opacity-25 mb-3"></i>
130
+ <p class="h5">Upload an Excel file to get started</p>
131
+ </div>
132
+ <table id="dataTable" class="table table-bordered m-0 d-none overflow-auto">
133
+ <!-- Table content will be generated dynamically -->
134
+ </table>
135
+ </div>
136
+
137
+ <!-- Action Buttons -->
138
+ <div class="d-flex flex-wrap gap-2 mt-4">
139
+ <button id="exportBtn" class="btn btn-success" disabled>
140
+ <i class="bi bi-download me-2"></i> Export Excel
141
+ </button>
142
+ <button id="addRowBtn" class="btn btn-primary" disabled>
143
+ <i class="bi bi-plus me-2"></i> Add Row
144
+ </button>
145
+ <button id="addColBtn" class="btn btn-primary" disabled>
146
+ <i class="bi bi-plus me-2"></i> Add Column
147
+ </button>
148
+ <button id="resetBtn" class="btn btn-secondary">
149
+ <i class="bi bi-arrow-counterclockwise me-2"></i> Reset
150
+ </button>
151
+ </div>
152
+ </div>
153
+ </div>
154
+ </div>
155
+ </div>
156
+
157
+ <!-- Instructions -->
158
+ <div class="card shadow-sm">
159
+ <div class="card-body p-4">
160
+ <h2 class="h4 fw-bold mb-4">How to use</h2>
161
+ <div class="row g-4">
162
+ <div class="col-md-4">
163
+ <div class="card bg-primary bg-opacity-10 how-to-use-card">
164
+ <div class="card-body">
165
+ <div class="d-flex align-items-center mb-3">
166
+ <div class="bg-primary bg-opacity-25 p-2 rounded-circle me-3">
167
+ <i class="bi bi-upload text-primary"></i>
168
+ </div>
169
+ <h3 class="h6 fw-bold mb-0 text-primary">Upload</h3>
170
+ </div>
171
+ <p class="card-text text-muted">Drag & drop or click to upload your Excel file (.xlsx, .xls, .csv)</p>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ <div class="col-md-4">
176
+ <div class="card bg-success bg-opacity-10 how-to-use-card">
177
+ <div class="card-body">
178
+ <div class="d-flex align-items-center mb-3">
179
+ <div class="bg-success bg-opacity-25 p-2 rounded-circle me-3">
180
+ <i class="bi bi-pencil text-success"></i>
181
+ </div>
182
+ <h3 class="h6 fw-bold mb-0 text-success">Edit</h3>
183
+ </div>
184
+ <p class="card-text text-muted">Click on any cell to edit its content. Add/remove rows and columns as needed.</p>
185
+ </div>
186
+ </div>
187
+ </div>
188
+ <div class="col-md-4">
189
+ <div class="card bg-info bg-opacity-10 how-to-use-card">
190
+ <div class="card-body">
191
+ <div class="d-flex align-items-center mb-3">
192
+ <div class="bg-info bg-opacity-25 p-2 rounded-circle me-3">
193
+ <i class="bi bi-download text-info"></i>
194
+ </div>
195
+ <h3 class="h6 fw-bold mb-0 text-info">Export</h3>
196
+ </div>
197
+ <p class="card-text text-muted">Download your edited file back to Excel format when you're done.</p>
198
+ </div>
199
+ </div>
200
+ </div>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ </div>
205
+
206
+ <!-- Bootstrap 5 JS Bundle with Popper -->
207
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js"></script>
208
+
209
+ <script>
210
+ document.addEventListener("DOMContentLoaded", function () {
211
+ // DOM Elements
212
+ const fileInput = document.getElementById("fileInput");
213
+ const dropzone = document.getElementById("dropzone");
214
+ const fileName = document.getElementById("fileName");
215
+ const sheetCount = document.getElementById("sheetCount");
216
+ const rowCount = document.getElementById("rowCount");
217
+ const tableContainer = document.getElementById("tableContainer");
218
+ const dataTable = document.getElementById("dataTable");
219
+ const emptyState = document.getElementById("emptyState");
220
+ const sheetTabs = document.getElementById("sheetTabs");
221
+ const exportBtn = document.getElementById("exportBtn");
222
+ const addRowBtn = document.getElementById("addRowBtn");
223
+ const addColBtn = document.getElementById("addColBtn");
224
+ const resetBtn = document.getElementById("resetBtn");
225
+
226
+ // Global variables
227
+ let workbook = null;
228
+ let currentSheetName = "";
229
+ let sheetData = [];
230
+ let headers = [];
231
+
232
+ // Event Listeners
233
+ fileInput.addEventListener("change", handleFileSelect);
234
+ dropzone.addEventListener("click", () => fileInput.click());
235
+ dropzone.addEventListener("dragover", handleDragOver);
236
+ dropzone.addEventListener("dragleave", handleDragLeave);
237
+ dropzone.addEventListener("drop", handleDrop);
238
+ exportBtn.addEventListener("click", exportToExcel);
239
+ addRowBtn.addEventListener("click", addNewRow);
240
+ addColBtn.addEventListener("click", addNewColumn);
241
+ resetBtn.addEventListener("click", resetApp);
242
+
243
+ // Functions
244
+ function handleFileSelect(e) {
245
+ const file = e.target.files[0];
246
+ if (!file) return;
247
+
248
+ processExcelFile(file);
249
+ }
250
+
251
+ function handleDragOver(e) {
252
+ e.preventDefault();
253
+ e.stopPropagation();
254
+ dropzone.classList.add("active");
255
+ }
256
+
257
+ function handleDragLeave(e) {
258
+ e.preventDefault();
259
+ e.stopPropagation();
260
+ dropzone.classList.remove("active");
261
+ }
262
+
263
+ function handleDrop(e) {
264
+ e.preventDefault();
265
+ e.stopPropagation();
266
+ dropzone.classList.remove("active");
267
+
268
+ const file = e.dataTransfer.files[0];
269
+ if (!file) return;
270
+
271
+ processExcelFile(file);
272
+ }
273
+
274
+ function processExcelFile(file) {
275
+ fileName.textContent = file.name;
276
+
277
+ const reader = new FileReader();
278
+ reader.onload = function (e) {
279
+ const data = new Uint8Array(e.target.result);
280
+ workbook = XLSX.read(data, { type: "array" });
281
+
282
+ updateSheetInfo();
283
+ renderSheetTabs();
284
+ loadFirstSheet();
285
+ enableButtons();
286
+ };
287
+ reader.readAsArrayBuffer(file);
288
+ }
289
+
290
+ function updateSheetInfo() {
291
+ if (!workbook) return;
292
+
293
+ sheetCount.textContent = `${workbook.SheetNames.length} sheet${
294
+ workbook.SheetNames.length !== 1 ? "s" : ""
295
+ }`;
296
+ }
297
+
298
+ function renderSheetTabs() {
299
+ if (!workbook) return;
300
+
301
+ sheetTabs.innerHTML = "";
302
+ workbook.SheetNames.forEach((sheetName) => {
303
+ const tab = document.createElement("div");
304
+ tab.className = "sheet-tab";
305
+ tab.textContent = sheetName;
306
+ tab.addEventListener("click", () => switchSheet(sheetName));
307
+
308
+ if (sheetName === workbook.SheetNames[0]) {
309
+ tab.classList.add("active");
310
+ }
311
+
312
+ sheetTabs.appendChild(tab);
313
+ });
314
+ }
315
+
316
+ function switchSheet(sheetName) {
317
+ if (!workbook || currentSheetName === sheetName) return;
318
+
319
+ // Update active tab
320
+ document.querySelectorAll(".sheet-tab").forEach((tab) => {
321
+ tab.classList.remove("active");
322
+ if (tab.textContent === sheetName) {
323
+ tab.classList.add("active");
324
+ }
325
+ });
326
+
327
+ currentSheetName = sheetName;
328
+ loadSheetData();
329
+ renderTable();
330
+ }
331
+
332
+ function loadFirstSheet() {
333
+ if (!workbook || workbook.SheetNames.length === 0) return;
334
+
335
+ currentSheetName = workbook.SheetNames[0];
336
+ loadSheetData();
337
+ renderTable();
338
+ }
339
+
340
+ function loadSheetData() {
341
+ if (!workbook || !currentSheetName) return;
342
+
343
+ const worksheet = workbook.Sheets[currentSheetName];
344
+ sheetData = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: "" });
345
+
346
+ if (sheetData.length > 0) {
347
+ headers = sheetData[0];
348
+ rowCount.textContent = `${sheetData.length - 1} row${
349
+ sheetData.length - 1 !== 1 ? "s" : ""
350
+ }`;
351
+ } else {
352
+ headers = [];
353
+ rowCount.textContent = "0 rows";
354
+ }
355
+ }
356
+
357
+ function renderTable() {
358
+ if (!sheetData || sheetData.length === 0) {
359
+ emptyState.classList.remove("d-none");
360
+ dataTable.classList.add("d-none");
361
+ return;
362
+ }
363
+
364
+ emptyState.classList.add("d-none");
365
+ dataTable.classList.remove("d-none");
366
+
367
+ // Clear existing table
368
+ dataTable.innerHTML = "";
369
+
370
+ // Create header row
371
+ const thead = document.createElement("thead");
372
+ const headerRow = document.createElement("tr");
373
+ headerRow.className = "table-light";
374
+
375
+ // Add empty cell for corner
376
+ const cornerCell = document.createElement("th");
377
+ cornerCell.className = "text-center";
378
+ cornerCell.style.padding = "0px 45px";
379
+ headerRow.appendChild(cornerCell);
380
+
381
+ // Add column headers
382
+ headers.forEach((header, colIndex) => {
383
+ const th = document.createElement("th");
384
+ th.className = "text-nowrap";
385
+ th.textContent = header || `Column ${colIndex + 1}`;
386
+
387
+ // Add delete column button
388
+ const deleteBtn = document.createElement("button");
389
+ deleteBtn.className = "btn btn-sm btn-link text-danger ms-2 p-0";
390
+ deleteBtn.innerHTML = '<i class="bi bi-x"></i>';
391
+ deleteBtn.addEventListener("click", () => deleteColumn(colIndex));
392
+
393
+ th.appendChild(deleteBtn);
394
+ headerRow.appendChild(th);
395
+ });
396
+
397
+ // Add new column header
398
+ const newColHeader = document.createElement("th");
399
+ newColHeader.className = "text-center";
400
+ newColHeader.style.width = "50px";
401
+
402
+ const addColBtn = document.createElement("button");
403
+ addColBtn.className = "btn btn-sm btn-link text-primary p-0";
404
+ addColBtn.innerHTML = '<i class="bi bi-plus"></i>';
405
+ addColBtn.addEventListener("click", addNewColumn);
406
+
407
+ newColHeader.appendChild(addColBtn);
408
+ headerRow.appendChild(newColHeader);
409
+
410
+ thead.appendChild(headerRow);
411
+ dataTable.appendChild(thead);
412
+
413
+ // Create table body
414
+ const tbody = document.createElement("tbody");
415
+
416
+ // Add data rows
417
+ for (let rowIndex = 1; rowIndex < sheetData.length; rowIndex++) {
418
+ const row = document.createElement("tr");
419
+
420
+ // Add row number cell
421
+ const rowNumCell = document.createElement("td");
422
+ rowNumCell.className = "text-center text-muted";
423
+ rowNumCell.textContent = rowIndex;
424
+
425
+ // Add delete row button
426
+ const deleteRowBtn = document.createElement("button");
427
+ deleteRowBtn.className = "btn btn-sm btn-link text-danger ms-2 p-0";
428
+ deleteRowBtn.innerHTML = '<i class="bi bi-x"></i>';
429
+ deleteRowBtn.addEventListener("click", () => deleteRow(rowIndex));
430
+
431
+ rowNumCell.appendChild(deleteRowBtn);
432
+ row.appendChild(rowNumCell);
433
+
434
+ // Add data cells
435
+ for (let colIndex = 0; colIndex < headers.length; colIndex++) {
436
+ const cell = document.createElement("td");
437
+ cell.className = "editable-cell";
438
+ cell.contentEditable = true;
439
+
440
+ // Set cell value (handle cases where row might be shorter than headers)
441
+ const cellValue =
442
+ sheetData[rowIndex][colIndex] !== undefined
443
+ ? sheetData[rowIndex][colIndex]
444
+ : "";
445
+ cell.textContent = cellValue;
446
+
447
+ // Add event listener to save changes
448
+ cell.addEventListener("blur", () => {
449
+ if (!sheetData[rowIndex]) {
450
+ sheetData[rowIndex] = new Array(headers.length).fill("");
451
+ }
452
+ sheetData[rowIndex][colIndex] = cell.textContent;
453
+ });
454
+
455
+ row.appendChild(cell);
456
+ }
457
+
458
+ // Add empty cell for new column
459
+ const newColCell = document.createElement("td");
460
+ newColCell.className = "text-center";
461
+ row.appendChild(newColCell);
462
+
463
+ tbody.appendChild(row);
464
+ }
465
+
466
+ // Add new row button
467
+ const newRow = document.createElement("tr");
468
+ const newRowCell = document.createElement("td");
469
+ newRowCell.colSpan = headers.length + 2;
470
+ newRowCell.className = "text-center py-3";
471
+
472
+ const addRowBtn = document.createElement("button");
473
+ addRowBtn.className = "btn btn-sm btn-primary";
474
+ addRowBtn.innerHTML = '<i class="bi bi-plus me-2"></i>Add Row';
475
+ addRowBtn.addEventListener("click", addNewRow);
476
+
477
+ newRowCell.appendChild(addRowBtn);
478
+ newRow.appendChild(newRowCell);
479
+ tbody.appendChild(newRow);
480
+
481
+ dataTable.appendChild(tbody);
482
+ }
483
+
484
+ function addNewRow() {
485
+ if (!sheetData) return;
486
+
487
+ const newRow = new Array(headers.length).fill("");
488
+ sheetData.push(newRow);
489
+ rowCount.textContent = `${sheetData.length - 1} row${
490
+ sheetData.length - 1 !== 1 ? "s" : ""
491
+ }`;
492
+ renderTable();
493
+ }
494
+
495
+ function addNewColumn() {
496
+ if (!headers) return;
497
+
498
+ const newHeader = `Column ${headers.length + 1}`;
499
+ headers.push(newHeader);
500
+
501
+ // Add empty value for the new column in each row
502
+ for (let i = 0; i < sheetData.length; i++) {
503
+ sheetData[i].push("");
504
+ }
505
+
506
+ renderTable();
507
+ }
508
+
509
+ function deleteRow(rowIndex) {
510
+ if (!sheetData || rowIndex < 1 || rowIndex >= sheetData.length) return;
511
+
512
+ sheetData.splice(rowIndex, 1);
513
+ rowCount.textContent = `${sheetData.length - 1} row${
514
+ sheetData.length - 1 !== 1 ? "s" : ""
515
+ }`;
516
+ renderTable();
517
+ }
518
+
519
+ function deleteColumn(colIndex) {
520
+ if (!headers || colIndex < 0 || colIndex >= headers.length) return;
521
+
522
+ headers.splice(colIndex, 1);
523
+
524
+ // Remove the column from each row
525
+ for (let i = 0; i < sheetData.length; i++) {
526
+ if (sheetData[i].length > colIndex) {
527
+ sheetData[i].splice(colIndex, 1);
528
+ }
529
+ }
530
+
531
+ renderTable();
532
+ }
533
+
534
+ function exportToExcel() {
535
+ if (!workbook || !currentSheetName) return;
536
+
537
+ // Update the current sheet with edited data
538
+ const worksheet = XLSX.utils.aoa_to_sheet(sheetData);
539
+ workbook.Sheets[currentSheetName] = worksheet;
540
+
541
+ // Generate Excel file
542
+ const excelBuffer = XLSX.write(workbook, {
543
+ bookType: "xlsx",
544
+ type: "array"
545
+ });
546
+ const blob = new Blob([excelBuffer], {
547
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
548
+ });
549
+
550
+ // Suggest a filename
551
+ let exportName = "edited_" + (fileName.textContent || "spreadsheet");
552
+ if (!exportName.endsWith(".xlsx")) {
553
+ exportName += ".xlsx";
554
+ }
555
+
556
+ // Save the file
557
+ saveAs(blob, exportName);
558
+ }
559
+
560
+ function resetApp() {
561
+ // Reset UI
562
+ fileName.textContent = "No file selected";
563
+ sheetCount.textContent = "0 sheets";
564
+ rowCount.textContent = "0 rows";
565
+
566
+ emptyState.classList.remove("d-none");
567
+ dataTable.classList.add("d-none");
568
+ sheetTabs.innerHTML = "";
569
+
570
+ // Reset data
571
+ workbook = null;
572
+ currentSheetName = "";
573
+ sheetData = [];
574
+ headers = [];
575
+
576
+ // Disable buttons
577
+ exportBtn.disabled = true;
578
+ addRowBtn.disabled = true;
579
+ addColBtn.disabled = true;
580
+
581
+ // Reset file input
582
+ fileInput.value = "";
583
+ }
584
+
585
+ function enableButtons() {
586
+ exportBtn.disabled = false;
587
+ addRowBtn.disabled = false;
588
+ addColBtn.disabled = false;
589
+ }
590
+ });
591
+
592
+ </script>
593
+ </body>
594
+
595
+ </html>